replace rsc.io/letsencrypt in favour of golang.org/x/crypto
Signed-off-by: Tariq Ibrahim <tariq181290@gmail.com>
This commit is contained in:
parent
3226863cbc
commit
8f9c8094fb
129 changed files with 6555 additions and 37728 deletions
|
@ -848,7 +848,9 @@ TLS certificates provided by
|
||||||
> to the `docker run` command or using a similar setting in a cloud
|
> to the `docker run` command or using a similar setting in a cloud
|
||||||
> configuration. You should also set the `hosts` option to the list of hostnames
|
> configuration. You should also set the `hosts` option to the list of hostnames
|
||||||
> that are valid for this registry to avoid trying to get certificates for random
|
> that are valid for this registry to avoid trying to get certificates for random
|
||||||
> hostnames due to malicious clients connecting with bogus SNI hostnames.
|
> hostnames due to malicious clients connecting with bogus SNI hostnames. Please
|
||||||
|
> ensure that you have the `ca-certificates` package installed in order to verify
|
||||||
|
> letsencrypt certificates.
|
||||||
|
|
||||||
| Parameter | Required | Description |
|
| Parameter | Required | Description |
|
||||||
|-----------|----------|-------------------------------------------------------|
|
|-----------|----------|-------------------------------------------------------|
|
||||||
|
|
|
@ -12,11 +12,17 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"rsc.io/letsencrypt"
|
|
||||||
|
|
||||||
"github.com/Shopify/logrus-bugsnag"
|
"github.com/Shopify/logrus-bugsnag"
|
||||||
logstash "github.com/bshuster-repo/logrus-logstash-hook"
|
logstash "github.com/bshuster-repo/logrus-logstash-hook"
|
||||||
"github.com/bugsnag/bugsnag-go"
|
"github.com/bugsnag/bugsnag-go"
|
||||||
|
"github.com/docker/go-metrics"
|
||||||
|
gorhandlers "github.com/gorilla/handlers"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/yvasiyarov/gorelic"
|
||||||
|
"golang.org/x/crypto/acme"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
|
||||||
"github.com/docker/distribution/configuration"
|
"github.com/docker/distribution/configuration"
|
||||||
dcontext "github.com/docker/distribution/context"
|
dcontext "github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/health"
|
"github.com/docker/distribution/health"
|
||||||
|
@ -24,11 +30,6 @@ import (
|
||||||
"github.com/docker/distribution/registry/listener"
|
"github.com/docker/distribution/registry/listener"
|
||||||
"github.com/docker/distribution/uuid"
|
"github.com/docker/distribution/uuid"
|
||||||
"github.com/docker/distribution/version"
|
"github.com/docker/distribution/version"
|
||||||
"github.com/docker/go-metrics"
|
|
||||||
gorhandlers "github.com/gorilla/handlers"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/yvasiyarov/gorelic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// this channel gets notified when process receives signal. It is global to ease unit testing
|
// this channel gets notified when process receives signal. It is global to ease unit testing
|
||||||
|
@ -170,19 +171,14 @@ func (registry *Registry) ListenAndServe() error {
|
||||||
if config.HTTP.TLS.Certificate != "" {
|
if config.HTTP.TLS.Certificate != "" {
|
||||||
return fmt.Errorf("cannot specify both certificate and Let's Encrypt")
|
return fmt.Errorf("cannot specify both certificate and Let's Encrypt")
|
||||||
}
|
}
|
||||||
var m letsencrypt.Manager
|
m := &autocert.Manager{
|
||||||
if err := m.CacheFile(config.HTTP.TLS.LetsEncrypt.CacheFile); err != nil {
|
HostPolicy: autocert.HostWhitelist(config.HTTP.TLS.LetsEncrypt.Hosts...),
|
||||||
return err
|
Cache: autocert.DirCache(config.HTTP.TLS.LetsEncrypt.CacheFile),
|
||||||
}
|
Email: config.HTTP.TLS.LetsEncrypt.Email,
|
||||||
if !m.Registered() {
|
Prompt: autocert.AcceptTOS,
|
||||||
if err := m.Register(config.HTTP.TLS.LetsEncrypt.Email, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(config.HTTP.TLS.LetsEncrypt.Hosts) > 0 {
|
|
||||||
m.SetHosts(config.HTTP.TLS.LetsEncrypt.Hosts)
|
|
||||||
}
|
}
|
||||||
tlsConf.GetCertificate = m.GetCertificate
|
tlsConf.GetCertificate = m.GetCertificate
|
||||||
|
tlsConf.NextProtos = append(tlsConf.NextProtos, acme.ALPNProto)
|
||||||
} else {
|
} else {
|
||||||
tlsConf.Certificates = make([]tls.Certificate, 1)
|
tlsConf.Certificates = make([]tls.Certificate, 1)
|
||||||
tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
|
tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
|
||||||
|
|
|
@ -21,7 +21,6 @@ github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||||
github.com/marstr/guid 8bd9a64bf37eb297b492a4101fb28e80ac0b290f
|
github.com/marstr/guid 8bd9a64bf37eb297b492a4101fb28e80ac0b290f
|
||||||
github.com/satori/go.uuid f58768cc1a7a7e77a3bd49e98cdd21419399b6a3
|
github.com/satori/go.uuid f58768cc1a7a7e77a3bd49e98cdd21419399b6a3
|
||||||
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c
|
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c
|
||||||
github.com/miekg/dns 271c58e0c14f552178ea321a545ff9af38930f39
|
|
||||||
github.com/mitchellh/mapstructure 482a9fd5fa83e8c4e7817413b80f3eb8feec03ef
|
github.com/mitchellh/mapstructure 482a9fd5fa83e8c4e7817413b80f3eb8feec03ef
|
||||||
github.com/ncw/swift a0320860b16212c2b59b4912bb6508cda1d7cee6
|
github.com/ncw/swift a0320860b16212c2b59b4912bb6508cda1d7cee6
|
||||||
github.com/prometheus/client_golang c332b6f63c0658a65eca15c0e5247ded801cf564
|
github.com/prometheus/client_golang c332b6f63c0658a65eca15c0e5247ded801cf564
|
||||||
|
@ -31,21 +30,17 @@ github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
|
||||||
github.com/Shopify/logrus-bugsnag 577dee27f20dd8f1a529f82210094af593be12bd
|
github.com/Shopify/logrus-bugsnag 577dee27f20dd8f1a529f82210094af593be12bd
|
||||||
github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064
|
github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064
|
||||||
github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842
|
github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842
|
||||||
github.com/xenolf/lego a9d8cec0e6563575e5868a005359ac97911b5985
|
|
||||||
github.com/yvasiyarov/go-metrics 57bccd1ccd43f94bb17fdd8bf3007059b802f85e
|
github.com/yvasiyarov/go-metrics 57bccd1ccd43f94bb17fdd8bf3007059b802f85e
|
||||||
github.com/yvasiyarov/gorelic a9bba5b9ab508a086f9a12b8c51fab68478e2128
|
github.com/yvasiyarov/gorelic a9bba5b9ab508a086f9a12b8c51fab68478e2128
|
||||||
github.com/yvasiyarov/newrelic_platform_go b21fdbd4370f3717f3bbd2bf41c223bc273068e6
|
github.com/yvasiyarov/newrelic_platform_go b21fdbd4370f3717f3bbd2bf41c223bc273068e6
|
||||||
golang.org/x/crypto c10c31b5e94b6f7a0283272dc2bb27163dcea24b
|
golang.org/x/crypto e84da0312774c21d64ee2317962ef669b27ffb41
|
||||||
golang.org/x/net 4876518f9e71663000c348837735820161a42df7
|
golang.org/x/net 4876518f9e71663000c348837735820161a42df7
|
||||||
golang.org/x/oauth2 045497edb6234273d67dbc25da3f2ddbc4c4cacf
|
golang.org/x/oauth2 045497edb6234273d67dbc25da3f2ddbc4c4cacf
|
||||||
golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
|
|
||||||
google.golang.org/api 9bf6e6e569ff057f75d9604a46c52928f17d2b54
|
google.golang.org/api 9bf6e6e569ff057f75d9604a46c52928f17d2b54
|
||||||
google.golang.org/appengine 12d5545dc1cfa6047a286d5e853841b6471f4c19
|
google.golang.org/appengine 12d5545dc1cfa6047a286d5e853841b6471f4c19
|
||||||
google.golang.org/cloud 975617b05ea8a58727e6c1a06b6161ff4185a9f2
|
google.golang.org/cloud 975617b05ea8a58727e6c1a06b6161ff4185a9f2
|
||||||
google.golang.org/grpc d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994
|
google.golang.org/grpc d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994
|
||||||
gopkg.in/check.v1 64131543e7896d5bcc6bd5a76287eb75ea96c673
|
gopkg.in/check.v1 64131543e7896d5bcc6bd5a76287eb75ea96c673
|
||||||
gopkg.in/square/go-jose.v1 40d457b439244b546f023d056628e5184136899b
|
|
||||||
gopkg.in/yaml.v2 v2.2.1
|
gopkg.in/yaml.v2 v2.2.1
|
||||||
rsc.io/letsencrypt e770c10b0f1a64775ae91d240407ce00d1a5bdeb https://github.com/dmcgowan/letsencrypt.git
|
|
||||||
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
||||||
github.com/opencontainers/image-spec ab7389ef9f50030c9b245bc16b981c7ddf192882
|
github.com/opencontainers/image-spec ab7389ef9f50030c9b245bc16b981c7ddf192882
|
||||||
|
|
6
vendor/github.com/aws/aws-sdk-go/go.mod
generated
vendored
Normal file
6
vendor/github.com/aws/aws-sdk-go/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module github.com/aws/aws-sdk-go
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-ini/ini v1.25.4
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8
|
||||||
|
)
|
32
vendor/github.com/miekg/dns/LICENSE
generated
vendored
32
vendor/github.com/miekg/dns/LICENSE
generated
vendored
|
@ -1,32 +0,0 @@
|
||||||
Extensions of the original work are copyright (c) 2011 Miek Gieben
|
|
||||||
|
|
||||||
As this is fork of the official Go code the same license applies:
|
|
||||||
|
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
154
vendor/github.com/miekg/dns/README.md
generated
vendored
154
vendor/github.com/miekg/dns/README.md
generated
vendored
|
@ -1,154 +0,0 @@
|
||||||
[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns) [![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns)
|
|
||||||
|
|
||||||
# Alternative (more granular) approach to a DNS library
|
|
||||||
|
|
||||||
> Less is more.
|
|
||||||
|
|
||||||
Complete and usable DNS library. All widely used Resource Records are
|
|
||||||
supported, including the DNSSEC types. It follows a lean and mean philosophy.
|
|
||||||
If there is stuff you should know as a DNS programmer there isn't a convenience
|
|
||||||
function for it. Server side and client side programming is supported, i.e. you
|
|
||||||
can build servers and resolvers with it.
|
|
||||||
|
|
||||||
We try to keep the "master" branch as sane as possible and at the bleeding edge
|
|
||||||
of standards, avoiding breaking changes wherever reasonable. We support the last
|
|
||||||
two versions of Go, currently: 1.5 and 1.6.
|
|
||||||
|
|
||||||
# Goals
|
|
||||||
|
|
||||||
* KISS;
|
|
||||||
* Fast;
|
|
||||||
* Small API, if its easy to code in Go, don't make a function for it.
|
|
||||||
|
|
||||||
# Users
|
|
||||||
|
|
||||||
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/
|
|
||||||
* https://github.com/chuangbo/jianbing-dictionary-dns
|
|
||||||
* http://www.dns-lg.com/
|
|
||||||
* https://github.com/fcambus/rrda
|
|
||||||
* https://github.com/kenshinx/godns
|
|
||||||
* https://github.com/skynetservices/skydns
|
|
||||||
* https://github.com/hashicorp/consul
|
|
||||||
* https://github.com/DevelopersPL/godnsagent
|
|
||||||
* https://github.com/duedil-ltd/discodns
|
|
||||||
* https://github.com/StalkR/dns-reverse-proxy
|
|
||||||
* 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
|
|
||||||
* https://github.com/fcambus/statzone
|
|
||||||
* https://github.com/benschw/dns-clb-go
|
|
||||||
* https://github.com/corny/dnscheck for http://public-dns.info/
|
|
||||||
* https://namesmith.io
|
|
||||||
* https://github.com/miekg/unbound
|
|
||||||
* https://github.com/miekg/exdns
|
|
||||||
* https://dnslookup.org
|
|
||||||
* https://github.com/looterz/grimd
|
|
||||||
* https://github.com/phamhongviet/serf-dns
|
|
||||||
* https://github.com/mehrdadrad/mylg
|
|
||||||
* https://github.com/bamarni/dockness
|
|
||||||
* https://github.com/fffaraz/microdns
|
|
||||||
|
|
||||||
Send pull request if you want to be listed here.
|
|
||||||
|
|
||||||
# Features
|
|
||||||
|
|
||||||
* UDP/TCP queries, IPv4 and IPv6;
|
|
||||||
* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported;
|
|
||||||
* Fast:
|
|
||||||
* Reply speed around ~ 80K qps (faster hardware results in more qps);
|
|
||||||
* Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds;
|
|
||||||
* Server side programming (mimicking the net/http package);
|
|
||||||
* Client side programming;
|
|
||||||
* DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA;
|
|
||||||
* EDNS0, NSID, Cookies;
|
|
||||||
* AXFR/IXFR;
|
|
||||||
* TSIG, SIG(0);
|
|
||||||
* DNS over TLS: optional encrypted connection between client and server;
|
|
||||||
* DNS name compression;
|
|
||||||
* Depends only on the standard library.
|
|
||||||
|
|
||||||
Have fun!
|
|
||||||
|
|
||||||
Miek Gieben - 2010-2012 - <miek@miek.nl>
|
|
||||||
|
|
||||||
# Building
|
|
||||||
|
|
||||||
Building is done with the `go` tool. If you have setup your GOPATH
|
|
||||||
correctly, the following should work:
|
|
||||||
|
|
||||||
go get github.com/miekg/dns
|
|
||||||
go build github.com/miekg/dns
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
A short "how to use the API" is at the beginning of doc.go (this also will show
|
|
||||||
when you call `godoc github.com/miekg/dns`).
|
|
||||||
|
|
||||||
Example programs can be found in the `github.com/miekg/exdns` repository.
|
|
||||||
|
|
||||||
## Supported RFCs
|
|
||||||
|
|
||||||
*all of them*
|
|
||||||
|
|
||||||
* 103{4,5} - DNS standard
|
|
||||||
* 1348 - NSAP record (removed the record)
|
|
||||||
* 1982 - Serial Arithmetic
|
|
||||||
* 1876 - LOC record
|
|
||||||
* 1995 - IXFR
|
|
||||||
* 1996 - DNS notify
|
|
||||||
* 2136 - DNS Update (dynamic updates)
|
|
||||||
* 2181 - RRset definition - there is no RRset type though, just []RR
|
|
||||||
* 2537 - RSAMD5 DNS keys
|
|
||||||
* 2065 - DNSSEC (updated in later RFCs)
|
|
||||||
* 2671 - EDNS record
|
|
||||||
* 2782 - SRV record
|
|
||||||
* 2845 - TSIG record
|
|
||||||
* 2915 - NAPTR record
|
|
||||||
* 2929 - DNS IANA Considerations
|
|
||||||
* 3110 - RSASHA1 DNS keys
|
|
||||||
* 3225 - DO bit (DNSSEC OK)
|
|
||||||
* 340{1,2,3} - NAPTR record
|
|
||||||
* 3445 - Limiting the scope of (DNS)KEY
|
|
||||||
* 3597 - Unknown RRs
|
|
||||||
* 403{3,4,5} - DNSSEC + validation functions
|
|
||||||
* 4255 - SSHFP record
|
|
||||||
* 4343 - Case insensitivity
|
|
||||||
* 4408 - SPF record
|
|
||||||
* 4509 - SHA256 Hash in DS
|
|
||||||
* 4592 - Wildcards in the DNS
|
|
||||||
* 4635 - HMAC SHA TSIG
|
|
||||||
* 4701 - DHCID
|
|
||||||
* 4892 - id.server
|
|
||||||
* 5001 - NSID
|
|
||||||
* 5155 - NSEC3 record
|
|
||||||
* 5205 - HIP record
|
|
||||||
* 5702 - SHA2 in the DNS
|
|
||||||
* 5936 - AXFR
|
|
||||||
* 5966 - TCP implementation recommendations
|
|
||||||
* 6605 - ECDSA
|
|
||||||
* 6725 - IANA Registry Update
|
|
||||||
* 6742 - ILNP DNS
|
|
||||||
* 6840 - Clarifications and Implementation Notes for DNS Security
|
|
||||||
* 6844 - CAA record
|
|
||||||
* 6891 - EDNS0 update
|
|
||||||
* 6895 - DNS IANA considerations
|
|
||||||
* 6975 - Algorithm Understanding in DNSSEC
|
|
||||||
* 7043 - EUI48/EUI64 records
|
|
||||||
* 7314 - DNS (EDNS) EXPIRE Option
|
|
||||||
* 7553 - URI record
|
|
||||||
* 7858 - DNS over TLS: Initiation and Performance Considerations (draft)
|
|
||||||
* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
|
|
||||||
* xxxx - EDNS0 DNS Update Lease (draft)
|
|
||||||
|
|
||||||
## Loosely based upon
|
|
||||||
|
|
||||||
* `ldns`
|
|
||||||
* `NSD`
|
|
||||||
* `Net::DNS`
|
|
||||||
* `GRONG`
|
|
455
vendor/github.com/miekg/dns/client.go
generated
vendored
455
vendor/github.com/miekg/dns/client.go
generated
vendored
|
@ -1,455 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
// A client implementation.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const dnsTimeout time.Duration = 2 * time.Second
|
|
||||||
const tcpIdleTimeout time.Duration = 8 * time.Second
|
|
||||||
|
|
||||||
// A Conn represents a connection to a DNS server.
|
|
||||||
type Conn struct {
|
|
||||||
net.Conn // a net.Conn holding the connection
|
|
||||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
|
||||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
|
|
||||||
rtt time.Duration
|
|
||||||
t time.Time
|
|
||||||
tsigRequestMAC string
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Client defines parameters for a DNS client.
|
|
||||||
type Client struct {
|
|
||||||
Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
|
|
||||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
|
||||||
TLSConfig *tls.Config // TLS connection configuration
|
|
||||||
Timeout time.Duration // a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout and WriteTimeout when non-zero
|
|
||||||
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
|
||||||
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
|
||||||
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
|
||||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
|
|
||||||
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
|
|
||||||
group singleflight
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exchange performs a synchronous UDP query. It sends the message m to the address
|
|
||||||
// contained in a and waits for a reply. Exchange does not retry a failed query, nor
|
|
||||||
// will it fall back to TCP in case of truncation.
|
|
||||||
// See client.Exchange for more information on setting larger buffer sizes.
|
|
||||||
func Exchange(m *Msg, a string) (r *Msg, err error) {
|
|
||||||
var co *Conn
|
|
||||||
co, err = DialTimeout("udp", a, dnsTimeout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer co.Close()
|
|
||||||
|
|
||||||
opt := m.IsEdns0()
|
|
||||||
// If EDNS0 is used use that for size.
|
|
||||||
if opt != nil && opt.UDPSize() >= MinMsgSize {
|
|
||||||
co.UDPSize = opt.UDPSize()
|
|
||||||
}
|
|
||||||
|
|
||||||
co.SetWriteDeadline(time.Now().Add(dnsTimeout))
|
|
||||||
if err = co.WriteMsg(m); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
co.SetReadDeadline(time.Now().Add(dnsTimeout))
|
|
||||||
r, err = co.ReadMsg()
|
|
||||||
if err == nil && r.Id != m.Id {
|
|
||||||
err = ErrId
|
|
||||||
}
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExchangeConn performs a synchronous query. It sends the message m via the connection
|
|
||||||
// c and waits for a reply. The connection c is not closed by ExchangeConn.
|
|
||||||
// This function is going away, but can easily be mimicked:
|
|
||||||
//
|
|
||||||
// co := &dns.Conn{Conn: c} // c is your net.Conn
|
|
||||||
// co.WriteMsg(m)
|
|
||||||
// in, _ := co.ReadMsg()
|
|
||||||
// co.Close()
|
|
||||||
//
|
|
||||||
func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
|
|
||||||
println("dns: this function is deprecated")
|
|
||||||
co := new(Conn)
|
|
||||||
co.Conn = c
|
|
||||||
if err = co.WriteMsg(m); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r, err = co.ReadMsg()
|
|
||||||
if err == nil && r.Id != m.Id {
|
|
||||||
err = ErrId
|
|
||||||
}
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exchange performs a synchronous query. It sends the message m to the address
|
|
||||||
// contained in a and waits for a reply. Basic use pattern with a *dns.Client:
|
|
||||||
//
|
|
||||||
// c := new(dns.Client)
|
|
||||||
// in, rtt, err := c.Exchange(message, "127.0.0.1:53")
|
|
||||||
//
|
|
||||||
// Exchange does not retry a failed query, nor will it fall back to TCP in
|
|
||||||
// case of truncation.
|
|
||||||
// It is up to the caller to create a message that allows for larger responses to be
|
|
||||||
// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
|
|
||||||
// buffer, see SetEdns0. Messsages without an OPT RR will fallback to the historic limit
|
|
||||||
// of 512 bytes.
|
|
||||||
func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
|
|
||||||
if !c.SingleInflight {
|
|
||||||
return c.exchange(m, a)
|
|
||||||
}
|
|
||||||
// This adds a bunch of garbage, TODO(miek).
|
|
||||||
t := "nop"
|
|
||||||
if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
|
|
||||||
t = t1
|
|
||||||
}
|
|
||||||
cl := "nop"
|
|
||||||
if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
|
|
||||||
cl = cl1
|
|
||||||
}
|
|
||||||
r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
|
|
||||||
return c.exchange(m, a)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return r, rtt, err
|
|
||||||
}
|
|
||||||
if shared {
|
|
||||||
return r.Copy(), rtt, nil
|
|
||||||
}
|
|
||||||
return r, rtt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) dialTimeout() time.Duration {
|
|
||||||
if c.Timeout != 0 {
|
|
||||||
return c.Timeout
|
|
||||||
}
|
|
||||||
if c.DialTimeout != 0 {
|
|
||||||
return c.DialTimeout
|
|
||||||
}
|
|
||||||
return dnsTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) readTimeout() time.Duration {
|
|
||||||
if c.ReadTimeout != 0 {
|
|
||||||
return c.ReadTimeout
|
|
||||||
}
|
|
||||||
return dnsTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) writeTimeout() time.Duration {
|
|
||||||
if c.WriteTimeout != 0 {
|
|
||||||
return c.WriteTimeout
|
|
||||||
}
|
|
||||||
return dnsTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
|
|
||||||
var co *Conn
|
|
||||||
network := "udp"
|
|
||||||
tls := false
|
|
||||||
|
|
||||||
switch c.Net {
|
|
||||||
case "tcp-tls":
|
|
||||||
network = "tcp"
|
|
||||||
tls = true
|
|
||||||
case "tcp4-tls":
|
|
||||||
network = "tcp4"
|
|
||||||
tls = true
|
|
||||||
case "tcp6-tls":
|
|
||||||
network = "tcp6"
|
|
||||||
tls = true
|
|
||||||
default:
|
|
||||||
if c.Net != "" {
|
|
||||||
network = c.Net
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var deadline time.Time
|
|
||||||
if c.Timeout != 0 {
|
|
||||||
deadline = time.Now().Add(c.Timeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tls {
|
|
||||||
co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout())
|
|
||||||
} else {
|
|
||||||
co, err = DialTimeout(network, a, c.dialTimeout())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
defer co.Close()
|
|
||||||
|
|
||||||
opt := m.IsEdns0()
|
|
||||||
// If EDNS0 is used use that for size.
|
|
||||||
if opt != nil && opt.UDPSize() >= MinMsgSize {
|
|
||||||
co.UDPSize = opt.UDPSize()
|
|
||||||
}
|
|
||||||
// Otherwise use the client's configured UDP size.
|
|
||||||
if opt == nil && c.UDPSize >= MinMsgSize {
|
|
||||||
co.UDPSize = c.UDPSize
|
|
||||||
}
|
|
||||||
|
|
||||||
co.TsigSecret = c.TsigSecret
|
|
||||||
co.SetWriteDeadline(deadlineOrTimeout(deadline, c.writeTimeout()))
|
|
||||||
if err = co.WriteMsg(m); err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
co.SetReadDeadline(deadlineOrTimeout(deadline, c.readTimeout()))
|
|
||||||
r, err = co.ReadMsg()
|
|
||||||
if err == nil && r.Id != m.Id {
|
|
||||||
err = ErrId
|
|
||||||
}
|
|
||||||
return r, co.rtt, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMsg reads a message from the connection co.
|
|
||||||
// If the received message contains a TSIG record the transaction
|
|
||||||
// signature is verified.
|
|
||||||
func (co *Conn) ReadMsg() (*Msg, error) {
|
|
||||||
p, err := co.ReadMsgHeader(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := new(Msg)
|
|
||||||
if err := m.Unpack(p); err != nil {
|
|
||||||
// If ErrTruncated was returned, we still want to allow the user to use
|
|
||||||
// the message, but naively they can just check err if they don't want
|
|
||||||
// to use a truncated message
|
|
||||||
if err == ErrTruncated {
|
|
||||||
return m, err
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if t := m.IsTsig(); t != nil {
|
|
||||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
|
||||||
return m, ErrSecret
|
|
||||||
}
|
|
||||||
// Need to work on the original message p, as that was used to calculate the tsig.
|
|
||||||
err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
|
|
||||||
}
|
|
||||||
return m, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil).
|
|
||||||
// Returns message as a byte slice to be parsed with Msg.Unpack later on.
|
|
||||||
// Note that error handling on the message body is not possible as only the header is parsed.
|
|
||||||
func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
|
||||||
var (
|
|
||||||
p []byte
|
|
||||||
n int
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
switch t := co.Conn.(type) {
|
|
||||||
case *net.TCPConn, *tls.Conn:
|
|
||||||
r := t.(io.Reader)
|
|
||||||
|
|
||||||
// First two bytes specify the length of the entire message.
|
|
||||||
l, err := tcpMsgLen(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p = make([]byte, l)
|
|
||||||
n, err = tcpRead(r, p)
|
|
||||||
co.rtt = time.Since(co.t)
|
|
||||||
default:
|
|
||||||
if co.UDPSize > MinMsgSize {
|
|
||||||
p = make([]byte, co.UDPSize)
|
|
||||||
} else {
|
|
||||||
p = make([]byte, MinMsgSize)
|
|
||||||
}
|
|
||||||
n, err = co.Read(p)
|
|
||||||
co.rtt = time.Since(co.t)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if n < headerSize {
|
|
||||||
return nil, ErrShortRead
|
|
||||||
}
|
|
||||||
|
|
||||||
p = p[:n]
|
|
||||||
if hdr != nil {
|
|
||||||
dh, _, err := unpackMsgHdr(p, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
*hdr = dh
|
|
||||||
}
|
|
||||||
return p, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
|
|
||||||
func tcpMsgLen(t io.Reader) (int, error) {
|
|
||||||
p := []byte{0, 0}
|
|
||||||
n, err := t.Read(p)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if n != 2 {
|
|
||||||
return 0, ErrShortRead
|
|
||||||
}
|
|
||||||
l := binary.BigEndian.Uint16(p)
|
|
||||||
if l == 0 {
|
|
||||||
return 0, ErrShortRead
|
|
||||||
}
|
|
||||||
return int(l), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
|
|
||||||
func tcpRead(t io.Reader, p []byte) (int, error) {
|
|
||||||
n, err := t.Read(p)
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
for n < len(p) {
|
|
||||||
j, err := t.Read(p[n:])
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
n += j
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read implements the net.Conn read method.
|
|
||||||
func (co *Conn) Read(p []byte) (n int, err error) {
|
|
||||||
if co.Conn == nil {
|
|
||||||
return 0, ErrConnEmpty
|
|
||||||
}
|
|
||||||
if len(p) < 2 {
|
|
||||||
return 0, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
switch t := co.Conn.(type) {
|
|
||||||
case *net.TCPConn, *tls.Conn:
|
|
||||||
r := t.(io.Reader)
|
|
||||||
|
|
||||||
l, err := tcpMsgLen(r)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if l > len(p) {
|
|
||||||
return int(l), io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
return tcpRead(r, p[:l])
|
|
||||||
}
|
|
||||||
// UDP connection
|
|
||||||
n, err = co.Conn.Read(p)
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteMsg sends a message through the connection co.
|
|
||||||
// If the message m contains a TSIG record the transaction
|
|
||||||
// signature is calculated.
|
|
||||||
func (co *Conn) WriteMsg(m *Msg) (err error) {
|
|
||||||
var out []byte
|
|
||||||
if t := m.IsTsig(); t != nil {
|
|
||||||
mac := ""
|
|
||||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
|
||||||
return ErrSecret
|
|
||||||
}
|
|
||||||
out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
|
|
||||||
// Set for the next read, although only used in zone transfers
|
|
||||||
co.tsigRequestMAC = mac
|
|
||||||
} else {
|
|
||||||
out, err = m.Pack()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
co.t = time.Now()
|
|
||||||
if _, err = co.Write(out); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write implements the net.Conn Write method.
|
|
||||||
func (co *Conn) Write(p []byte) (n int, err error) {
|
|
||||||
switch t := co.Conn.(type) {
|
|
||||||
case *net.TCPConn, *tls.Conn:
|
|
||||||
w := t.(io.Writer)
|
|
||||||
|
|
||||||
lp := len(p)
|
|
||||||
if lp < 2 {
|
|
||||||
return 0, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
if lp > MaxMsgSize {
|
|
||||||
return 0, &Error{err: "message too large"}
|
|
||||||
}
|
|
||||||
l := make([]byte, 2, lp+2)
|
|
||||||
binary.BigEndian.PutUint16(l, uint16(lp))
|
|
||||||
p = append(l, p...)
|
|
||||||
n, err := io.Copy(w, bytes.NewReader(p))
|
|
||||||
return int(n), err
|
|
||||||
}
|
|
||||||
n, err = co.Conn.(*net.UDPConn).Write(p)
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the address on the named network.
|
|
||||||
func Dial(network, address string) (conn *Conn, err error) {
|
|
||||||
conn = new(Conn)
|
|
||||||
conn.Conn, err = net.Dial(network, address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialTimeout acts like Dial but takes a timeout.
|
|
||||||
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
|
|
||||||
conn = new(Conn)
|
|
||||||
conn.Conn, err = net.DialTimeout(network, address, timeout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialWithTLS connects to the address on the named network with TLS.
|
|
||||||
func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) {
|
|
||||||
conn = new(Conn)
|
|
||||||
conn.Conn, err = tls.Dial(network, address, tlsConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
|
|
||||||
func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) {
|
|
||||||
var dialer net.Dialer
|
|
||||||
dialer.Timeout = timeout
|
|
||||||
|
|
||||||
conn = new(Conn)
|
|
||||||
conn.Conn, err = tls.DialWithDialer(&dialer, network, address, tlsConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func deadlineOrTimeout(deadline time.Time, timeout time.Duration) time.Time {
|
|
||||||
if deadline.IsZero() {
|
|
||||||
return time.Now().Add(timeout)
|
|
||||||
}
|
|
||||||
return deadline
|
|
||||||
}
|
|
99
vendor/github.com/miekg/dns/clientconfig.go
generated
vendored
99
vendor/github.com/miekg/dns/clientconfig.go
generated
vendored
|
@ -1,99 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ClientConfig wraps the contents of the /etc/resolv.conf file.
|
|
||||||
type ClientConfig struct {
|
|
||||||
Servers []string // servers to use
|
|
||||||
Search []string // suffixes to append to local name
|
|
||||||
Port string // what port to use
|
|
||||||
Ndots int // number of dots in name to trigger absolute lookup
|
|
||||||
Timeout int // seconds before giving up on packet
|
|
||||||
Attempts int // lost packets before giving up on server, not used in the package dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientConfigFromFile parses a resolv.conf(5) like file and returns
|
|
||||||
// a *ClientConfig.
|
|
||||||
func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
|
|
||||||
file, err := os.Open(resolvconf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
c := new(ClientConfig)
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
c.Servers = make([]string, 0)
|
|
||||||
c.Search = make([]string, 0)
|
|
||||||
c.Port = "53"
|
|
||||||
c.Ndots = 1
|
|
||||||
c.Timeout = 5
|
|
||||||
c.Attempts = 2
|
|
||||||
|
|
||||||
for scanner.Scan() {
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
line := scanner.Text()
|
|
||||||
f := strings.Fields(line)
|
|
||||||
if len(f) < 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch f[0] {
|
|
||||||
case "nameserver": // add one name server
|
|
||||||
if len(f) > 1 {
|
|
||||||
// One more check: make sure server name is
|
|
||||||
// just an IP address. Otherwise we need DNS
|
|
||||||
// to look it up.
|
|
||||||
name := f[1]
|
|
||||||
c.Servers = append(c.Servers, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "domain": // set search path to just this domain
|
|
||||||
if len(f) > 1 {
|
|
||||||
c.Search = make([]string, 1)
|
|
||||||
c.Search[0] = f[1]
|
|
||||||
} else {
|
|
||||||
c.Search = make([]string, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "search": // set search path to given servers
|
|
||||||
c.Search = make([]string, len(f)-1)
|
|
||||||
for i := 0; i < len(c.Search); i++ {
|
|
||||||
c.Search[i] = f[i+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
case "options": // magic options
|
|
||||||
for i := 1; i < len(f); i++ {
|
|
||||||
s := f[i]
|
|
||||||
switch {
|
|
||||||
case len(s) >= 6 && s[:6] == "ndots:":
|
|
||||||
n, _ := strconv.Atoi(s[6:])
|
|
||||||
if n < 1 {
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
c.Ndots = n
|
|
||||||
case len(s) >= 8 && s[:8] == "timeout:":
|
|
||||||
n, _ := strconv.Atoi(s[8:])
|
|
||||||
if n < 1 {
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
c.Timeout = n
|
|
||||||
case len(s) >= 8 && s[:9] == "attempts:":
|
|
||||||
n, _ := strconv.Atoi(s[9:])
|
|
||||||
if n < 1 {
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
c.Attempts = n
|
|
||||||
case s == "rotate":
|
|
||||||
/* not imp */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
44
vendor/github.com/miekg/dns/dane.go
generated
vendored
44
vendor/github.com/miekg/dns/dane.go
generated
vendored
|
@ -1,44 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records.
|
|
||||||
func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) {
|
|
||||||
switch matchingType {
|
|
||||||
case 0:
|
|
||||||
switch selector {
|
|
||||||
case 0:
|
|
||||||
return hex.EncodeToString(cert.Raw), nil
|
|
||||||
case 1:
|
|
||||||
return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
h := sha256.New()
|
|
||||||
switch selector {
|
|
||||||
case 0:
|
|
||||||
io.WriteString(h, string(cert.Raw))
|
|
||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
|
||||||
case 1:
|
|
||||||
io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
|
|
||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
h := sha512.New()
|
|
||||||
switch selector {
|
|
||||||
case 0:
|
|
||||||
io.WriteString(h, string(cert.Raw))
|
|
||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
|
||||||
case 1:
|
|
||||||
io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
|
|
||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", errors.New("dns: bad MatchingType or Selector")
|
|
||||||
}
|
|
282
vendor/github.com/miekg/dns/defaults.go
generated
vendored
282
vendor/github.com/miekg/dns/defaults.go
generated
vendored
|
@ -1,282 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
const hexDigit = "0123456789abcdef"
|
|
||||||
|
|
||||||
// Everything is assumed in ClassINET.
|
|
||||||
|
|
||||||
// SetReply creates a reply message from a request message.
|
|
||||||
func (dns *Msg) SetReply(request *Msg) *Msg {
|
|
||||||
dns.Id = request.Id
|
|
||||||
dns.RecursionDesired = request.RecursionDesired // Copy rd bit
|
|
||||||
dns.Response = true
|
|
||||||
dns.Opcode = OpcodeQuery
|
|
||||||
dns.Rcode = RcodeSuccess
|
|
||||||
if len(request.Question) > 0 {
|
|
||||||
dns.Question = make([]Question, 1)
|
|
||||||
dns.Question[0] = request.Question[0]
|
|
||||||
}
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetQuestion creates a question message, it sets the Question
|
|
||||||
// section, generates an Id and sets the RecursionDesired (RD)
|
|
||||||
// bit to true.
|
|
||||||
func (dns *Msg) SetQuestion(z string, t uint16) *Msg {
|
|
||||||
dns.Id = Id()
|
|
||||||
dns.RecursionDesired = true
|
|
||||||
dns.Question = make([]Question, 1)
|
|
||||||
dns.Question[0] = Question{z, t, ClassINET}
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNotify creates a notify message, it sets the Question
|
|
||||||
// section, generates an Id and sets the Authoritative (AA)
|
|
||||||
// bit to true.
|
|
||||||
func (dns *Msg) SetNotify(z string) *Msg {
|
|
||||||
dns.Opcode = OpcodeNotify
|
|
||||||
dns.Authoritative = true
|
|
||||||
dns.Id = Id()
|
|
||||||
dns.Question = make([]Question, 1)
|
|
||||||
dns.Question[0] = Question{z, TypeSOA, ClassINET}
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRcode creates an error message suitable for the request.
|
|
||||||
func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {
|
|
||||||
dns.SetReply(request)
|
|
||||||
dns.Rcode = rcode
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRcodeFormatError creates a message with FormError set.
|
|
||||||
func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {
|
|
||||||
dns.Rcode = RcodeFormatError
|
|
||||||
dns.Opcode = OpcodeQuery
|
|
||||||
dns.Response = true
|
|
||||||
dns.Authoritative = false
|
|
||||||
dns.Id = request.Id
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUpdate makes the message a dynamic update message. It
|
|
||||||
// sets the ZONE section to: z, TypeSOA, ClassINET.
|
|
||||||
func (dns *Msg) SetUpdate(z string) *Msg {
|
|
||||||
dns.Id = Id()
|
|
||||||
dns.Response = false
|
|
||||||
dns.Opcode = OpcodeUpdate
|
|
||||||
dns.Compress = false // BIND9 cannot handle compression
|
|
||||||
dns.Question = make([]Question, 1)
|
|
||||||
dns.Question[0] = Question{z, TypeSOA, ClassINET}
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIxfr creates message for requesting an IXFR.
|
|
||||||
func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg {
|
|
||||||
dns.Id = Id()
|
|
||||||
dns.Question = make([]Question, 1)
|
|
||||||
dns.Ns = make([]RR, 1)
|
|
||||||
s := new(SOA)
|
|
||||||
s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}
|
|
||||||
s.Serial = serial
|
|
||||||
s.Ns = ns
|
|
||||||
s.Mbox = mbox
|
|
||||||
dns.Question[0] = Question{z, TypeIXFR, ClassINET}
|
|
||||||
dns.Ns[0] = s
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAxfr creates message for requesting an AXFR.
|
|
||||||
func (dns *Msg) SetAxfr(z string) *Msg {
|
|
||||||
dns.Id = Id()
|
|
||||||
dns.Question = make([]Question, 1)
|
|
||||||
dns.Question[0] = Question{z, TypeAXFR, ClassINET}
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTsig appends a TSIG RR to the message.
|
|
||||||
// This is only a skeleton TSIG RR that is added as the last RR in the
|
|
||||||
// additional section. The Tsig is calculated when the message is being send.
|
|
||||||
func (dns *Msg) SetTsig(z, algo string, fudge, timesigned int64) *Msg {
|
|
||||||
t := new(TSIG)
|
|
||||||
t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
|
|
||||||
t.Algorithm = algo
|
|
||||||
t.Fudge = 300
|
|
||||||
t.TimeSigned = uint64(timesigned)
|
|
||||||
t.OrigId = dns.Id
|
|
||||||
dns.Extra = append(dns.Extra, t)
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEdns0 appends a EDNS0 OPT RR to the message.
|
|
||||||
// TSIG should always the last RR in a message.
|
|
||||||
func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
|
|
||||||
e := new(OPT)
|
|
||||||
e.Hdr.Name = "."
|
|
||||||
e.Hdr.Rrtype = TypeOPT
|
|
||||||
e.SetUDPSize(udpsize)
|
|
||||||
if do {
|
|
||||||
e.SetDo()
|
|
||||||
}
|
|
||||||
dns.Extra = append(dns.Extra, e)
|
|
||||||
return dns
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTsig checks if the message has a TSIG record as the last record
|
|
||||||
// in the additional section. It returns the TSIG record found or nil.
|
|
||||||
func (dns *Msg) IsTsig() *TSIG {
|
|
||||||
if len(dns.Extra) > 0 {
|
|
||||||
if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
|
|
||||||
return dns.Extra[len(dns.Extra)-1].(*TSIG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
|
|
||||||
// record in the additional section will do. It returns the OPT record
|
|
||||||
// found or nil.
|
|
||||||
func (dns *Msg) IsEdns0() *OPT {
|
|
||||||
// EDNS0 is at the end of the additional section, start there.
|
|
||||||
// We might want to change this to *only* look at the last two
|
|
||||||
// records. So we see TSIG and/or OPT - this a slightly bigger
|
|
||||||
// change though.
|
|
||||||
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
|
||||||
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
|
||||||
return dns.Extra[i].(*OPT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDomainName checks if s is a valid domain name, it returns the number of
|
|
||||||
// labels and true, when a domain name is valid. Note that non fully qualified
|
|
||||||
// domain name is considered valid, in this case the last label is counted in
|
|
||||||
// the number of labels. When false is returned the number of labels is not
|
|
||||||
// defined. Also note that this function is extremely liberal; almost any
|
|
||||||
// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
|
|
||||||
// label fits in 63 characters, but there is no length check for the entire
|
|
||||||
// string s. I.e. a domain name longer than 255 characters is considered valid.
|
|
||||||
func IsDomainName(s string) (labels int, ok bool) {
|
|
||||||
_, labels, err := packDomainName(s, nil, 0, nil, false)
|
|
||||||
return labels, err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSubDomain checks if child is indeed a child of the parent. If child and parent
|
|
||||||
// are the same domain true is returned as well.
|
|
||||||
func IsSubDomain(parent, child string) bool {
|
|
||||||
// Entire child is contained in parent
|
|
||||||
return CompareDomainName(parent, child) == CountLabel(parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
|
|
||||||
// The checking is performed on the binary payload.
|
|
||||||
func IsMsg(buf []byte) error {
|
|
||||||
// Header
|
|
||||||
if len(buf) < 12 {
|
|
||||||
return errors.New("dns: bad message header")
|
|
||||||
}
|
|
||||||
// Header: Opcode
|
|
||||||
// TODO(miek): more checks here, e.g. check all header bits.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFqdn checks if a domain name is fully qualified.
|
|
||||||
func IsFqdn(s string) bool {
|
|
||||||
l := len(s)
|
|
||||||
if l == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
if IsFqdn(s) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s + "."
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from the official Go code.
|
|
||||||
|
|
||||||
// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
|
|
||||||
// address suitable for reverse DNS (PTR) record lookups or an error if it fails
|
|
||||||
// to parse the IP address.
|
|
||||||
func ReverseAddr(addr string) (arpa string, err error) {
|
|
||||||
ip := net.ParseIP(addr)
|
|
||||||
if ip == nil {
|
|
||||||
return "", &Error{err: "unrecognized address: " + addr}
|
|
||||||
}
|
|
||||||
if ip.To4() != nil {
|
|
||||||
return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." +
|
|
||||||
strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil
|
|
||||||
}
|
|
||||||
// Must be IPv6
|
|
||||||
buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
|
|
||||||
// Add it, in reverse, to the buffer
|
|
||||||
for i := len(ip) - 1; i >= 0; i-- {
|
|
||||||
v := ip[i]
|
|
||||||
buf = append(buf, hexDigit[v&0xF])
|
|
||||||
buf = append(buf, '.')
|
|
||||||
buf = append(buf, hexDigit[v>>4])
|
|
||||||
buf = append(buf, '.')
|
|
||||||
}
|
|
||||||
// Append "ip6.arpa." and return (buf already has the final .)
|
|
||||||
buf = append(buf, "ip6.arpa."...)
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string representation for the type t.
|
|
||||||
func (t Type) String() string {
|
|
||||||
if t1, ok := TypeToString[uint16(t)]; ok {
|
|
||||||
return t1
|
|
||||||
}
|
|
||||||
return "TYPE" + strconv.Itoa(int(t))
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string representation for the class c.
|
|
||||||
func (c Class) String() string {
|
|
||||||
if c1, ok := ClassToString[uint16(c)]; ok {
|
|
||||||
return c1
|
|
||||||
}
|
|
||||||
return "CLASS" + strconv.Itoa(int(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string representation for the name n.
|
|
||||||
func (n Name) String() string {
|
|
||||||
return sprintName(string(n))
|
|
||||||
}
|
|
104
vendor/github.com/miekg/dns/dns.go
generated
vendored
104
vendor/github.com/miekg/dns/dns.go
generated
vendored
|
@ -1,104 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
const (
|
|
||||||
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
|
|
||||||
defaultTtl = 3600 // Default internal TTL.
|
|
||||||
|
|
||||||
DefaultMsgSize = 4096 // DefaultMsgSize is the standard default for messages larger than 512 bytes.
|
|
||||||
MinMsgSize = 512 // MinMsgSize is the minimal size of a DNS packet.
|
|
||||||
MaxMsgSize = 65535 // MaxMsgSize is the largest possible DNS packet.
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error represents a DNS error.
|
|
||||||
type Error struct{ err string }
|
|
||||||
|
|
||||||
func (e *Error) Error() string {
|
|
||||||
if e == nil {
|
|
||||||
return "dns: <nil>"
|
|
||||||
}
|
|
||||||
return "dns: " + e.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// An RR represents a resource record.
|
|
||||||
type RR interface {
|
|
||||||
// Header returns the header of an resource record. The header contains
|
|
||||||
// everything up to the rdata.
|
|
||||||
Header() *RR_Header
|
|
||||||
// String returns the text representation of the resource record.
|
|
||||||
String() string
|
|
||||||
|
|
||||||
// copy returns a copy of the RR
|
|
||||||
copy() RR
|
|
||||||
// len returns the length (in octets) of the uncompressed RR in wire format.
|
|
||||||
len() int
|
|
||||||
// pack packs an RR into wire format.
|
|
||||||
pack([]byte, int, map[string]int, bool) (int, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RR_Header is the header all DNS resource records share.
|
|
||||||
type RR_Header struct {
|
|
||||||
Name string `dns:"cdomain-name"`
|
|
||||||
Rrtype uint16
|
|
||||||
Class uint16
|
|
||||||
Ttl uint32
|
|
||||||
Rdlength uint16 // Length of data after header.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header returns itself. This is here to make RR_Header implements the RR interface.
|
|
||||||
func (h *RR_Header) Header() *RR_Header { return h }
|
|
||||||
|
|
||||||
// Just to implement the RR interface.
|
|
||||||
func (h *RR_Header) copy() RR { return nil }
|
|
||||||
|
|
||||||
func (h *RR_Header) copyHeader() *RR_Header {
|
|
||||||
r := new(RR_Header)
|
|
||||||
r.Name = h.Name
|
|
||||||
r.Rrtype = h.Rrtype
|
|
||||||
r.Class = h.Class
|
|
||||||
r.Ttl = h.Ttl
|
|
||||||
r.Rdlength = h.Rdlength
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RR_Header) String() string {
|
|
||||||
var s string
|
|
||||||
|
|
||||||
if h.Rrtype == TypeOPT {
|
|
||||||
s = ";"
|
|
||||||
// and maybe other things
|
|
||||||
}
|
|
||||||
|
|
||||||
s += sprintName(h.Name) + "\t"
|
|
||||||
s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
|
|
||||||
s += Class(h.Class).String() + "\t"
|
|
||||||
s += Type(h.Rrtype).String() + "\t"
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RR_Header) len() int {
|
|
||||||
l := len(h.Name) + 1
|
|
||||||
l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.
|
|
||||||
func (rr *RFC3597) ToRFC3597(r RR) error {
|
|
||||||
buf := make([]byte, r.len()*2)
|
|
||||||
off, err := PackRR(r, buf, 0, nil, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buf = buf[:off]
|
|
||||||
if int(r.Header().Rdlength) > off {
|
|
||||||
return ErrBuf
|
|
||||||
}
|
|
||||||
|
|
||||||
rfc3597, _, err := unpackRFC3597(*r.Header(), buf, off-int(r.Header().Rdlength))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*rr = *rfc3597.(*RFC3597)
|
|
||||||
return nil
|
|
||||||
}
|
|
721
vendor/github.com/miekg/dns/dnssec.go
generated
vendored
721
vendor/github.com/miekg/dns/dnssec.go
generated
vendored
|
@ -1,721 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto"
|
|
||||||
"crypto/dsa"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
_ "crypto/md5"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
_ "crypto/sha1"
|
|
||||||
_ "crypto/sha256"
|
|
||||||
_ "crypto/sha512"
|
|
||||||
"encoding/asn1"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"math/big"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DNSSEC encryption algorithm codes.
|
|
||||||
const (
|
|
||||||
_ uint8 = iota
|
|
||||||
RSAMD5
|
|
||||||
DH
|
|
||||||
DSA
|
|
||||||
_ // Skip 4, RFC 6725, section 2.1
|
|
||||||
RSASHA1
|
|
||||||
DSANSEC3SHA1
|
|
||||||
RSASHA1NSEC3SHA1
|
|
||||||
RSASHA256
|
|
||||||
_ // Skip 9, RFC 6725, section 2.1
|
|
||||||
RSASHA512
|
|
||||||
_ // Skip 11, RFC 6725, section 2.1
|
|
||||||
ECCGOST
|
|
||||||
ECDSAP256SHA256
|
|
||||||
ECDSAP384SHA384
|
|
||||||
INDIRECT uint8 = 252
|
|
||||||
PRIVATEDNS uint8 = 253 // Private (experimental keys)
|
|
||||||
PRIVATEOID uint8 = 254
|
|
||||||
)
|
|
||||||
|
|
||||||
// Map for algorithm names.
|
|
||||||
var AlgorithmToString = map[uint8]string{
|
|
||||||
RSAMD5: "RSAMD5",
|
|
||||||
DH: "DH",
|
|
||||||
DSA: "DSA",
|
|
||||||
RSASHA1: "RSASHA1",
|
|
||||||
DSANSEC3SHA1: "DSA-NSEC3-SHA1",
|
|
||||||
RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
|
|
||||||
RSASHA256: "RSASHA256",
|
|
||||||
RSASHA512: "RSASHA512",
|
|
||||||
ECCGOST: "ECC-GOST",
|
|
||||||
ECDSAP256SHA256: "ECDSAP256SHA256",
|
|
||||||
ECDSAP384SHA384: "ECDSAP384SHA384",
|
|
||||||
INDIRECT: "INDIRECT",
|
|
||||||
PRIVATEDNS: "PRIVATEDNS",
|
|
||||||
PRIVATEOID: "PRIVATEOID",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map of algorithm strings.
|
|
||||||
var StringToAlgorithm = reverseInt8(AlgorithmToString)
|
|
||||||
|
|
||||||
// Map of algorithm crypto hashes.
|
|
||||||
var AlgorithmToHash = map[uint8]crypto.Hash{
|
|
||||||
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
|
|
||||||
RSASHA1: crypto.SHA1,
|
|
||||||
RSASHA1NSEC3SHA1: crypto.SHA1,
|
|
||||||
RSASHA256: crypto.SHA256,
|
|
||||||
ECDSAP256SHA256: crypto.SHA256,
|
|
||||||
ECDSAP384SHA384: crypto.SHA384,
|
|
||||||
RSASHA512: crypto.SHA512,
|
|
||||||
}
|
|
||||||
|
|
||||||
// DNSSEC hashing algorithm codes.
|
|
||||||
const (
|
|
||||||
_ uint8 = iota
|
|
||||||
SHA1 // RFC 4034
|
|
||||||
SHA256 // RFC 4509
|
|
||||||
GOST94 // RFC 5933
|
|
||||||
SHA384 // Experimental
|
|
||||||
SHA512 // Experimental
|
|
||||||
)
|
|
||||||
|
|
||||||
// Map for hash names.
|
|
||||||
var HashToString = map[uint8]string{
|
|
||||||
SHA1: "SHA1",
|
|
||||||
SHA256: "SHA256",
|
|
||||||
GOST94: "GOST94",
|
|
||||||
SHA384: "SHA384",
|
|
||||||
SHA512: "SHA512",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map of hash strings.
|
|
||||||
var StringToHash = reverseInt8(HashToString)
|
|
||||||
|
|
||||||
// DNSKEY flag values.
|
|
||||||
const (
|
|
||||||
SEP = 1
|
|
||||||
REVOKE = 1 << 7
|
|
||||||
ZONE = 1 << 8
|
|
||||||
)
|
|
||||||
|
|
||||||
// The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing.
|
|
||||||
type rrsigWireFmt struct {
|
|
||||||
TypeCovered uint16
|
|
||||||
Algorithm uint8
|
|
||||||
Labels uint8
|
|
||||||
OrigTtl uint32
|
|
||||||
Expiration uint32
|
|
||||||
Inception uint32
|
|
||||||
KeyTag uint16
|
|
||||||
SignerName string `dns:"domain-name"`
|
|
||||||
/* No Signature */
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for converting DNSKEY's rdata to wirefmt.
|
|
||||||
type dnskeyWireFmt struct {
|
|
||||||
Flags uint16
|
|
||||||
Protocol uint8
|
|
||||||
Algorithm uint8
|
|
||||||
PublicKey string `dns:"base64"`
|
|
||||||
/* Nothing is left out */
|
|
||||||
}
|
|
||||||
|
|
||||||
func divRoundUp(a, b int) int {
|
|
||||||
return (a + b - 1) / b
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyTag calculates the keytag (or key-id) of the DNSKEY.
|
|
||||||
func (k *DNSKEY) KeyTag() uint16 {
|
|
||||||
if k == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var keytag int
|
|
||||||
switch k.Algorithm {
|
|
||||||
case RSAMD5:
|
|
||||||
// Look at the bottom two bytes of the modules, which the last
|
|
||||||
// item in the pubkey. We could do this faster by looking directly
|
|
||||||
// at the base64 values. But I'm lazy.
|
|
||||||
modulus, _ := fromBase64([]byte(k.PublicKey))
|
|
||||||
if len(modulus) > 1 {
|
|
||||||
x := binary.BigEndian.Uint16(modulus[len(modulus)-2:])
|
|
||||||
keytag = int(x)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
keywire := new(dnskeyWireFmt)
|
|
||||||
keywire.Flags = k.Flags
|
|
||||||
keywire.Protocol = k.Protocol
|
|
||||||
keywire.Algorithm = k.Algorithm
|
|
||||||
keywire.PublicKey = k.PublicKey
|
|
||||||
wire := make([]byte, DefaultMsgSize)
|
|
||||||
n, err := packKeyWire(keywire, wire)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
wire = wire[:n]
|
|
||||||
for i, v := range wire {
|
|
||||||
if i&1 != 0 {
|
|
||||||
keytag += int(v) // must be larger than uint32
|
|
||||||
} else {
|
|
||||||
keytag += int(v) << 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keytag += (keytag >> 16) & 0xFFFF
|
|
||||||
keytag &= 0xFFFF
|
|
||||||
}
|
|
||||||
return uint16(keytag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDS converts a DNSKEY record to a DS record.
|
|
||||||
func (k *DNSKEY) ToDS(h uint8) *DS {
|
|
||||||
if k == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ds := new(DS)
|
|
||||||
ds.Hdr.Name = k.Hdr.Name
|
|
||||||
ds.Hdr.Class = k.Hdr.Class
|
|
||||||
ds.Hdr.Rrtype = TypeDS
|
|
||||||
ds.Hdr.Ttl = k.Hdr.Ttl
|
|
||||||
ds.Algorithm = k.Algorithm
|
|
||||||
ds.DigestType = h
|
|
||||||
ds.KeyTag = k.KeyTag()
|
|
||||||
|
|
||||||
keywire := new(dnskeyWireFmt)
|
|
||||||
keywire.Flags = k.Flags
|
|
||||||
keywire.Protocol = k.Protocol
|
|
||||||
keywire.Algorithm = k.Algorithm
|
|
||||||
keywire.PublicKey = k.PublicKey
|
|
||||||
wire := make([]byte, DefaultMsgSize)
|
|
||||||
n, err := packKeyWire(keywire, wire)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
wire = wire[:n]
|
|
||||||
|
|
||||||
owner := make([]byte, 255)
|
|
||||||
off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false)
|
|
||||||
if err1 != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
owner = owner[:off]
|
|
||||||
// RFC4034:
|
|
||||||
// digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
|
|
||||||
// "|" denotes concatenation
|
|
||||||
// DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
|
|
||||||
|
|
||||||
// digest buffer
|
|
||||||
digest := append(owner, wire...) // another copy
|
|
||||||
|
|
||||||
var hash crypto.Hash
|
|
||||||
switch h {
|
|
||||||
case SHA1:
|
|
||||||
hash = crypto.SHA1
|
|
||||||
case SHA256:
|
|
||||||
hash = crypto.SHA256
|
|
||||||
case SHA384:
|
|
||||||
hash = crypto.SHA384
|
|
||||||
case SHA512:
|
|
||||||
hash = crypto.SHA512
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
s := hash.New()
|
|
||||||
s.Write(digest)
|
|
||||||
ds.Digest = hex.EncodeToString(s.Sum(nil))
|
|
||||||
return ds
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
|
|
||||||
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
|
|
||||||
c := &CDNSKEY{DNSKEY: *k}
|
|
||||||
c.Hdr = *k.Hdr.copyHeader()
|
|
||||||
c.Hdr.Rrtype = TypeCDNSKEY
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToCDS converts a DS record to a CDS record.
|
|
||||||
func (d *DS) ToCDS() *CDS {
|
|
||||||
c := &CDS{DS: *d}
|
|
||||||
c.Hdr = *d.Hdr.copyHeader()
|
|
||||||
c.Hdr.Rrtype = TypeCDS
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign signs an RRSet. The signature needs to be filled in with the values:
|
|
||||||
// Inception, Expiration, KeyTag, SignerName and Algorithm. The rest is copied
|
|
||||||
// from the RRset. Sign returns a non-nill error when the signing went OK.
|
|
||||||
// There is no check if RRSet is a proper (RFC 2181) RRSet. If OrigTTL is non
|
|
||||||
// zero, it is used as-is, otherwise the TTL of the RRset is used as the
|
|
||||||
// OrigTTL.
|
|
||||||
func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
|
|
||||||
if k == nil {
|
|
||||||
return ErrPrivKey
|
|
||||||
}
|
|
||||||
// s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set
|
|
||||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
|
|
||||||
rr.Hdr.Rrtype = TypeRRSIG
|
|
||||||
rr.Hdr.Name = rrset[0].Header().Name
|
|
||||||
rr.Hdr.Class = rrset[0].Header().Class
|
|
||||||
if rr.OrigTtl == 0 { // If set don't override
|
|
||||||
rr.OrigTtl = rrset[0].Header().Ttl
|
|
||||||
}
|
|
||||||
rr.TypeCovered = rrset[0].Header().Rrtype
|
|
||||||
rr.Labels = uint8(CountLabel(rrset[0].Header().Name))
|
|
||||||
|
|
||||||
if strings.HasPrefix(rrset[0].Header().Name, "*") {
|
|
||||||
rr.Labels-- // wildcard, remove from label count
|
|
||||||
}
|
|
||||||
|
|
||||||
sigwire := new(rrsigWireFmt)
|
|
||||||
sigwire.TypeCovered = rr.TypeCovered
|
|
||||||
sigwire.Algorithm = rr.Algorithm
|
|
||||||
sigwire.Labels = rr.Labels
|
|
||||||
sigwire.OrigTtl = rr.OrigTtl
|
|
||||||
sigwire.Expiration = rr.Expiration
|
|
||||||
sigwire.Inception = rr.Inception
|
|
||||||
sigwire.KeyTag = rr.KeyTag
|
|
||||||
// For signing, lowercase this name
|
|
||||||
sigwire.SignerName = strings.ToLower(rr.SignerName)
|
|
||||||
|
|
||||||
// Create the desired binary blob
|
|
||||||
signdata := make([]byte, DefaultMsgSize)
|
|
||||||
n, err := packSigWire(sigwire, signdata)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
signdata = signdata[:n]
|
|
||||||
wire, err := rawSignatureData(rrset, rr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
signdata = append(signdata, wire...)
|
|
||||||
|
|
||||||
hash, ok := AlgorithmToHash[rr.Algorithm]
|
|
||||||
if !ok {
|
|
||||||
return ErrAlg
|
|
||||||
}
|
|
||||||
|
|
||||||
h := hash.New()
|
|
||||||
h.Write(signdata)
|
|
||||||
|
|
||||||
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rr.Signature = toBase64(signature)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) {
|
|
||||||
signature, err := k.Sign(rand.Reader, hashed, hash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch alg {
|
|
||||||
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
|
|
||||||
return signature, nil
|
|
||||||
|
|
||||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
|
||||||
ecdsaSignature := &struct {
|
|
||||||
R, S *big.Int
|
|
||||||
}{}
|
|
||||||
if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var intlen int
|
|
||||||
switch alg {
|
|
||||||
case ECDSAP256SHA256:
|
|
||||||
intlen = 32
|
|
||||||
case ECDSAP384SHA384:
|
|
||||||
intlen = 48
|
|
||||||
}
|
|
||||||
|
|
||||||
signature := intToBytes(ecdsaSignature.R, intlen)
|
|
||||||
signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
|
|
||||||
return signature, nil
|
|
||||||
|
|
||||||
// There is no defined interface for what a DSA backed crypto.Signer returns
|
|
||||||
case DSA, DSANSEC3SHA1:
|
|
||||||
// t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8)
|
|
||||||
// signature := []byte{byte(t)}
|
|
||||||
// signature = append(signature, intToBytes(r1, 20)...)
|
|
||||||
// signature = append(signature, intToBytes(s1, 20)...)
|
|
||||||
// rr.Signature = signature
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, ErrAlg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify validates an RRSet with the signature and key. This is only the
|
|
||||||
// cryptographic test, the signature validity period must be checked separately.
|
|
||||||
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
|
|
||||||
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
|
||||||
// First the easy checks
|
|
||||||
if !IsRRset(rrset) {
|
|
||||||
return ErrRRset
|
|
||||||
}
|
|
||||||
if rr.KeyTag != k.KeyTag() {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
if rr.Hdr.Class != k.Hdr.Class {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
if rr.Algorithm != k.Algorithm {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
if strings.ToLower(rr.SignerName) != strings.ToLower(k.Hdr.Name) {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
if k.Protocol != 3 {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
sigwire.TypeCovered = rr.TypeCovered
|
|
||||||
sigwire.Algorithm = rr.Algorithm
|
|
||||||
sigwire.Labels = rr.Labels
|
|
||||||
sigwire.OrigTtl = rr.OrigTtl
|
|
||||||
sigwire.Expiration = rr.Expiration
|
|
||||||
sigwire.Inception = rr.Inception
|
|
||||||
sigwire.KeyTag = rr.KeyTag
|
|
||||||
sigwire.SignerName = strings.ToLower(rr.SignerName)
|
|
||||||
// Create the desired binary blob
|
|
||||||
signeddata := make([]byte, DefaultMsgSize)
|
|
||||||
n, err := packSigWire(sigwire, signeddata)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
signeddata = signeddata[:n]
|
|
||||||
wire, err := rawSignatureData(rrset, rr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
signeddata = append(signeddata, wire...)
|
|
||||||
|
|
||||||
sigbuf := rr.sigBuf() // Get the binary signature data
|
|
||||||
if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
|
|
||||||
// TODO(miek)
|
|
||||||
// remove the domain name and assume its ours?
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, ok := AlgorithmToHash[rr.Algorithm]
|
|
||||||
if !ok {
|
|
||||||
return ErrAlg
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rr.Algorithm {
|
|
||||||
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, RSAMD5:
|
|
||||||
// TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??
|
|
||||||
pubkey := k.publicKeyRSA() // Get the key
|
|
||||||
if pubkey == nil {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
|
|
||||||
h := hash.New()
|
|
||||||
h.Write(signeddata)
|
|
||||||
return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
|
|
||||||
|
|
||||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
|
||||||
pubkey := k.publicKeyECDSA()
|
|
||||||
if pubkey == nil {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split sigbuf into the r and s coordinates
|
|
||||||
r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
|
|
||||||
s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
|
|
||||||
|
|
||||||
h := hash.New()
|
|
||||||
h.Write(signeddata)
|
|
||||||
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ErrSig
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ErrAlg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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. 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() {
|
|
||||||
utc = time.Now().UTC().Unix()
|
|
||||||
} else {
|
|
||||||
utc = t.UTC().Unix()
|
|
||||||
}
|
|
||||||
modi := (int64(rr.Inception) - utc) / year68
|
|
||||||
mode := (int64(rr.Expiration) - utc) / year68
|
|
||||||
ti := int64(rr.Inception) + (modi * year68)
|
|
||||||
te := int64(rr.Expiration) + (mode * year68)
|
|
||||||
return ti <= utc && utc <= te
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the signatures base64 encodedig sigdata as a byte slice.
|
|
||||||
func (rr *RRSIG) sigBuf() []byte {
|
|
||||||
sigbuf, err := fromBase64([]byte(rr.Signature))
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return sigbuf
|
|
||||||
}
|
|
||||||
|
|
||||||
// publicKeyRSA returns the RSA public key from a DNSKEY record.
|
|
||||||
func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
|
|
||||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RFC 2537/3110, section 2. RSA Public KEY Resource Records
|
|
||||||
// Length is in the 0th byte, unless its zero, then it
|
|
||||||
// it in bytes 1 and 2 and its a 16 bit number
|
|
||||||
explen := uint16(keybuf[0])
|
|
||||||
keyoff := 1
|
|
||||||
if explen == 0 {
|
|
||||||
explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
|
|
||||||
keyoff = 3
|
|
||||||
}
|
|
||||||
pubkey := new(rsa.PublicKey)
|
|
||||||
|
|
||||||
pubkey.N = big.NewInt(0)
|
|
||||||
shift := uint64((explen - 1) * 8)
|
|
||||||
expo := uint64(0)
|
|
||||||
for i := int(explen - 1); i > 0; i-- {
|
|
||||||
expo += uint64(keybuf[keyoff+i]) << shift
|
|
||||||
shift -= 8
|
|
||||||
}
|
|
||||||
// Remainder
|
|
||||||
expo += uint64(keybuf[keyoff])
|
|
||||||
if expo > 2<<31 {
|
|
||||||
// Larger expo than supported.
|
|
||||||
// println("dns: F5 primes (or larger) are not supported")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pubkey.E = int(expo)
|
|
||||||
|
|
||||||
pubkey.N.SetBytes(keybuf[keyoff+int(explen):])
|
|
||||||
return pubkey
|
|
||||||
}
|
|
||||||
|
|
||||||
// publicKeyECDSA returns the Curve public key from the DNSKEY record.
|
|
||||||
func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
|
|
||||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pubkey := new(ecdsa.PublicKey)
|
|
||||||
switch k.Algorithm {
|
|
||||||
case ECDSAP256SHA256:
|
|
||||||
pubkey.Curve = elliptic.P256()
|
|
||||||
if len(keybuf) != 64 {
|
|
||||||
// wrongly encoded key
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case ECDSAP384SHA384:
|
|
||||||
pubkey.Curve = elliptic.P384()
|
|
||||||
if len(keybuf) != 96 {
|
|
||||||
// Wrongly encoded key
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pubkey.X = big.NewInt(0)
|
|
||||||
pubkey.X.SetBytes(keybuf[:len(keybuf)/2])
|
|
||||||
pubkey.Y = big.NewInt(0)
|
|
||||||
pubkey.Y.SetBytes(keybuf[len(keybuf)/2:])
|
|
||||||
return pubkey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
|
|
||||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(keybuf) < 22 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
t, keybuf := int(keybuf[0]), keybuf[1:]
|
|
||||||
size := 64 + t*8
|
|
||||||
q, keybuf := keybuf[:20], keybuf[20:]
|
|
||||||
if len(keybuf) != 3*size {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
p, keybuf := keybuf[:size], keybuf[size:]
|
|
||||||
g, y := keybuf[:size], keybuf[size:]
|
|
||||||
pubkey := new(dsa.PublicKey)
|
|
||||||
pubkey.Parameters.Q = big.NewInt(0).SetBytes(q)
|
|
||||||
pubkey.Parameters.P = big.NewInt(0).SetBytes(p)
|
|
||||||
pubkey.Parameters.G = big.NewInt(0).SetBytes(g)
|
|
||||||
pubkey.Y = big.NewInt(0).SetBytes(y)
|
|
||||||
return pubkey
|
|
||||||
}
|
|
||||||
|
|
||||||
type wireSlice [][]byte
|
|
||||||
|
|
||||||
func (p wireSlice) Len() int { return len(p) }
|
|
||||||
func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
|
||||||
func (p wireSlice) Less(i, j int) bool {
|
|
||||||
_, ioff, _ := UnpackDomainName(p[i], 0)
|
|
||||||
_, joff, _ := UnpackDomainName(p[j], 0)
|
|
||||||
return bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the raw signature data.
|
|
||||||
func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
|
|
||||||
wires := make(wireSlice, len(rrset))
|
|
||||||
for i, r := range rrset {
|
|
||||||
r1 := r.copy()
|
|
||||||
r1.Header().Ttl = s.OrigTtl
|
|
||||||
labels := SplitDomainName(r1.Header().Name)
|
|
||||||
// 6.2. Canonical RR Form. (4) - wildcards
|
|
||||||
if len(labels) > int(s.Labels) {
|
|
||||||
// Wildcard
|
|
||||||
r1.Header().Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
|
|
||||||
}
|
|
||||||
// RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase
|
|
||||||
r1.Header().Name = strings.ToLower(r1.Header().Name)
|
|
||||||
// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
|
|
||||||
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
|
|
||||||
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
|
|
||||||
// SRV, DNAME, A6
|
|
||||||
//
|
|
||||||
// RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):
|
|
||||||
// Section 6.2 of [RFC4034] also erroneously lists HINFO as a record
|
|
||||||
// that needs conversion to lowercase, and twice at that. Since HINFO
|
|
||||||
// records contain no domain names, they are not subject to case
|
|
||||||
// conversion.
|
|
||||||
switch x := r1.(type) {
|
|
||||||
case *NS:
|
|
||||||
x.Ns = strings.ToLower(x.Ns)
|
|
||||||
case *CNAME:
|
|
||||||
x.Target = strings.ToLower(x.Target)
|
|
||||||
case *SOA:
|
|
||||||
x.Ns = strings.ToLower(x.Ns)
|
|
||||||
x.Mbox = strings.ToLower(x.Mbox)
|
|
||||||
case *MB:
|
|
||||||
x.Mb = strings.ToLower(x.Mb)
|
|
||||||
case *MG:
|
|
||||||
x.Mg = strings.ToLower(x.Mg)
|
|
||||||
case *MR:
|
|
||||||
x.Mr = strings.ToLower(x.Mr)
|
|
||||||
case *PTR:
|
|
||||||
x.Ptr = strings.ToLower(x.Ptr)
|
|
||||||
case *MINFO:
|
|
||||||
x.Rmail = strings.ToLower(x.Rmail)
|
|
||||||
x.Email = strings.ToLower(x.Email)
|
|
||||||
case *MX:
|
|
||||||
x.Mx = strings.ToLower(x.Mx)
|
|
||||||
case *NAPTR:
|
|
||||||
x.Replacement = strings.ToLower(x.Replacement)
|
|
||||||
case *KX:
|
|
||||||
x.Exchanger = strings.ToLower(x.Exchanger)
|
|
||||||
case *SRV:
|
|
||||||
x.Target = strings.ToLower(x.Target)
|
|
||||||
case *DNAME:
|
|
||||||
x.Target = strings.ToLower(x.Target)
|
|
||||||
}
|
|
||||||
// 6.2. Canonical RR Form. (5) - origTTL
|
|
||||||
wire := make([]byte, r1.len()+1) // +1 to be safe(r)
|
|
||||||
off, err1 := PackRR(r1, wire, 0, nil, false)
|
|
||||||
if err1 != nil {
|
|
||||||
return nil, err1
|
|
||||||
}
|
|
||||||
wire = wire[:off]
|
|
||||||
wires[i] = wire
|
|
||||||
}
|
|
||||||
sort.Sort(wires)
|
|
||||||
for i, wire := range wires {
|
|
||||||
if i > 0 && bytes.Equal(wire, wires[i-1]) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buf = append(buf, wire...)
|
|
||||||
}
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) {
|
|
||||||
// copied from zmsg.go RRSIG packing
|
|
||||||
off, err := packUint16(sw.TypeCovered, msg, 0)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint8(sw.Algorithm, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint8(sw.Labels, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint32(sw.OrigTtl, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint32(sw.Expiration, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint32(sw.Inception, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint16(sw.KeyTag, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = PackDomainName(sw.SignerName, msg, off, nil, false)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) {
|
|
||||||
// copied from zmsg.go DNSKEY packing
|
|
||||||
off, err := packUint16(dw.Flags, msg, 0)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint8(dw.Protocol, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint8(dw.Algorithm, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packStringBase64(dw.PublicKey, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
156
vendor/github.com/miekg/dns/dnssec_keygen.go
generated
vendored
156
vendor/github.com/miekg/dns/dnssec_keygen.go
generated
vendored
|
@ -1,156 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/dsa"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generate generates a DNSKEY of the given bit size.
|
|
||||||
// The public part is put inside the DNSKEY record.
|
|
||||||
// The Algorithm in the key must be set as this will define
|
|
||||||
// what kind of DNSKEY will be generated.
|
|
||||||
// The ECDSA algorithms imply a fixed keysize, in that case
|
|
||||||
// bits should be set to the size of the algorithm.
|
|
||||||
func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
|
|
||||||
switch k.Algorithm {
|
|
||||||
case DSA, DSANSEC3SHA1:
|
|
||||||
if bits != 1024 {
|
|
||||||
return nil, ErrKeySize
|
|
||||||
}
|
|
||||||
case RSAMD5, RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:
|
|
||||||
if bits < 512 || bits > 4096 {
|
|
||||||
return nil, ErrKeySize
|
|
||||||
}
|
|
||||||
case RSASHA512:
|
|
||||||
if bits < 1024 || bits > 4096 {
|
|
||||||
return nil, ErrKeySize
|
|
||||||
}
|
|
||||||
case ECDSAP256SHA256:
|
|
||||||
if bits != 256 {
|
|
||||||
return nil, ErrKeySize
|
|
||||||
}
|
|
||||||
case ECDSAP384SHA384:
|
|
||||||
if bits != 384 {
|
|
||||||
return nil, ErrKeySize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch k.Algorithm {
|
|
||||||
case DSA, DSANSEC3SHA1:
|
|
||||||
params := new(dsa.Parameters)
|
|
||||||
if err := dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
priv := new(dsa.PrivateKey)
|
|
||||||
priv.PublicKey.Parameters = *params
|
|
||||||
err := dsa.GenerateKey(priv, rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y)
|
|
||||||
return priv, nil
|
|
||||||
case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:
|
|
||||||
priv, err := rsa.GenerateKey(rand.Reader, bits)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N)
|
|
||||||
return priv, nil
|
|
||||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
|
||||||
var c elliptic.Curve
|
|
||||||
switch k.Algorithm {
|
|
||||||
case ECDSAP256SHA256:
|
|
||||||
c = elliptic.P256()
|
|
||||||
case ECDSAP384SHA384:
|
|
||||||
c = elliptic.P384()
|
|
||||||
}
|
|
||||||
priv, err := ecdsa.GenerateKey(c, rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)
|
|
||||||
return priv, nil
|
|
||||||
default:
|
|
||||||
return nil, ErrAlg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the public key (the value E and N)
|
|
||||||
func (k *DNSKEY) setPublicKeyRSA(_E int, _N *big.Int) bool {
|
|
||||||
if _E == 0 || _N == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
buf := exponentToBuf(_E)
|
|
||||||
buf = append(buf, _N.Bytes()...)
|
|
||||||
k.PublicKey = toBase64(buf)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the public key for Elliptic Curves
|
|
||||||
func (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool {
|
|
||||||
if _X == nil || _Y == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var intlen int
|
|
||||||
switch k.Algorithm {
|
|
||||||
case ECDSAP256SHA256:
|
|
||||||
intlen = 32
|
|
||||||
case ECDSAP384SHA384:
|
|
||||||
intlen = 48
|
|
||||||
}
|
|
||||||
k.PublicKey = toBase64(curveToBuf(_X, _Y, intlen))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the public key for DSA
|
|
||||||
func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool {
|
|
||||||
if _Q == nil || _P == nil || _G == nil || _Y == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
buf := dsaToBuf(_Q, _P, _G, _Y)
|
|
||||||
k.PublicKey = toBase64(buf)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the public key (the values E and N) for RSA
|
|
||||||
// RFC 3110: Section 2. RSA Public KEY Resource Records
|
|
||||||
func exponentToBuf(_E int) []byte {
|
|
||||||
var buf []byte
|
|
||||||
i := big.NewInt(int64(_E))
|
|
||||||
if len(i.Bytes()) < 256 {
|
|
||||||
buf = make([]byte, 1)
|
|
||||||
buf[0] = uint8(len(i.Bytes()))
|
|
||||||
} else {
|
|
||||||
buf = make([]byte, 3)
|
|
||||||
buf[0] = 0
|
|
||||||
buf[1] = uint8(len(i.Bytes()) >> 8)
|
|
||||||
buf[2] = uint8(len(i.Bytes()))
|
|
||||||
}
|
|
||||||
buf = append(buf, i.Bytes()...)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the public key for X and Y for Curve. The two
|
|
||||||
// values are just concatenated.
|
|
||||||
func curveToBuf(_X, _Y *big.Int, intlen int) []byte {
|
|
||||||
buf := intToBytes(_X, intlen)
|
|
||||||
buf = append(buf, intToBytes(_Y, intlen)...)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the public key for X and Y for Curve. The two
|
|
||||||
// values are just concatenated.
|
|
||||||
func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte {
|
|
||||||
t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8)
|
|
||||||
buf := []byte{byte(t)}
|
|
||||||
buf = append(buf, intToBytes(_Q, 20)...)
|
|
||||||
buf = append(buf, intToBytes(_P, 64+t*8)...)
|
|
||||||
buf = append(buf, intToBytes(_G, 64+t*8)...)
|
|
||||||
buf = append(buf, intToBytes(_Y, 64+t*8)...)
|
|
||||||
return buf
|
|
||||||
}
|
|
249
vendor/github.com/miekg/dns/dnssec_keyscan.go
generated
vendored
249
vendor/github.com/miekg/dns/dnssec_keyscan.go
generated
vendored
|
@ -1,249 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/dsa"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rsa"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewPrivateKey returns a PrivateKey by parsing the string s.
|
|
||||||
// s should be in the same form of the BIND private key files.
|
|
||||||
func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) {
|
|
||||||
if s == "" || s[len(s)-1] != '\n' { // We need a closing newline
|
|
||||||
return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
|
|
||||||
}
|
|
||||||
return k.ReadPrivateKey(strings.NewReader(s), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPrivateKey reads a private key from the io.Reader q. The string file is
|
|
||||||
// only used in error reporting.
|
|
||||||
// The public key must be known, because some cryptographic algorithms embed
|
|
||||||
// the public inside the privatekey.
|
|
||||||
func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) {
|
|
||||||
m, err := parseKey(q, file)
|
|
||||||
if m == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, ok := m["private-key-format"]; !ok {
|
|
||||||
return nil, ErrPrivKey
|
|
||||||
}
|
|
||||||
if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
|
|
||||||
return nil, ErrPrivKey
|
|
||||||
}
|
|
||||||
// TODO(mg): check if the pubkey matches the private key
|
|
||||||
algo, err := strconv.Atoi(strings.SplitN(m["algorithm"], " ", 2)[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, ErrPrivKey
|
|
||||||
}
|
|
||||||
switch uint8(algo) {
|
|
||||||
case DSA:
|
|
||||||
priv, err := readPrivateKeyDSA(m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pub := k.publicKeyDSA()
|
|
||||||
if pub == nil {
|
|
||||||
return nil, ErrKey
|
|
||||||
}
|
|
||||||
priv.PublicKey = *pub
|
|
||||||
return priv, nil
|
|
||||||
case RSAMD5:
|
|
||||||
fallthrough
|
|
||||||
case RSASHA1:
|
|
||||||
fallthrough
|
|
||||||
case RSASHA1NSEC3SHA1:
|
|
||||||
fallthrough
|
|
||||||
case RSASHA256:
|
|
||||||
fallthrough
|
|
||||||
case RSASHA512:
|
|
||||||
priv, err := readPrivateKeyRSA(m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pub := k.publicKeyRSA()
|
|
||||||
if pub == nil {
|
|
||||||
return nil, ErrKey
|
|
||||||
}
|
|
||||||
priv.PublicKey = *pub
|
|
||||||
return priv, nil
|
|
||||||
case ECCGOST:
|
|
||||||
return nil, ErrPrivKey
|
|
||||||
case ECDSAP256SHA256:
|
|
||||||
fallthrough
|
|
||||||
case ECDSAP384SHA384:
|
|
||||||
priv, err := readPrivateKeyECDSA(m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pub := k.publicKeyECDSA()
|
|
||||||
if pub == nil {
|
|
||||||
return nil, ErrKey
|
|
||||||
}
|
|
||||||
priv.PublicKey = *pub
|
|
||||||
return priv, nil
|
|
||||||
default:
|
|
||||||
return nil, ErrPrivKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a private key (file) string and create a public key. Return the private key.
|
|
||||||
func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
|
|
||||||
p := new(rsa.PrivateKey)
|
|
||||||
p.Primes = []*big.Int{nil, nil}
|
|
||||||
for k, v := range m {
|
|
||||||
switch k {
|
|
||||||
case "modulus", "publicexponent", "privateexponent", "prime1", "prime2":
|
|
||||||
v1, err := fromBase64([]byte(v))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch k {
|
|
||||||
case "modulus":
|
|
||||||
p.PublicKey.N = big.NewInt(0)
|
|
||||||
p.PublicKey.N.SetBytes(v1)
|
|
||||||
case "publicexponent":
|
|
||||||
i := big.NewInt(0)
|
|
||||||
i.SetBytes(v1)
|
|
||||||
p.PublicKey.E = int(i.Int64()) // int64 should be large enough
|
|
||||||
case "privateexponent":
|
|
||||||
p.D = big.NewInt(0)
|
|
||||||
p.D.SetBytes(v1)
|
|
||||||
case "prime1":
|
|
||||||
p.Primes[0] = big.NewInt(0)
|
|
||||||
p.Primes[0].SetBytes(v1)
|
|
||||||
case "prime2":
|
|
||||||
p.Primes[1] = big.NewInt(0)
|
|
||||||
p.Primes[1].SetBytes(v1)
|
|
||||||
}
|
|
||||||
case "exponent1", "exponent2", "coefficient":
|
|
||||||
// not used in Go (yet)
|
|
||||||
case "created", "publish", "activate":
|
|
||||||
// not used in Go (yet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
|
|
||||||
p := new(dsa.PrivateKey)
|
|
||||||
p.X = big.NewInt(0)
|
|
||||||
for k, v := range m {
|
|
||||||
switch k {
|
|
||||||
case "private_value(x)":
|
|
||||||
v1, err := fromBase64([]byte(v))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p.X.SetBytes(v1)
|
|
||||||
case "created", "publish", "activate":
|
|
||||||
/* not used in Go (yet) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
|
|
||||||
p := new(ecdsa.PrivateKey)
|
|
||||||
p.D = big.NewInt(0)
|
|
||||||
// TODO: validate that the required flags are present
|
|
||||||
for k, v := range m {
|
|
||||||
switch k {
|
|
||||||
case "privatekey":
|
|
||||||
v1, err := fromBase64([]byte(v))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p.D.SetBytes(v1)
|
|
||||||
case "created", "publish", "activate":
|
|
||||||
/* not used in Go (yet) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseKey reads a private key from r. It returns a map[string]string,
|
|
||||||
// with the key-value pairs, or an error when the file is not correct.
|
|
||||||
func parseKey(r io.Reader, file string) (map[string]string, error) {
|
|
||||||
s := scanInit(r)
|
|
||||||
m := make(map[string]string)
|
|
||||||
c := make(chan lex)
|
|
||||||
k := ""
|
|
||||||
// Start the lexer
|
|
||||||
go klexer(s, c)
|
|
||||||
for l := range c {
|
|
||||||
// It should alternate
|
|
||||||
switch l.value {
|
|
||||||
case zKey:
|
|
||||||
k = l.token
|
|
||||||
case zValue:
|
|
||||||
if k == "" {
|
|
||||||
return nil, &ParseError{file, "no private key seen", l}
|
|
||||||
}
|
|
||||||
//println("Setting", strings.ToLower(k), "to", l.token, "b")
|
|
||||||
m[strings.ToLower(k)] = l.token
|
|
||||||
k = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// klexer scans the sourcefile and returns tokens on the channel c.
|
|
||||||
func klexer(s *scan, c chan lex) {
|
|
||||||
var l lex
|
|
||||||
str := "" // Hold the current read text
|
|
||||||
commt := false
|
|
||||||
key := true
|
|
||||||
x, err := s.tokenText()
|
|
||||||
defer close(c)
|
|
||||||
for err == nil {
|
|
||||||
l.column = s.position.Column
|
|
||||||
l.line = s.position.Line
|
|
||||||
switch x {
|
|
||||||
case ':':
|
|
||||||
if commt {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
l.token = str
|
|
||||||
if key {
|
|
||||||
l.value = zKey
|
|
||||||
c <- l
|
|
||||||
// Next token is a space, eat it
|
|
||||||
s.tokenText()
|
|
||||||
key = false
|
|
||||||
str = ""
|
|
||||||
} else {
|
|
||||||
l.value = zValue
|
|
||||||
}
|
|
||||||
case ';':
|
|
||||||
commt = true
|
|
||||||
case '\n':
|
|
||||||
if commt {
|
|
||||||
// Reset a comment
|
|
||||||
commt = false
|
|
||||||
}
|
|
||||||
l.value = zValue
|
|
||||||
l.token = str
|
|
||||||
c <- l
|
|
||||||
str = ""
|
|
||||||
commt = false
|
|
||||||
key = true
|
|
||||||
default:
|
|
||||||
if commt {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
str += string(x)
|
|
||||||
}
|
|
||||||
x, err = s.tokenText()
|
|
||||||
}
|
|
||||||
if len(str) > 0 {
|
|
||||||
// Send remainder
|
|
||||||
l.token = str
|
|
||||||
l.value = zValue
|
|
||||||
c <- l
|
|
||||||
}
|
|
||||||
}
|
|
85
vendor/github.com/miekg/dns/dnssec_privkey.go
generated
vendored
85
vendor/github.com/miekg/dns/dnssec_privkey.go
generated
vendored
|
@ -1,85 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/dsa"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rsa"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
const format = "Private-key-format: v1.3\n"
|
|
||||||
|
|
||||||
// PrivateKeyString converts a PrivateKey to a string. This string has the same
|
|
||||||
// format as the private-key-file of BIND9 (Private-key-format: v1.3).
|
|
||||||
// It needs some info from the key (the algorithm), so its a method of the DNSKEY
|
|
||||||
// It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey
|
|
||||||
func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
|
|
||||||
algorithm := strconv.Itoa(int(r.Algorithm))
|
|
||||||
algorithm += " (" + AlgorithmToString[r.Algorithm] + ")"
|
|
||||||
|
|
||||||
switch p := p.(type) {
|
|
||||||
case *rsa.PrivateKey:
|
|
||||||
modulus := toBase64(p.PublicKey.N.Bytes())
|
|
||||||
e := big.NewInt(int64(p.PublicKey.E))
|
|
||||||
publicExponent := toBase64(e.Bytes())
|
|
||||||
privateExponent := toBase64(p.D.Bytes())
|
|
||||||
prime1 := toBase64(p.Primes[0].Bytes())
|
|
||||||
prime2 := toBase64(p.Primes[1].Bytes())
|
|
||||||
// Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm
|
|
||||||
// and from: http://code.google.com/p/go/issues/detail?id=987
|
|
||||||
one := big.NewInt(1)
|
|
||||||
p1 := big.NewInt(0).Sub(p.Primes[0], one)
|
|
||||||
q1 := big.NewInt(0).Sub(p.Primes[1], one)
|
|
||||||
exp1 := big.NewInt(0).Mod(p.D, p1)
|
|
||||||
exp2 := big.NewInt(0).Mod(p.D, q1)
|
|
||||||
coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0])
|
|
||||||
|
|
||||||
exponent1 := toBase64(exp1.Bytes())
|
|
||||||
exponent2 := toBase64(exp2.Bytes())
|
|
||||||
coefficient := toBase64(coeff.Bytes())
|
|
||||||
|
|
||||||
return format +
|
|
||||||
"Algorithm: " + algorithm + "\n" +
|
|
||||||
"Modulus: " + modulus + "\n" +
|
|
||||||
"PublicExponent: " + publicExponent + "\n" +
|
|
||||||
"PrivateExponent: " + privateExponent + "\n" +
|
|
||||||
"Prime1: " + prime1 + "\n" +
|
|
||||||
"Prime2: " + prime2 + "\n" +
|
|
||||||
"Exponent1: " + exponent1 + "\n" +
|
|
||||||
"Exponent2: " + exponent2 + "\n" +
|
|
||||||
"Coefficient: " + coefficient + "\n"
|
|
||||||
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
var intlen int
|
|
||||||
switch r.Algorithm {
|
|
||||||
case ECDSAP256SHA256:
|
|
||||||
intlen = 32
|
|
||||||
case ECDSAP384SHA384:
|
|
||||||
intlen = 48
|
|
||||||
}
|
|
||||||
private := toBase64(intToBytes(p.D, intlen))
|
|
||||||
return format +
|
|
||||||
"Algorithm: " + algorithm + "\n" +
|
|
||||||
"PrivateKey: " + private + "\n"
|
|
||||||
|
|
||||||
case *dsa.PrivateKey:
|
|
||||||
T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8)
|
|
||||||
prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8))
|
|
||||||
subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20))
|
|
||||||
base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8))
|
|
||||||
priv := toBase64(intToBytes(p.X, 20))
|
|
||||||
pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8))
|
|
||||||
return format +
|
|
||||||
"Algorithm: " + algorithm + "\n" +
|
|
||||||
"Prime(p): " + prime + "\n" +
|
|
||||||
"Subprime(q): " + subprime + "\n" +
|
|
||||||
"Base(g): " + base + "\n" +
|
|
||||||
"Private_value(x): " + priv + "\n" +
|
|
||||||
"Public_value(y): " + pub + "\n"
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
251
vendor/github.com/miekg/dns/doc.go
generated
vendored
251
vendor/github.com/miekg/dns/doc.go
generated
vendored
|
@ -1,251 +0,0 @@
|
||||||
/*
|
|
||||||
Package dns implements a full featured interface to the Domain Name System.
|
|
||||||
Server- and client-side programming is supported.
|
|
||||||
The package allows complete control over what is send out to the DNS. The package
|
|
||||||
API follows the less-is-more principle, by presenting a small, clean interface.
|
|
||||||
|
|
||||||
The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
|
|
||||||
TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
|
|
||||||
Note that domain names MUST be fully qualified, before sending them, unqualified
|
|
||||||
names in a message will result in a packing failure.
|
|
||||||
|
|
||||||
Resource records are native types. They are not stored in wire format.
|
|
||||||
Basic usage pattern for creating a new resource record:
|
|
||||||
|
|
||||||
r := new(dns.MX)
|
|
||||||
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX,
|
|
||||||
Class: dns.ClassINET, Ttl: 3600}
|
|
||||||
r.Preference = 10
|
|
||||||
r.Mx = "mx.miek.nl."
|
|
||||||
|
|
||||||
Or directly from a string:
|
|
||||||
|
|
||||||
mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
|
|
||||||
|
|
||||||
Or when the default TTL (3600) and class (IN) suit you:
|
|
||||||
|
|
||||||
mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.")
|
|
||||||
|
|
||||||
Or even:
|
|
||||||
|
|
||||||
mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
|
|
||||||
|
|
||||||
In the DNS messages are exchanged, these messages contain resource
|
|
||||||
records (sets). Use pattern for creating a message:
|
|
||||||
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetQuestion("miek.nl.", dns.TypeMX)
|
|
||||||
|
|
||||||
Or when not certain if the domain name is fully qualified:
|
|
||||||
|
|
||||||
m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
|
|
||||||
|
|
||||||
The message m is now a message with the question section set to ask
|
|
||||||
the MX records for the miek.nl. zone.
|
|
||||||
|
|
||||||
The following is slightly more verbose, but more flexible:
|
|
||||||
|
|
||||||
m1 := new(dns.Msg)
|
|
||||||
m1.Id = dns.Id()
|
|
||||||
m1.RecursionDesired = true
|
|
||||||
m1.Question = make([]dns.Question, 1)
|
|
||||||
m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
|
|
||||||
|
|
||||||
After creating a message it can be send.
|
|
||||||
Basic use pattern for synchronous querying the DNS at a
|
|
||||||
server configured on 127.0.0.1 and port 53:
|
|
||||||
|
|
||||||
c := new(dns.Client)
|
|
||||||
in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
|
|
||||||
|
|
||||||
Suppressing multiple outstanding queries (with the same question, type and
|
|
||||||
class) is as easy as setting:
|
|
||||||
|
|
||||||
c.SingleInflight = true
|
|
||||||
|
|
||||||
If these "advanced" features are not needed, a simple UDP query can be send,
|
|
||||||
with:
|
|
||||||
|
|
||||||
in, err := dns.Exchange(m1, "127.0.0.1:53")
|
|
||||||
|
|
||||||
When this functions returns you will get dns message. A dns message consists
|
|
||||||
out of four sections.
|
|
||||||
The question section: in.Question, the answer section: in.Answer,
|
|
||||||
the authority section: in.Ns and the additional section: in.Extra.
|
|
||||||
|
|
||||||
Each of these sections (except the Question section) contain a []RR. Basic
|
|
||||||
use pattern for accessing the rdata of a TXT RR as the first RR in
|
|
||||||
the Answer section:
|
|
||||||
|
|
||||||
if t, ok := in.Answer[0].(*dns.TXT); ok {
|
|
||||||
// do something with t.Txt
|
|
||||||
}
|
|
||||||
|
|
||||||
Domain Name and TXT Character String Representations
|
|
||||||
|
|
||||||
Both domain names and TXT character strings are converted to presentation
|
|
||||||
form both when unpacked and when converted to strings.
|
|
||||||
|
|
||||||
For TXT character strings, tabs, carriage returns and line feeds will be
|
|
||||||
converted to \t, \r and \n respectively. Back slashes and quotations marks
|
|
||||||
will be escaped. Bytes below 32 and above 127 will be converted to \DDD
|
|
||||||
form.
|
|
||||||
|
|
||||||
For domain names, in addition to the above rules brackets, periods,
|
|
||||||
spaces, semicolons and the at symbol are escaped.
|
|
||||||
|
|
||||||
DNSSEC
|
|
||||||
|
|
||||||
DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It
|
|
||||||
uses public key cryptography to sign resource records. The
|
|
||||||
public keys are stored in DNSKEY records and the signatures in RRSIG records.
|
|
||||||
|
|
||||||
Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit
|
|
||||||
to a request.
|
|
||||||
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetEdns0(4096, true)
|
|
||||||
|
|
||||||
Signature generation, signature verification and key generation are all supported.
|
|
||||||
|
|
||||||
DYNAMIC UPDATES
|
|
||||||
|
|
||||||
Dynamic updates reuses the DNS message format, but renames three of
|
|
||||||
the sections. Question is Zone, Answer is Prerequisite, Authority is
|
|
||||||
Update, only the Additional is not renamed. See RFC 2136 for the gory details.
|
|
||||||
|
|
||||||
You can set a rather complex set of rules for the existence of absence of
|
|
||||||
certain resource records or names in a zone to specify if resource records
|
|
||||||
should be added or removed. The table from RFC 2136 supplemented with the Go
|
|
||||||
DNS function shows which functions exist to specify the prerequisites.
|
|
||||||
|
|
||||||
3.2.4 - Table Of Metavalues Used In Prerequisite Section
|
|
||||||
|
|
||||||
CLASS TYPE RDATA Meaning Function
|
|
||||||
--------------------------------------------------------------
|
|
||||||
ANY ANY empty Name is in use dns.NameUsed
|
|
||||||
ANY rrset empty RRset exists (value indep) dns.RRsetUsed
|
|
||||||
NONE ANY empty Name is not in use dns.NameNotUsed
|
|
||||||
NONE rrset empty RRset does not exist dns.RRsetNotUsed
|
|
||||||
zone rrset rr RRset exists (value dep) dns.Used
|
|
||||||
|
|
||||||
The prerequisite section can also be left empty.
|
|
||||||
If you have decided on the prerequisites you can tell what RRs should
|
|
||||||
be added or deleted. The next table shows the options you have and
|
|
||||||
what functions to call.
|
|
||||||
|
|
||||||
3.4.2.6 - Table Of Metavalues Used In Update Section
|
|
||||||
|
|
||||||
CLASS TYPE RDATA Meaning Function
|
|
||||||
---------------------------------------------------------------
|
|
||||||
ANY ANY empty Delete all RRsets from name dns.RemoveName
|
|
||||||
ANY rrset empty Delete an RRset dns.RemoveRRset
|
|
||||||
NONE rrset rr Delete an RR from RRset dns.Remove
|
|
||||||
zone rrset rr Add to an RRset dns.Insert
|
|
||||||
|
|
||||||
TRANSACTION SIGNATURE
|
|
||||||
|
|
||||||
An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
|
|
||||||
The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512.
|
|
||||||
|
|
||||||
Basic use pattern when querying with a TSIG name "axfr." (note that these key names
|
|
||||||
must be fully qualified - as they are domain names) and the base64 secret
|
|
||||||
"so6ZGir4GPAqINNh9U5c3A==":
|
|
||||||
|
|
||||||
c := new(dns.Client)
|
|
||||||
c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetQuestion("miek.nl.", dns.TypeMX)
|
|
||||||
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
|
|
||||||
...
|
|
||||||
// When sending the TSIG RR is calculated and filled in before sending
|
|
||||||
|
|
||||||
When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with
|
|
||||||
TSIG, this is the basic use pattern. In this example we request an AXFR for
|
|
||||||
miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
|
|
||||||
and using the server 176.58.119.54:
|
|
||||||
|
|
||||||
t := new(dns.Transfer)
|
|
||||||
m := new(dns.Msg)
|
|
||||||
t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
|
|
||||||
m.SetAxfr("miek.nl.")
|
|
||||||
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
|
|
||||||
c, err := t.In(m, "176.58.119.54:53")
|
|
||||||
for r := range c { ... }
|
|
||||||
|
|
||||||
You can now read the records from the transfer as they come in. Each envelope is checked with TSIG.
|
|
||||||
If something is not correct an error is returned.
|
|
||||||
|
|
||||||
Basic use pattern validating and replying to a message that has TSIG set.
|
|
||||||
|
|
||||||
server := &dns.Server{Addr: ":53", Net: "udp"}
|
|
||||||
server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
|
|
||||||
go server.ListenAndServe()
|
|
||||||
dns.HandleFunc(".", handleRequest)
|
|
||||||
|
|
||||||
func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetReply(r)
|
|
||||||
if r.IsTsig() != nil {
|
|
||||||
if w.TsigStatus() == nil {
|
|
||||||
// *Msg r has an TSIG record and it was validated
|
|
||||||
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
|
|
||||||
} else {
|
|
||||||
// *Msg r has an TSIG records and it was not valided
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.WriteMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
PRIVATE RRS
|
|
||||||
|
|
||||||
RFC 6895 sets aside a range of type codes for private use. This range
|
|
||||||
is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
|
|
||||||
can be used, before requesting an official type code from IANA.
|
|
||||||
|
|
||||||
see http://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
|
|
||||||
information.
|
|
||||||
|
|
||||||
EDNS0
|
|
||||||
|
|
||||||
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated
|
|
||||||
by RFC 6891. It defines an new RR type, the OPT RR, which is then completely
|
|
||||||
abused.
|
|
||||||
Basic use pattern for creating an (empty) OPT RR:
|
|
||||||
|
|
||||||
o := new(dns.OPT)
|
|
||||||
o.Hdr.Name = "." // MUST be the root zone, per definition.
|
|
||||||
o.Hdr.Rrtype = dns.TypeOPT
|
|
||||||
|
|
||||||
The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891)
|
|
||||||
interfaces. Currently only a few have been standardized: EDNS0_NSID
|
|
||||||
(RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note
|
|
||||||
that these options may be combined in an OPT RR.
|
|
||||||
Basic use pattern for a server to check if (and which) options are set:
|
|
||||||
|
|
||||||
// o is a dns.OPT
|
|
||||||
for _, s := range o.Option {
|
|
||||||
switch e := s.(type) {
|
|
||||||
case *dns.EDNS0_NSID:
|
|
||||||
// do stuff with e.Nsid
|
|
||||||
case *dns.EDNS0_SUBNET:
|
|
||||||
// access e.Family, e.Address, etc.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SIG(0)
|
|
||||||
|
|
||||||
From RFC 2931:
|
|
||||||
|
|
||||||
SIG(0) provides protection for DNS transactions and requests ....
|
|
||||||
... protection for glue records, DNS requests, protection for message headers
|
|
||||||
on requests and responses, and protection of the overall integrity of a response.
|
|
||||||
|
|
||||||
It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared
|
|
||||||
secret approach in TSIG.
|
|
||||||
Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and
|
|
||||||
RSASHA512.
|
|
||||||
|
|
||||||
Signing subsequent messages in multi-message sessions is not implemented.
|
|
||||||
*/
|
|
||||||
package dns
|
|
597
vendor/github.com/miekg/dns/edns.go
generated
vendored
597
vendor/github.com/miekg/dns/edns.go
generated
vendored
|
@ -1,597 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EDNS0 Option codes.
|
|
||||||
const (
|
|
||||||
EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
|
||||||
EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
|
|
||||||
EDNS0NSID = 0x3 // nsid (RFC5001)
|
|
||||||
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
|
|
||||||
EDNS0DHU = 0x6 // DS Hash Understood
|
|
||||||
EDNS0N3U = 0x7 // NSEC3 Hash Understood
|
|
||||||
EDNS0SUBNET = 0x8 // client-subnet (RFC6891)
|
|
||||||
EDNS0EXPIRE = 0x9 // EDNS0 expire
|
|
||||||
EDNS0COOKIE = 0xa // EDNS0 Cookie
|
|
||||||
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (RFC7828)
|
|
||||||
EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
|
|
||||||
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (RFC6891)
|
|
||||||
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (RFC6891)
|
|
||||||
_DO = 1 << 15 // dnssec ok
|
|
||||||
)
|
|
||||||
|
|
||||||
// OPT is the EDNS0 RR appended to messages to convey extra (meta) information.
|
|
||||||
// See RFC 6891.
|
|
||||||
type OPT struct {
|
|
||||||
Hdr RR_Header
|
|
||||||
Option []EDNS0 `dns:"opt"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *OPT) String() string {
|
|
||||||
s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
|
|
||||||
if rr.Do() {
|
|
||||||
s += "flags: do; "
|
|
||||||
} else {
|
|
||||||
s += "flags: ; "
|
|
||||||
}
|
|
||||||
s += "udp: " + strconv.Itoa(int(rr.UDPSize()))
|
|
||||||
|
|
||||||
for _, o := range rr.Option {
|
|
||||||
switch o.(type) {
|
|
||||||
case *EDNS0_NSID:
|
|
||||||
s += "\n; NSID: " + o.String()
|
|
||||||
h, e := o.pack()
|
|
||||||
var r string
|
|
||||||
if e == nil {
|
|
||||||
for _, c := range h {
|
|
||||||
r += "(" + string(c) + ")"
|
|
||||||
}
|
|
||||||
s += " " + r
|
|
||||||
}
|
|
||||||
case *EDNS0_SUBNET:
|
|
||||||
s += "\n; SUBNET: " + o.String()
|
|
||||||
if o.(*EDNS0_SUBNET).DraftOption {
|
|
||||||
s += " (draft)"
|
|
||||||
}
|
|
||||||
case *EDNS0_COOKIE:
|
|
||||||
s += "\n; COOKIE: " + o.String()
|
|
||||||
case *EDNS0_UL:
|
|
||||||
s += "\n; UPDATE LEASE: " + o.String()
|
|
||||||
case *EDNS0_LLQ:
|
|
||||||
s += "\n; LONG LIVED QUERIES: " + o.String()
|
|
||||||
case *EDNS0_DAU:
|
|
||||||
s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String()
|
|
||||||
case *EDNS0_DHU:
|
|
||||||
s += "\n; DS HASH UNDERSTOOD: " + o.String()
|
|
||||||
case *EDNS0_N3U:
|
|
||||||
s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String()
|
|
||||||
case *EDNS0_LOCAL:
|
|
||||||
s += "\n; LOCAL OPT: " + o.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *OPT) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
for i := 0; i < len(rr.Option); i++ {
|
|
||||||
l += 4 // Account for 2-byte option code and 2-byte option length.
|
|
||||||
lo, _ := rr.Option[i].pack()
|
|
||||||
l += len(lo)
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the old value -> delete SetVersion?
|
|
||||||
|
|
||||||
// Version returns the EDNS version used. Only zero is defined.
|
|
||||||
func (rr *OPT) Version() uint8 {
|
|
||||||
return uint8((rr.Hdr.Ttl & 0x00FF0000) >> 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVersion sets the version of EDNS. This is usually zero.
|
|
||||||
func (rr *OPT) SetVersion(v uint8) {
|
|
||||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | (uint32(v) << 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
|
|
||||||
func (rr *OPT) ExtendedRcode() int {
|
|
||||||
return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetExtendedRcode sets the EDNS extended RCODE field.
|
|
||||||
func (rr *OPT) SetExtendedRcode(v uint8) {
|
|
||||||
if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have!
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UDPSize returns the UDP buffer size.
|
|
||||||
func (rr *OPT) UDPSize() uint16 {
|
|
||||||
return rr.Hdr.Class
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUDPSize sets the UDP buffer size.
|
|
||||||
func (rr *OPT) SetUDPSize(size uint16) {
|
|
||||||
rr.Hdr.Class = size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do returns the value of the DO (DNSSEC OK) bit.
|
|
||||||
func (rr *OPT) Do() bool {
|
|
||||||
return rr.Hdr.Ttl&_DO == _DO
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDo sets the DO (DNSSEC OK) bit.
|
|
||||||
// If we pass an argument, set the DO bit to that value.
|
|
||||||
// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored.
|
|
||||||
func (rr *OPT) SetDo(do ...bool) {
|
|
||||||
if len(do) == 1 {
|
|
||||||
if do[0] {
|
|
||||||
rr.Hdr.Ttl |= _DO
|
|
||||||
} else {
|
|
||||||
rr.Hdr.Ttl &^= _DO
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rr.Hdr.Ttl |= _DO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it.
|
|
||||||
type EDNS0 interface {
|
|
||||||
// Option returns the option code for the option.
|
|
||||||
Option() uint16
|
|
||||||
// pack returns the bytes of the option data.
|
|
||||||
pack() ([]byte, error)
|
|
||||||
// unpack sets the data as found in the buffer. Is also sets
|
|
||||||
// the length of the slice as the length of the option data.
|
|
||||||
unpack([]byte) error
|
|
||||||
// String returns the string representation of the option.
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// The nsid EDNS0 option is used to retrieve a nameserver
|
|
||||||
// identifier. When sending a request Nsid must be set to the empty string
|
|
||||||
// The identifier is an opaque string encoded as hex.
|
|
||||||
// Basic use pattern for creating an nsid option:
|
|
||||||
//
|
|
||||||
// o := new(dns.OPT)
|
|
||||||
// o.Hdr.Name = "."
|
|
||||||
// o.Hdr.Rrtype = dns.TypeOPT
|
|
||||||
// e := new(dns.EDNS0_NSID)
|
|
||||||
// e.Code = dns.EDNS0NSID
|
|
||||||
// e.Nsid = "AA"
|
|
||||||
// o.Option = append(o.Option, e)
|
|
||||||
type EDNS0_NSID struct {
|
|
||||||
Code uint16 // Always EDNS0NSID
|
|
||||||
Nsid string // This string needs to be hex encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_NSID) pack() ([]byte, error) {
|
|
||||||
h, err := hex.DecodeString(e.Nsid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID }
|
|
||||||
func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
|
|
||||||
func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
|
|
||||||
|
|
||||||
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
|
|
||||||
// an idea of where the client lives. It can then give back a different
|
|
||||||
// answer depending on the location or network topology.
|
|
||||||
// Basic use pattern for creating an subnet option:
|
|
||||||
//
|
|
||||||
// o := new(dns.OPT)
|
|
||||||
// o.Hdr.Name = "."
|
|
||||||
// o.Hdr.Rrtype = dns.TypeOPT
|
|
||||||
// e := new(dns.EDNS0_SUBNET)
|
|
||||||
// e.Code = dns.EDNS0SUBNET
|
|
||||||
// e.Family = 1 // 1 for IPv4 source address, 2 for IPv6
|
|
||||||
// e.NetMask = 32 // 32 for IPV4, 128 for IPv6
|
|
||||||
// e.SourceScope = 0
|
|
||||||
// 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
|
|
||||||
SourceNetmask uint8
|
|
||||||
SourceScope uint8
|
|
||||||
Address net.IP
|
|
||||||
DraftOption bool // Set to true if using the old (0x50fa) option code
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) Option() uint16 {
|
|
||||||
if e.DraftOption {
|
|
||||||
return EDNS0SUBNETDRAFT
|
|
||||||
}
|
|
||||||
return EDNS0SUBNET
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) pack() ([]byte, error) {
|
|
||||||
b := make([]byte, 4)
|
|
||||||
binary.BigEndian.PutUint16(b[0:], e.Family)
|
|
||||||
b[2] = e.SourceNetmask
|
|
||||||
b[3] = e.SourceScope
|
|
||||||
switch e.Family {
|
|
||||||
case 1:
|
|
||||||
if e.SourceNetmask > net.IPv4len*8 {
|
|
||||||
return nil, errors.New("dns: bad netmask")
|
|
||||||
}
|
|
||||||
if len(e.Address.To4()) != net.IPv4len {
|
|
||||||
return nil, errors.New("dns: bad address")
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
if len(e.Address) != net.IPv6len {
|
|
||||||
return nil, errors.New("dns: bad address")
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) unpack(b []byte) error {
|
|
||||||
if len(b) < 4 {
|
|
||||||
return ErrBuf
|
|
||||||
}
|
|
||||||
e.Family = binary.BigEndian.Uint16(b)
|
|
||||||
e.SourceNetmask = b[2]
|
|
||||||
e.SourceScope = b[3]
|
|
||||||
switch e.Family {
|
|
||||||
case 1:
|
|
||||||
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:
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) String() (s string) {
|
|
||||||
if e.Address == nil {
|
|
||||||
s = "<nil>"
|
|
||||||
} else if e.Address.To4() != nil {
|
|
||||||
s = e.Address.String()
|
|
||||||
} else {
|
|
||||||
s = "[" + e.Address.String() + "]"
|
|
||||||
}
|
|
||||||
s += "/" + strconv.Itoa(int(e.SourceNetmask)) + "/" + strconv.Itoa(int(e.SourceScope))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Cookie EDNS0 option
|
|
||||||
//
|
|
||||||
// o := new(dns.OPT)
|
|
||||||
// o.Hdr.Name = "."
|
|
||||||
// o.Hdr.Rrtype = dns.TypeOPT
|
|
||||||
// e := new(dns.EDNS0_COOKIE)
|
|
||||||
// e.Code = dns.EDNS0COOKIE
|
|
||||||
// e.Cookie = "24a5ac.."
|
|
||||||
// o.Option = append(o.Option, e)
|
|
||||||
//
|
|
||||||
// The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is
|
|
||||||
// always 8 bytes. It may then optionally be followed by the server cookie. The server
|
|
||||||
// cookie is of variable length, 8 to a maximum of 32 bytes. In other words:
|
|
||||||
//
|
|
||||||
// cCookie := o.Cookie[:16]
|
|
||||||
// sCookie := o.Cookie[16:]
|
|
||||||
//
|
|
||||||
// There is no guarantee that the Cookie string has a specific length.
|
|
||||||
type EDNS0_COOKIE struct {
|
|
||||||
Code uint16 // Always EDNS0COOKIE
|
|
||||||
Cookie string // Hex-encoded cookie data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_COOKIE) pack() ([]byte, error) {
|
|
||||||
h, err := hex.DecodeString(e.Cookie)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
|
|
||||||
func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
|
|
||||||
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
|
|
||||||
|
|
||||||
// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
|
|
||||||
// an expiration on an update RR. This is helpful for clients that cannot clean
|
|
||||||
// up after themselves. This is a draft RFC and more information can be found at
|
|
||||||
// http://files.dns-sd.org/draft-sekar-dns-ul.txt
|
|
||||||
//
|
|
||||||
// o := new(dns.OPT)
|
|
||||||
// o.Hdr.Name = "."
|
|
||||||
// o.Hdr.Rrtype = dns.TypeOPT
|
|
||||||
// e := new(dns.EDNS0_UL)
|
|
||||||
// e.Code = dns.EDNS0UL
|
|
||||||
// e.Lease = 120 // in seconds
|
|
||||||
// o.Option = append(o.Option, e)
|
|
||||||
type EDNS0_UL struct {
|
|
||||||
Code uint16 // Always EDNS0UL
|
|
||||||
Lease uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
|
|
||||||
func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) }
|
|
||||||
|
|
||||||
// Copied: http://golang.org/src/pkg/net/dnsmsg.go
|
|
||||||
func (e *EDNS0_UL) pack() ([]byte, error) {
|
|
||||||
b := make([]byte, 4)
|
|
||||||
binary.BigEndian.PutUint32(b, e.Lease)
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_UL) unpack(b []byte) error {
|
|
||||||
if len(b) < 4 {
|
|
||||||
return ErrBuf
|
|
||||||
}
|
|
||||||
e.Lease = binary.BigEndian.Uint32(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
|
||||||
// Implemented for completeness, as the EDNS0 type code is assigned.
|
|
||||||
type EDNS0_LLQ struct {
|
|
||||||
Code uint16 // Always EDNS0LLQ
|
|
||||||
Version uint16
|
|
||||||
Opcode uint16
|
|
||||||
Error uint16
|
|
||||||
Id uint64
|
|
||||||
LeaseLife uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
|
|
||||||
|
|
||||||
func (e *EDNS0_LLQ) pack() ([]byte, error) {
|
|
||||||
b := make([]byte, 18)
|
|
||||||
binary.BigEndian.PutUint16(b[0:], e.Version)
|
|
||||||
binary.BigEndian.PutUint16(b[2:], e.Opcode)
|
|
||||||
binary.BigEndian.PutUint16(b[4:], e.Error)
|
|
||||||
binary.BigEndian.PutUint64(b[6:], e.Id)
|
|
||||||
binary.BigEndian.PutUint32(b[14:], e.LeaseLife)
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_LLQ) unpack(b []byte) error {
|
|
||||||
if len(b) < 18 {
|
|
||||||
return ErrBuf
|
|
||||||
}
|
|
||||||
e.Version = binary.BigEndian.Uint16(b[0:])
|
|
||||||
e.Opcode = binary.BigEndian.Uint16(b[2:])
|
|
||||||
e.Error = binary.BigEndian.Uint16(b[4:])
|
|
||||||
e.Id = binary.BigEndian.Uint64(b[6:])
|
|
||||||
e.LeaseLife = binary.BigEndian.Uint32(b[14:])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_LLQ) String() string {
|
|
||||||
s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) +
|
|
||||||
" " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(uint64(e.Id), 10) +
|
|
||||||
" " + strconv.FormatUint(uint64(e.LeaseLife), 10)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type EDNS0_DAU struct {
|
|
||||||
Code uint16 // Always EDNS0DAU
|
|
||||||
AlgCode []uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU }
|
|
||||||
func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil }
|
|
||||||
func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil }
|
|
||||||
|
|
||||||
func (e *EDNS0_DAU) String() string {
|
|
||||||
s := ""
|
|
||||||
for i := 0; i < len(e.AlgCode); i++ {
|
|
||||||
if a, ok := AlgorithmToString[e.AlgCode[i]]; ok {
|
|
||||||
s += " " + a
|
|
||||||
} else {
|
|
||||||
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type EDNS0_DHU struct {
|
|
||||||
Code uint16 // Always EDNS0DHU
|
|
||||||
AlgCode []uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU }
|
|
||||||
func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil }
|
|
||||||
func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil }
|
|
||||||
|
|
||||||
func (e *EDNS0_DHU) String() string {
|
|
||||||
s := ""
|
|
||||||
for i := 0; i < len(e.AlgCode); i++ {
|
|
||||||
if a, ok := HashToString[e.AlgCode[i]]; ok {
|
|
||||||
s += " " + a
|
|
||||||
} else {
|
|
||||||
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type EDNS0_N3U struct {
|
|
||||||
Code uint16 // Always EDNS0N3U
|
|
||||||
AlgCode []uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U }
|
|
||||||
func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil }
|
|
||||||
func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil }
|
|
||||||
|
|
||||||
func (e *EDNS0_N3U) String() string {
|
|
||||||
// Re-use the hash map
|
|
||||||
s := ""
|
|
||||||
for i := 0; i < len(e.AlgCode); i++ {
|
|
||||||
if a, ok := HashToString[e.AlgCode[i]]; ok {
|
|
||||||
s += " " + a
|
|
||||||
} else {
|
|
||||||
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type EDNS0_EXPIRE struct {
|
|
||||||
Code uint16 // Always EDNS0EXPIRE
|
|
||||||
Expire uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
|
|
||||||
func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
|
|
||||||
|
|
||||||
func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
|
|
||||||
b := make([]byte, 4)
|
|
||||||
b[0] = byte(e.Expire >> 24)
|
|
||||||
b[1] = byte(e.Expire >> 16)
|
|
||||||
b[2] = byte(e.Expire >> 8)
|
|
||||||
b[3] = byte(e.Expire)
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_EXPIRE) unpack(b []byte) error {
|
|
||||||
if len(b) < 4 {
|
|
||||||
return ErrBuf
|
|
||||||
}
|
|
||||||
e.Expire = binary.BigEndian.Uint32(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The EDNS0_LOCAL option is used for local/experimental purposes. The option
|
|
||||||
// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]
|
|
||||||
// (RFC6891), although any unassigned code can actually be used. The content of
|
|
||||||
// the option is made available in Data, unaltered.
|
|
||||||
// Basic use pattern for creating a local option:
|
|
||||||
//
|
|
||||||
// o := new(dns.OPT)
|
|
||||||
// o.Hdr.Name = "."
|
|
||||||
// o.Hdr.Rrtype = dns.TypeOPT
|
|
||||||
// e := new(dns.EDNS0_LOCAL)
|
|
||||||
// e.Code = dns.EDNS0LOCALSTART
|
|
||||||
// e.Data = []byte{72, 82, 74}
|
|
||||||
// o.Option = append(o.Option, e)
|
|
||||||
type EDNS0_LOCAL struct {
|
|
||||||
Code uint16
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_LOCAL) Option() uint16 { return e.Code }
|
|
||||||
func (e *EDNS0_LOCAL) String() string {
|
|
||||||
return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_LOCAL) pack() ([]byte, error) {
|
|
||||||
b := make([]byte, len(e.Data))
|
|
||||||
copied := copy(b, e.Data)
|
|
||||||
if copied != len(e.Data) {
|
|
||||||
return nil, ErrBuf
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_LOCAL) unpack(b []byte) error {
|
|
||||||
e.Data = make([]byte, len(b))
|
|
||||||
copied := copy(e.Data, b)
|
|
||||||
if copied != len(b) {
|
|
||||||
return ErrBuf
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type EDNS0_TCP_KEEPALIVE struct {
|
|
||||||
Code uint16 // Always EDNSTCPKEEPALIVE
|
|
||||||
Length uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present;
|
|
||||||
Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 {
|
|
||||||
return EDNS0TCPKEEPALIVE
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {
|
|
||||||
if e.Timeout != 0 && e.Length != 2 {
|
|
||||||
return nil, errors.New("dns: timeout specified but length is not 2")
|
|
||||||
}
|
|
||||||
if e.Timeout == 0 && e.Length != 0 {
|
|
||||||
return nil, errors.New("dns: timeout not specified but length is not 0")
|
|
||||||
}
|
|
||||||
b := make([]byte, 4+e.Length)
|
|
||||||
binary.BigEndian.PutUint16(b[0:], e.Code)
|
|
||||||
binary.BigEndian.PutUint16(b[2:], e.Length)
|
|
||||||
if e.Length == 2 {
|
|
||||||
binary.BigEndian.PutUint16(b[4:], e.Timeout)
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error {
|
|
||||||
if len(b) < 4 {
|
|
||||||
return ErrBuf
|
|
||||||
}
|
|
||||||
e.Length = binary.BigEndian.Uint16(b[2:4])
|
|
||||||
if e.Length != 0 && e.Length != 2 {
|
|
||||||
return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10))
|
|
||||||
}
|
|
||||||
if e.Length == 2 {
|
|
||||||
if len(b) < 6 {
|
|
||||||
return ErrBuf
|
|
||||||
}
|
|
||||||
e.Timeout = binary.BigEndian.Uint16(b[4:6])
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
|
|
||||||
s = "use tcp keep-alive"
|
|
||||||
if e.Length == 0 {
|
|
||||||
s += ", timeout omitted"
|
|
||||||
} else {
|
|
||||||
s += fmt.Sprintf(", timeout %dms", e.Timeout*100)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
87
vendor/github.com/miekg/dns/format.go
generated
vendored
87
vendor/github.com/miekg/dns/format.go
generated
vendored
|
@ -1,87 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NumField returns the number of rdata fields r has.
|
|
||||||
func NumField(r RR) int {
|
|
||||||
return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header
|
|
||||||
}
|
|
||||||
|
|
||||||
// Field returns the rdata field i as a string. Fields are indexed starting from 1.
|
|
||||||
// RR types that holds slice data, for instance the NSEC type bitmap will return a single
|
|
||||||
// string where the types are concatenated using a space.
|
|
||||||
// Accessing non existing fields will cause a panic.
|
|
||||||
func Field(r RR, i int) string {
|
|
||||||
if i == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
d := reflect.ValueOf(r).Elem().Field(i)
|
|
||||||
switch k := d.Kind(); k {
|
|
||||||
case reflect.String:
|
|
||||||
return d.String()
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return strconv.FormatInt(d.Int(), 10)
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
return strconv.FormatUint(d.Uint(), 10)
|
|
||||||
case reflect.Slice:
|
|
||||||
switch reflect.ValueOf(r).Elem().Type().Field(i).Tag {
|
|
||||||
case `dns:"a"`:
|
|
||||||
// TODO(miek): Hmm store this as 16 bytes
|
|
||||||
if d.Len() < net.IPv6len {
|
|
||||||
return net.IPv4(byte(d.Index(0).Uint()),
|
|
||||||
byte(d.Index(1).Uint()),
|
|
||||||
byte(d.Index(2).Uint()),
|
|
||||||
byte(d.Index(3).Uint())).String()
|
|
||||||
}
|
|
||||||
return net.IPv4(byte(d.Index(12).Uint()),
|
|
||||||
byte(d.Index(13).Uint()),
|
|
||||||
byte(d.Index(14).Uint()),
|
|
||||||
byte(d.Index(15).Uint())).String()
|
|
||||||
case `dns:"aaaa"`:
|
|
||||||
return net.IP{
|
|
||||||
byte(d.Index(0).Uint()),
|
|
||||||
byte(d.Index(1).Uint()),
|
|
||||||
byte(d.Index(2).Uint()),
|
|
||||||
byte(d.Index(3).Uint()),
|
|
||||||
byte(d.Index(4).Uint()),
|
|
||||||
byte(d.Index(5).Uint()),
|
|
||||||
byte(d.Index(6).Uint()),
|
|
||||||
byte(d.Index(7).Uint()),
|
|
||||||
byte(d.Index(8).Uint()),
|
|
||||||
byte(d.Index(9).Uint()),
|
|
||||||
byte(d.Index(10).Uint()),
|
|
||||||
byte(d.Index(11).Uint()),
|
|
||||||
byte(d.Index(12).Uint()),
|
|
||||||
byte(d.Index(13).Uint()),
|
|
||||||
byte(d.Index(14).Uint()),
|
|
||||||
byte(d.Index(15).Uint()),
|
|
||||||
}.String()
|
|
||||||
case `dns:"nsec"`:
|
|
||||||
if d.Len() == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
s := Type(d.Index(0).Uint()).String()
|
|
||||||
for i := 1; i < d.Len(); i++ {
|
|
||||||
s += " " + Type(d.Index(i).Uint()).String()
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
default:
|
|
||||||
// if it does not have a tag its a string slice
|
|
||||||
fallthrough
|
|
||||||
case `dns:"txt"`:
|
|
||||||
if d.Len() == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
s := d.Index(0).String()
|
|
||||||
for i := 1; i < d.Len(); i++ {
|
|
||||||
s += " " + d.Index(i).String()
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
159
vendor/github.com/miekg/dns/generate.go
generated
vendored
159
vendor/github.com/miekg/dns/generate.go
generated
vendored
|
@ -1,159 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Parse the $GENERATE statement as used in BIND9 zones.
|
|
||||||
// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
|
|
||||||
// We are called after '$GENERATE '. After which we expect:
|
|
||||||
// * the range (12-24/2)
|
|
||||||
// * lhs (ownername)
|
|
||||||
// * [[ttl][class]]
|
|
||||||
// * type
|
|
||||||
// * rhs (rdata)
|
|
||||||
// But we are lazy here, only the range is parsed *all* occurrences
|
|
||||||
// of $ after that are interpreted.
|
|
||||||
// Any error are returned as a string value, the empty string signals
|
|
||||||
// "no error".
|
|
||||||
func generate(l lex, c chan lex, t chan *Token, o string) string {
|
|
||||||
step := 1
|
|
||||||
if i := strings.IndexAny(l.token, "/"); i != -1 {
|
|
||||||
if i+1 == len(l.token) {
|
|
||||||
return "bad step in $GENERATE range"
|
|
||||||
}
|
|
||||||
if s, err := strconv.Atoi(l.token[i+1:]); err == nil {
|
|
||||||
if s < 0 {
|
|
||||||
return "bad step in $GENERATE range"
|
|
||||||
}
|
|
||||||
step = s
|
|
||||||
} else {
|
|
||||||
return "bad step in $GENERATE range"
|
|
||||||
}
|
|
||||||
l.token = l.token[:i]
|
|
||||||
}
|
|
||||||
sx := strings.SplitN(l.token, "-", 2)
|
|
||||||
if len(sx) != 2 {
|
|
||||||
return "bad start-stop in $GENERATE range"
|
|
||||||
}
|
|
||||||
start, err := strconv.Atoi(sx[0])
|
|
||||||
if err != nil {
|
|
||||||
return "bad start in $GENERATE range"
|
|
||||||
}
|
|
||||||
end, err := strconv.Atoi(sx[1])
|
|
||||||
if err != nil {
|
|
||||||
return "bad stop in $GENERATE range"
|
|
||||||
}
|
|
||||||
if end < 0 || start < 0 || end < start {
|
|
||||||
return "bad range in $GENERATE range"
|
|
||||||
}
|
|
||||||
|
|
||||||
<-c // _BLANK
|
|
||||||
// Create a complete new string, which we then parse again.
|
|
||||||
s := ""
|
|
||||||
BuildRR:
|
|
||||||
l = <-c
|
|
||||||
if l.value != zNewline && l.value != zEOF {
|
|
||||||
s += l.token
|
|
||||||
goto BuildRR
|
|
||||||
}
|
|
||||||
for i := start; i <= end; i += step {
|
|
||||||
var (
|
|
||||||
escape bool
|
|
||||||
dom bytes.Buffer
|
|
||||||
mod string
|
|
||||||
err error
|
|
||||||
offset int
|
|
||||||
)
|
|
||||||
|
|
||||||
for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
|
|
||||||
switch s[j] {
|
|
||||||
case '\\':
|
|
||||||
if escape {
|
|
||||||
dom.WriteByte('\\')
|
|
||||||
escape = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
escape = true
|
|
||||||
case '$':
|
|
||||||
mod = "%d"
|
|
||||||
offset = 0
|
|
||||||
if escape {
|
|
||||||
dom.WriteByte('$')
|
|
||||||
escape = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
escape = false
|
|
||||||
if j+1 >= len(s) { // End of the string
|
|
||||||
dom.WriteString(fmt.Sprintf(mod, i+offset))
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
if s[j+1] == '$' {
|
|
||||||
dom.WriteByte('$')
|
|
||||||
j++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Search for { and }
|
|
||||||
if s[j+1] == '{' { // Modifier block
|
|
||||||
sep := strings.Index(s[j+2:], "}")
|
|
||||||
if sep == -1 {
|
|
||||||
return "bad modifier in $GENERATE"
|
|
||||||
}
|
|
||||||
mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
j += 2 + sep // Jump to it
|
|
||||||
}
|
|
||||||
dom.WriteString(fmt.Sprintf(mod, i+offset))
|
|
||||||
default:
|
|
||||||
if escape { // Pretty useless here
|
|
||||||
escape = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dom.WriteByte(s[j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Re-parse the RR and send it on the current channel t
|
|
||||||
rx, err := NewRR("$ORIGIN " + o + "\n" + dom.String())
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
t <- &Token{RR: rx}
|
|
||||||
// Its more efficient to first built the rrlist and then parse it in
|
|
||||||
// one go! But is this a problem?
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
|
|
||||||
func modToPrintf(s string) (string, int, error) {
|
|
||||||
xs := strings.SplitN(s, ",", 3)
|
|
||||||
if len(xs) != 3 {
|
|
||||||
return "", 0, errors.New("bad modifier in $GENERATE")
|
|
||||||
}
|
|
||||||
// xs[0] is offset, xs[1] is width, xs[2] is base
|
|
||||||
if xs[2] != "o" && xs[2] != "d" && xs[2] != "x" && xs[2] != "X" {
|
|
||||||
return "", 0, errors.New("bad base in $GENERATE")
|
|
||||||
}
|
|
||||||
offset, err := strconv.Atoi(xs[0])
|
|
||||||
if err != nil || offset > 255 {
|
|
||||||
return "", 0, errors.New("bad offset in $GENERATE")
|
|
||||||
}
|
|
||||||
width, err := strconv.Atoi(xs[1])
|
|
||||||
if err != nil || width > 255 {
|
|
||||||
return "", offset, errors.New("bad width in $GENERATE")
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case width < 0:
|
|
||||||
return "", offset, errors.New("bad width in $GENERATE")
|
|
||||||
case width == 0:
|
|
||||||
return "%" + xs[1] + xs[2], offset, nil
|
|
||||||
}
|
|
||||||
return "%0" + xs[1] + xs[2], offset, nil
|
|
||||||
}
|
|
168
vendor/github.com/miekg/dns/labels.go
generated
vendored
168
vendor/github.com/miekg/dns/labels.go
generated
vendored
|
@ -1,168 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
// Holds a bunch of helper functions for dealing with labels.
|
|
||||||
|
|
||||||
// SplitDomainName splits a name string into it's labels.
|
|
||||||
// www.miek.nl. returns []string{"www", "miek", "nl"}
|
|
||||||
// .www.miek.nl. returns []string{"", "www", "miek", "nl"},
|
|
||||||
// The root label (.) returns nil. Note that using
|
|
||||||
// strings.Split(s) will work in most cases, but does not handle
|
|
||||||
// escaped dots (\.) for instance.
|
|
||||||
// s must be a syntactically valid domain name, see IsDomainName.
|
|
||||||
func SplitDomainName(s string) (labels []string) {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
fqdnEnd := 0 // offset of the final '.' or the length of the name
|
|
||||||
idx := Split(s)
|
|
||||||
begin := 0
|
|
||||||
if s[len(s)-1] == '.' {
|
|
||||||
fqdnEnd = len(s) - 1
|
|
||||||
} else {
|
|
||||||
fqdnEnd = len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch len(idx) {
|
|
||||||
case 0:
|
|
||||||
return nil
|
|
||||||
case 1:
|
|
||||||
// no-op
|
|
||||||
default:
|
|
||||||
end := 0
|
|
||||||
for i := 1; i < len(idx); i++ {
|
|
||||||
end = idx[i]
|
|
||||||
labels = append(labels, s[begin:end-1])
|
|
||||||
begin = end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
labels = append(labels, s[begin:fqdnEnd])
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompareDomainName compares the names s1 and s2 and
|
|
||||||
// returns how many labels they have in common starting from the *right*.
|
|
||||||
// The comparison stops at the first inequality. The names are not downcased
|
|
||||||
// before the comparison.
|
|
||||||
//
|
|
||||||
// www.miek.nl. and miek.nl. have two labels in common: miek and nl
|
|
||||||
// www.miek.nl. and www.bla.nl. have one label in common: nl
|
|
||||||
//
|
|
||||||
// s1 and s2 must be syntactically valid domain names.
|
|
||||||
func CompareDomainName(s1, s2 string) (n int) {
|
|
||||||
s1 = Fqdn(s1)
|
|
||||||
s2 = Fqdn(s2)
|
|
||||||
l1 := Split(s1)
|
|
||||||
l2 := Split(s2)
|
|
||||||
|
|
||||||
// the first check: root label
|
|
||||||
if l1 == nil || l2 == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
j1 := len(l1) - 1 // end
|
|
||||||
i1 := len(l1) - 2 // start
|
|
||||||
j2 := len(l2) - 1
|
|
||||||
i2 := len(l2) - 2
|
|
||||||
// the second check can be done here: last/only label
|
|
||||||
// before we fall through into the for-loop below
|
|
||||||
if s1[l1[j1]:] == s2[l2[j2]:] {
|
|
||||||
n++
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
if i1 < 0 || i2 < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if s1[l1[i1]:l1[j1]] == s2[l2[i2]:l2[j2]] {
|
|
||||||
n++
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
j1--
|
|
||||||
i1--
|
|
||||||
j2--
|
|
||||||
i2--
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountLabel counts the the number of labels in the string s.
|
|
||||||
// s must be a syntactically valid domain name.
|
|
||||||
func CountLabel(s string) (labels int) {
|
|
||||||
if s == "." {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
off := 0
|
|
||||||
end := false
|
|
||||||
for {
|
|
||||||
off, end = NextLabel(s, off)
|
|
||||||
labels++
|
|
||||||
if end {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split splits a name s into its label indexes.
|
|
||||||
// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
|
|
||||||
// The root name (.) returns nil. Also see SplitDomainName.
|
|
||||||
// s must be a syntactically valid domain name.
|
|
||||||
func Split(s string) []int {
|
|
||||||
if s == "." {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
idx := make([]int, 1, 3)
|
|
||||||
off := 0
|
|
||||||
end := false
|
|
||||||
|
|
||||||
for {
|
|
||||||
off, end = NextLabel(s, off)
|
|
||||||
if end {
|
|
||||||
return idx
|
|
||||||
}
|
|
||||||
idx = append(idx, off)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextLabel returns the index of the start of the next label in the
|
|
||||||
// string s starting at offset.
|
|
||||||
// The bool end is true when the end of the string has been reached.
|
|
||||||
// Also see PrevLabel.
|
|
||||||
func NextLabel(s string, offset int) (i int, end bool) {
|
|
||||||
quote := false
|
|
||||||
for i = offset; i < len(s)-1; i++ {
|
|
||||||
switch s[i] {
|
|
||||||
case '\\':
|
|
||||||
quote = !quote
|
|
||||||
default:
|
|
||||||
quote = false
|
|
||||||
case '.':
|
|
||||||
if quote {
|
|
||||||
quote = !quote
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return i + 1, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i + 1, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevLabel returns the index of the label when starting from the right and
|
|
||||||
// jumping n labels to the left.
|
|
||||||
// The bool start is true when the start of the string has been overshot.
|
|
||||||
// Also see NextLabel.
|
|
||||||
func PrevLabel(s string, n int) (i int, start bool) {
|
|
||||||
if n == 0 {
|
|
||||||
return len(s), false
|
|
||||||
}
|
|
||||||
lab := Split(s)
|
|
||||||
if lab == nil {
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
if n > len(lab) {
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
return lab[len(lab)-n], false
|
|
||||||
}
|
|
1231
vendor/github.com/miekg/dns/msg.go
generated
vendored
1231
vendor/github.com/miekg/dns/msg.go
generated
vendored
File diff suppressed because it is too large
Load diff
630
vendor/github.com/miekg/dns/msg_helpers.go
generated
vendored
630
vendor/github.com/miekg/dns/msg_helpers.go
generated
vendored
|
@ -1,630 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base32"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// helper functions called from the generated zmsg.go
|
|
||||||
|
|
||||||
// These function are named after the tag to help pack/unpack, if there is no tag it is the name
|
|
||||||
// of the type they pack/unpack (string, int, etc). We prefix all with unpackData or packData, so packDataA or
|
|
||||||
// packDataDomainName.
|
|
||||||
|
|
||||||
func unpackDataA(msg []byte, off int) (net.IP, int, error) {
|
|
||||||
if off+net.IPv4len > len(msg) {
|
|
||||||
return nil, len(msg), &Error{err: "overflow unpacking a"}
|
|
||||||
}
|
|
||||||
a := append(make(net.IP, 0, net.IPv4len), msg[off:off+net.IPv4len]...)
|
|
||||||
off += net.IPv4len
|
|
||||||
return a, off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packDataA(a net.IP, msg []byte, off int) (int, error) {
|
|
||||||
// It must be a slice of 4, even if it is 16, we encode only the first 4
|
|
||||||
if off+net.IPv4len > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing a"}
|
|
||||||
}
|
|
||||||
switch len(a) {
|
|
||||||
case net.IPv4len, net.IPv6len:
|
|
||||||
copy(msg[off:], a.To4())
|
|
||||||
off += net.IPv4len
|
|
||||||
case 0:
|
|
||||||
// Allowed, for dynamic updates.
|
|
||||||
default:
|
|
||||||
return len(msg), &Error{err: "overflow packing a"}
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) {
|
|
||||||
if off+net.IPv6len > len(msg) {
|
|
||||||
return nil, len(msg), &Error{err: "overflow unpacking aaaa"}
|
|
||||||
}
|
|
||||||
aaaa := append(make(net.IP, 0, net.IPv6len), msg[off:off+net.IPv6len]...)
|
|
||||||
off += net.IPv6len
|
|
||||||
return aaaa, off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) {
|
|
||||||
if off+net.IPv6len > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing aaaa"}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch len(aaaa) {
|
|
||||||
case net.IPv6len:
|
|
||||||
copy(msg[off:], aaaa)
|
|
||||||
off += net.IPv6len
|
|
||||||
case 0:
|
|
||||||
// Allowed, dynamic updates.
|
|
||||||
default:
|
|
||||||
return len(msg), &Error{err: "overflow packing aaaa"}
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpackHeader unpacks an RR header, returning the offset to the end of the header and a
|
|
||||||
// re-sliced msg according to the expected length of the RR.
|
|
||||||
func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, err error) {
|
|
||||||
hdr := RR_Header{}
|
|
||||||
if off == len(msg) {
|
|
||||||
return hdr, off, msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
hdr.Name, off, err = UnpackDomainName(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return hdr, len(msg), msg, err
|
|
||||||
}
|
|
||||||
hdr.Rrtype, off, err = unpackUint16(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return hdr, len(msg), msg, err
|
|
||||||
}
|
|
||||||
hdr.Class, off, err = unpackUint16(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return hdr, len(msg), msg, err
|
|
||||||
}
|
|
||||||
hdr.Ttl, off, err = unpackUint32(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return hdr, len(msg), msg, err
|
|
||||||
}
|
|
||||||
hdr.Rdlength, off, err = unpackUint16(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return hdr, len(msg), msg, err
|
|
||||||
}
|
|
||||||
msg, err = truncateMsgFromRdlength(msg, off, hdr.Rdlength)
|
|
||||||
return hdr, off, msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// pack packs an RR header, returning the offset to the end of the header.
|
|
||||||
// See PackDomainName for documentation about the compression.
|
|
||||||
func (hdr RR_Header) pack(msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
|
|
||||||
if off == len(msg) {
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
off, err = PackDomainName(hdr.Name, msg, off, compression, compress)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
off, err = packUint16(hdr.Rrtype, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
off, err = packUint16(hdr.Class, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
off, err = packUint32(hdr.Ttl, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
off, err = packUint16(hdr.Rdlength, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper helper functions.
|
|
||||||
|
|
||||||
// truncateMsgFromRdLength truncates msg to match the expected length of the RR.
|
|
||||||
// Returns an error if msg is smaller than the expected size.
|
|
||||||
func truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []byte, err error) {
|
|
||||||
lenrd := off + int(rdlength)
|
|
||||||
if lenrd > len(msg) {
|
|
||||||
return msg, &Error{err: "overflowing header size"}
|
|
||||||
}
|
|
||||||
return msg[:lenrd], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromBase32(s []byte) (buf []byte, err error) {
|
|
||||||
buflen := base32.HexEncoding.DecodedLen(len(s))
|
|
||||||
buf = make([]byte, buflen)
|
|
||||||
n, err := base32.HexEncoding.Decode(buf, s)
|
|
||||||
buf = buf[:n]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func toBase32(b []byte) string { return base32.HexEncoding.EncodeToString(b) }
|
|
||||||
|
|
||||||
func fromBase64(s []byte) (buf []byte, err error) {
|
|
||||||
buflen := base64.StdEncoding.DecodedLen(len(s))
|
|
||||||
buf = make([]byte, buflen)
|
|
||||||
n, err := base64.StdEncoding.Decode(buf, s)
|
|
||||||
buf = buf[:n]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func toBase64(b []byte) string { return base64.StdEncoding.EncodeToString(b) }
|
|
||||||
|
|
||||||
// dynamicUpdate returns true if the Rdlength is zero.
|
|
||||||
func noRdata(h RR_Header) bool { return h.Rdlength == 0 }
|
|
||||||
|
|
||||||
func unpackUint8(msg []byte, off int) (i uint8, off1 int, err error) {
|
|
||||||
if off+1 > len(msg) {
|
|
||||||
return 0, len(msg), &Error{err: "overflow unpacking uint8"}
|
|
||||||
}
|
|
||||||
return uint8(msg[off]), off + 1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packUint8(i uint8, msg []byte, off int) (off1 int, err error) {
|
|
||||||
if off+1 > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing uint8"}
|
|
||||||
}
|
|
||||||
msg[off] = byte(i)
|
|
||||||
return off + 1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackUint16(msg []byte, off int) (i uint16, off1 int, err error) {
|
|
||||||
if off+2 > len(msg) {
|
|
||||||
return 0, len(msg), &Error{err: "overflow unpacking uint16"}
|
|
||||||
}
|
|
||||||
return binary.BigEndian.Uint16(msg[off:]), off + 2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packUint16(i uint16, msg []byte, off int) (off1 int, err error) {
|
|
||||||
if off+2 > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing uint16"}
|
|
||||||
}
|
|
||||||
binary.BigEndian.PutUint16(msg[off:], i)
|
|
||||||
return off + 2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackUint32(msg []byte, off int) (i uint32, off1 int, err error) {
|
|
||||||
if off+4 > len(msg) {
|
|
||||||
return 0, len(msg), &Error{err: "overflow unpacking uint32"}
|
|
||||||
}
|
|
||||||
return binary.BigEndian.Uint32(msg[off:]), off + 4, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packUint32(i uint32, msg []byte, off int) (off1 int, err error) {
|
|
||||||
if off+4 > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing uint32"}
|
|
||||||
}
|
|
||||||
binary.BigEndian.PutUint32(msg[off:], i)
|
|
||||||
return off + 4, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) {
|
|
||||||
if off+6 > len(msg) {
|
|
||||||
return 0, len(msg), &Error{err: "overflow unpacking uint64 as uint48"}
|
|
||||||
}
|
|
||||||
// Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes)
|
|
||||||
i = (uint64(uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 |
|
|
||||||
uint64(msg[off+4])<<8 | uint64(msg[off+5])))
|
|
||||||
off += 6
|
|
||||||
return i, off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packUint48(i uint64, msg []byte, off int) (off1 int, err error) {
|
|
||||||
if off+6 > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing uint64 as uint48"}
|
|
||||||
}
|
|
||||||
msg[off] = byte(i >> 40)
|
|
||||||
msg[off+1] = byte(i >> 32)
|
|
||||||
msg[off+2] = byte(i >> 24)
|
|
||||||
msg[off+3] = byte(i >> 16)
|
|
||||||
msg[off+4] = byte(i >> 8)
|
|
||||||
msg[off+5] = byte(i)
|
|
||||||
off += 6
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackUint64(msg []byte, off int) (i uint64, off1 int, err error) {
|
|
||||||
if off+8 > len(msg) {
|
|
||||||
return 0, len(msg), &Error{err: "overflow unpacking uint64"}
|
|
||||||
}
|
|
||||||
return binary.BigEndian.Uint64(msg[off:]), off + 8, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packUint64(i uint64, msg []byte, off int) (off1 int, err error) {
|
|
||||||
if off+8 > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing uint64"}
|
|
||||||
}
|
|
||||||
binary.BigEndian.PutUint64(msg[off:], i)
|
|
||||||
off += 8
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackString(msg []byte, off int) (string, int, error) {
|
|
||||||
if off+1 > len(msg) {
|
|
||||||
return "", off, &Error{err: "overflow unpacking txt"}
|
|
||||||
}
|
|
||||||
l := int(msg[off])
|
|
||||||
if off+l+1 > len(msg) {
|
|
||||||
return "", off, &Error{err: "overflow unpacking txt"}
|
|
||||||
}
|
|
||||||
s := make([]byte, 0, l)
|
|
||||||
for _, b := range msg[off+1 : off+1+l] {
|
|
||||||
switch b {
|
|
||||||
case '"', '\\':
|
|
||||||
s = append(s, '\\', b)
|
|
||||||
case '\t', '\r', '\n':
|
|
||||||
s = append(s, b)
|
|
||||||
default:
|
|
||||||
if b < 32 || b > 127 { // unprintable
|
|
||||||
var buf [3]byte
|
|
||||||
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
|
|
||||||
s = append(s, '\\')
|
|
||||||
for i := 0; i < 3-len(bufs); i++ {
|
|
||||||
s = append(s, '0')
|
|
||||||
}
|
|
||||||
for _, r := range bufs {
|
|
||||||
s = append(s, r)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s = append(s, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
off += 1 + l
|
|
||||||
return string(s), off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packString(s string, msg []byte, off int) (int, error) {
|
|
||||||
txtTmp := make([]byte, 256*4+1)
|
|
||||||
off, err := packTxtString(s, msg, off, txtTmp)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackStringBase32(msg []byte, off, end int) (string, int, error) {
|
|
||||||
if end > len(msg) {
|
|
||||||
return "", len(msg), &Error{err: "overflow unpacking base32"}
|
|
||||||
}
|
|
||||||
s := toBase32(msg[off:end])
|
|
||||||
return s, end, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packStringBase32(s string, msg []byte, off int) (int, error) {
|
|
||||||
b32, err := fromBase32([]byte(s))
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
if off+len(b32) > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing base32"}
|
|
||||||
}
|
|
||||||
copy(msg[off:off+len(b32)], b32)
|
|
||||||
off += len(b32)
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackStringBase64(msg []byte, off, end int) (string, int, error) {
|
|
||||||
// Rest of the RR is base64 encoded value, so we don't need an explicit length
|
|
||||||
// to be set. Thus far all RR's that have base64 encoded fields have those as their
|
|
||||||
// last one. What we do need is the end of the RR!
|
|
||||||
if end > len(msg) {
|
|
||||||
return "", len(msg), &Error{err: "overflow unpacking base64"}
|
|
||||||
}
|
|
||||||
s := toBase64(msg[off:end])
|
|
||||||
return s, end, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packStringBase64(s string, msg []byte, off int) (int, error) {
|
|
||||||
b64, err := fromBase64([]byte(s))
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
if off+len(b64) > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing base64"}
|
|
||||||
}
|
|
||||||
copy(msg[off:off+len(b64)], b64)
|
|
||||||
off += len(b64)
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackStringHex(msg []byte, off, end int) (string, int, error) {
|
|
||||||
// Rest of the RR is hex encoded value, so we don't need an explicit length
|
|
||||||
// to be set. NSEC and TSIG have hex fields with a length field.
|
|
||||||
// What we do need is the end of the RR!
|
|
||||||
if end > len(msg) {
|
|
||||||
return "", len(msg), &Error{err: "overflow unpacking hex"}
|
|
||||||
}
|
|
||||||
|
|
||||||
s := hex.EncodeToString(msg[off:end])
|
|
||||||
return s, end, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packStringHex(s string, msg []byte, off int) (int, error) {
|
|
||||||
h, err := hex.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
if off+(len(h)) > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing hex"}
|
|
||||||
}
|
|
||||||
copy(msg[off:off+len(h)], h)
|
|
||||||
off += len(h)
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackStringTxt(msg []byte, off int) ([]string, int, error) {
|
|
||||||
txt, off, err := unpackTxt(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
return txt, off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packStringTxt(s []string, msg []byte, off int) (int, error) {
|
|
||||||
txtTmp := make([]byte, 256*4+1) // If the whole string consists out of \DDD we need this many.
|
|
||||||
off, err := packTxt(s, msg, off, txtTmp)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) {
|
|
||||||
var edns []EDNS0
|
|
||||||
Option:
|
|
||||||
code := uint16(0)
|
|
||||||
if off+4 > len(msg) {
|
|
||||||
return nil, len(msg), &Error{err: "overflow unpacking opt"}
|
|
||||||
}
|
|
||||||
code = binary.BigEndian.Uint16(msg[off:])
|
|
||||||
off += 2
|
|
||||||
optlen := binary.BigEndian.Uint16(msg[off:])
|
|
||||||
off += 2
|
|
||||||
if off+int(optlen) > len(msg) {
|
|
||||||
return nil, len(msg), &Error{err: "overflow unpacking opt"}
|
|
||||||
}
|
|
||||||
switch code {
|
|
||||||
case EDNS0NSID:
|
|
||||||
e := new(EDNS0_NSID)
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
case EDNS0SUBNET, EDNS0SUBNETDRAFT:
|
|
||||||
e := new(EDNS0_SUBNET)
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
if code == EDNS0SUBNETDRAFT {
|
|
||||||
e.DraftOption = true
|
|
||||||
}
|
|
||||||
case EDNS0COOKIE:
|
|
||||||
e := new(EDNS0_COOKIE)
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
case EDNS0UL:
|
|
||||||
e := new(EDNS0_UL)
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
case EDNS0LLQ:
|
|
||||||
e := new(EDNS0_LLQ)
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
case EDNS0DAU:
|
|
||||||
e := new(EDNS0_DAU)
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
case EDNS0DHU:
|
|
||||||
e := new(EDNS0_DHU)
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
case EDNS0N3U:
|
|
||||||
e := new(EDNS0_N3U)
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
default:
|
|
||||||
e := new(EDNS0_LOCAL)
|
|
||||||
e.Code = code
|
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
|
||||||
return nil, len(msg), err
|
|
||||||
}
|
|
||||||
edns = append(edns, e)
|
|
||||||
off += int(optlen)
|
|
||||||
}
|
|
||||||
|
|
||||||
if off < len(msg) {
|
|
||||||
goto Option
|
|
||||||
}
|
|
||||||
|
|
||||||
return edns, off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {
|
|
||||||
for _, el := range options {
|
|
||||||
b, err := el.pack()
|
|
||||||
if err != nil || off+3 > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing opt"}
|
|
||||||
}
|
|
||||||
binary.BigEndian.PutUint16(msg[off:], el.Option()) // Option code
|
|
||||||
binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length
|
|
||||||
off += 4
|
|
||||||
if off+len(b) > len(msg) {
|
|
||||||
copy(msg[off:], b)
|
|
||||||
off = len(msg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Actual data
|
|
||||||
copy(msg[off:off+len(b)], b)
|
|
||||||
off += len(b)
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackStringOctet(msg []byte, off int) (string, int, error) {
|
|
||||||
s := string(msg[off:])
|
|
||||||
return s, len(msg), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packStringOctet(s string, msg []byte, off int) (int, error) {
|
|
||||||
txtTmp := make([]byte, 256*4+1)
|
|
||||||
off, err := packOctetString(s, msg, off, txtTmp)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
|
|
||||||
var nsec []uint16
|
|
||||||
length, window, lastwindow := 0, 0, -1
|
|
||||||
for off < len(msg) {
|
|
||||||
if off+2 > len(msg) {
|
|
||||||
return nsec, len(msg), &Error{err: "overflow unpacking nsecx"}
|
|
||||||
}
|
|
||||||
window = int(msg[off])
|
|
||||||
length = int(msg[off+1])
|
|
||||||
off += 2
|
|
||||||
if window <= lastwindow {
|
|
||||||
// RFC 4034: Blocks are present in the NSEC RR RDATA in
|
|
||||||
// increasing numerical order.
|
|
||||||
return nsec, len(msg), &Error{err: "out of order NSEC block"}
|
|
||||||
}
|
|
||||||
if length == 0 {
|
|
||||||
// RFC 4034: Blocks with no types present MUST NOT be included.
|
|
||||||
return nsec, len(msg), &Error{err: "empty NSEC block"}
|
|
||||||
}
|
|
||||||
if length > 32 {
|
|
||||||
return nsec, len(msg), &Error{err: "NSEC block too long"}
|
|
||||||
}
|
|
||||||
if off+length > len(msg) {
|
|
||||||
return nsec, len(msg), &Error{err: "overflowing NSEC block"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk the bytes in the window and extract the type bits
|
|
||||||
for j := 0; j < length; j++ {
|
|
||||||
b := msg[off+j]
|
|
||||||
// Check the bits one by one, and set the type
|
|
||||||
if b&0x80 == 0x80 {
|
|
||||||
nsec = append(nsec, uint16(window*256+j*8+0))
|
|
||||||
}
|
|
||||||
if b&0x40 == 0x40 {
|
|
||||||
nsec = append(nsec, uint16(window*256+j*8+1))
|
|
||||||
}
|
|
||||||
if b&0x20 == 0x20 {
|
|
||||||
nsec = append(nsec, uint16(window*256+j*8+2))
|
|
||||||
}
|
|
||||||
if b&0x10 == 0x10 {
|
|
||||||
nsec = append(nsec, uint16(window*256+j*8+3))
|
|
||||||
}
|
|
||||||
if b&0x8 == 0x8 {
|
|
||||||
nsec = append(nsec, uint16(window*256+j*8+4))
|
|
||||||
}
|
|
||||||
if b&0x4 == 0x4 {
|
|
||||||
nsec = append(nsec, uint16(window*256+j*8+5))
|
|
||||||
}
|
|
||||||
if b&0x2 == 0x2 {
|
|
||||||
nsec = append(nsec, uint16(window*256+j*8+6))
|
|
||||||
}
|
|
||||||
if b&0x1 == 0x1 {
|
|
||||||
nsec = append(nsec, uint16(window*256+j*8+7))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
off += length
|
|
||||||
lastwindow = window
|
|
||||||
}
|
|
||||||
return nsec, off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
|
|
||||||
if len(bitmap) == 0 {
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
var lastwindow, lastlength uint16
|
|
||||||
for j := 0; j < len(bitmap); j++ {
|
|
||||||
t := bitmap[j]
|
|
||||||
window := t / 256
|
|
||||||
length := (t-window*256)/8 + 1
|
|
||||||
if window > lastwindow && lastlength != 0 { // New window, jump to the new offset
|
|
||||||
off += int(lastlength) + 2
|
|
||||||
lastlength = 0
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
// Setting the bit value for the type in the right octet
|
|
||||||
msg[off+1+int(length)] |= byte(1 << (7 - (t % 8)))
|
|
||||||
lastwindow, lastlength = window, length
|
|
||||||
}
|
|
||||||
off += int(lastlength) + 2
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) {
|
|
||||||
var (
|
|
||||||
servers []string
|
|
||||||
s string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if end > len(msg) {
|
|
||||||
return nil, len(msg), &Error{err: "overflow unpacking domain names"}
|
|
||||||
}
|
|
||||||
for off < end {
|
|
||||||
s, off, err = UnpackDomainName(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return servers, len(msg), err
|
|
||||||
}
|
|
||||||
servers = append(servers, s)
|
|
||||||
}
|
|
||||||
return servers, off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packDataDomainNames(names []string, msg []byte, off int, compression map[string]int, compress bool) (int, error) {
|
|
||||||
var err error
|
|
||||||
for j := 0; j < len(names); j++ {
|
|
||||||
off, err = PackDomainName(names[j], msg, off, compression, false && compress)
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
119
vendor/github.com/miekg/dns/nsecx.go
generated
vendored
119
vendor/github.com/miekg/dns/nsecx.go
generated
vendored
|
@ -1,119 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha1"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type saltWireFmt struct {
|
|
||||||
Salt string `dns:"size-hex"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase.
|
|
||||||
func HashName(label string, ha uint8, iter uint16, salt string) string {
|
|
||||||
saltwire := new(saltWireFmt)
|
|
||||||
saltwire.Salt = salt
|
|
||||||
wire := make([]byte, DefaultMsgSize)
|
|
||||||
n, err := packSaltWire(saltwire, wire)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
wire = wire[:n]
|
|
||||||
name := make([]byte, 255)
|
|
||||||
off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
name = name[:off]
|
|
||||||
var s hash.Hash
|
|
||||||
switch ha {
|
|
||||||
case SHA1:
|
|
||||||
s = sha1.New()
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// k = 0
|
|
||||||
name = append(name, wire...)
|
|
||||||
io.WriteString(s, string(name))
|
|
||||||
nsec3 := s.Sum(nil)
|
|
||||||
// k > 0
|
|
||||||
for k := uint16(0); k < iter; k++ {
|
|
||||||
s.Reset()
|
|
||||||
nsec3 = append(nsec3, wire...)
|
|
||||||
io.WriteString(s, string(nsec3))
|
|
||||||
nsec3 = s.Sum(nil)
|
|
||||||
}
|
|
||||||
return toBase32(nsec3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Denialer is an interface that should be implemented by types that are used to denial
|
|
||||||
// answers in DNSSEC.
|
|
||||||
type Denialer interface {
|
|
||||||
// Cover will check if the (unhashed) name is being covered by this NSEC or NSEC3.
|
|
||||||
Cover(name string) bool
|
|
||||||
// Match will check if the ownername matches the (unhashed) name for this NSEC3 or NSEC3.
|
|
||||||
Match(name string) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cover implements the Denialer interface.
|
|
||||||
func (rr *NSEC) Cover(name string) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match implements the Denialer interface.
|
|
||||||
func (rr *NSEC) Match(name string) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cover implements the Denialer interface.
|
|
||||||
func (rr *NSEC3) Cover(name string) bool {
|
|
||||||
// FIXME(miek): check if the zones match
|
|
||||||
// FIXME(miek): check if we're not dealing with parent nsec3
|
|
||||||
hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
|
|
||||||
labels := Split(rr.Hdr.Name)
|
|
||||||
if len(labels) < 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the dot
|
|
||||||
if hash == rr.NextDomain {
|
|
||||||
return false // empty interval
|
|
||||||
}
|
|
||||||
if hash > rr.NextDomain { // last name, points to apex
|
|
||||||
// hname > hash
|
|
||||||
// hname > rr.NextDomain
|
|
||||||
// TODO(miek)
|
|
||||||
}
|
|
||||||
if hname <= hash {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if hname >= rr.NextDomain {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match implements the Denialer interface.
|
|
||||||
func (rr *NSEC3) Match(name string) bool {
|
|
||||||
// FIXME(miek): Check if we are in the same zone
|
|
||||||
hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
|
|
||||||
labels := Split(rr.Hdr.Name)
|
|
||||||
if len(labels) < 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the .
|
|
||||||
if hash == hname {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func packSaltWire(sw *saltWireFmt, msg []byte) (int, error) {
|
|
||||||
off, err := packStringHex(sw.Salt, msg, 0)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
149
vendor/github.com/miekg/dns/privaterr.go
generated
vendored
149
vendor/github.com/miekg/dns/privaterr.go
generated
vendored
|
@ -1,149 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PrivateRdata is an interface used for implementing "Private Use" RR types, see
|
|
||||||
// RFC 6895. This allows one to experiment with new RR types, without requesting an
|
|
||||||
// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
|
|
||||||
type PrivateRdata interface {
|
|
||||||
// String returns the text presentaton of the Rdata of the Private RR.
|
|
||||||
String() string
|
|
||||||
// Parse parses the Rdata of the private RR.
|
|
||||||
Parse([]string) error
|
|
||||||
// Pack is used when packing a private RR into a buffer.
|
|
||||||
Pack([]byte) (int, error)
|
|
||||||
// Unpack is used when unpacking a private RR from a buffer.
|
|
||||||
// TODO(miek): diff. signature than Pack, see edns0.go for instance.
|
|
||||||
Unpack([]byte) (int, error)
|
|
||||||
// Copy copies the Rdata.
|
|
||||||
Copy(PrivateRdata) error
|
|
||||||
// Len returns the length in octets of the Rdata.
|
|
||||||
Len() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrivateRR represents an RR that uses a PrivateRdata user-defined type.
|
|
||||||
// It mocks normal RRs and implements dns.RR interface.
|
|
||||||
type PrivateRR struct {
|
|
||||||
Hdr RR_Header
|
|
||||||
Data PrivateRdata
|
|
||||||
}
|
|
||||||
|
|
||||||
func mkPrivateRR(rrtype uint16) *PrivateRR {
|
|
||||||
// Panics if RR is not an instance of PrivateRR.
|
|
||||||
rrfunc, ok := TypeToRR[rrtype]
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
|
|
||||||
}
|
|
||||||
|
|
||||||
anyrr := rrfunc()
|
|
||||||
switch rr := anyrr.(type) {
|
|
||||||
case *PrivateRR:
|
|
||||||
return rr
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header return the RR header of r.
|
|
||||||
func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
|
|
||||||
|
|
||||||
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
|
|
||||||
|
|
||||||
// Private len and copy parts to satisfy RR interface.
|
|
||||||
func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() }
|
|
||||||
func (r *PrivateRR) copy() RR {
|
|
||||||
// make new RR like this:
|
|
||||||
rr := mkPrivateRR(r.Hdr.Rrtype)
|
|
||||||
newh := r.Hdr.copyHeader()
|
|
||||||
rr.Hdr = *newh
|
|
||||||
|
|
||||||
err := r.Data.Copy(rr.Data)
|
|
||||||
if err != nil {
|
|
||||||
panic("dns: got value that could not be used to copy Private rdata")
|
|
||||||
}
|
|
||||||
return rr
|
|
||||||
}
|
|
||||||
func (r *PrivateRR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {
|
|
||||||
off, err := r.Hdr.pack(msg, off, compression, compress)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
headerEnd := off
|
|
||||||
n, err := r.Data.Pack(msg[off:])
|
|
||||||
if err != nil {
|
|
||||||
return len(msg), err
|
|
||||||
}
|
|
||||||
off += n
|
|
||||||
r.Header().Rdlength = uint16(off - headerEnd)
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrivateHandle registers a private resource record type. It requires
|
|
||||||
// string and numeric representation of private RR type and generator function as argument.
|
|
||||||
func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
|
|
||||||
rtypestr = strings.ToUpper(rtypestr)
|
|
||||||
|
|
||||||
TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
|
|
||||||
TypeToString[rtype] = rtypestr
|
|
||||||
StringToType[rtypestr] = rtype
|
|
||||||
|
|
||||||
typeToUnpack[rtype] = func(h RR_Header, msg []byte, off int) (RR, int, error) {
|
|
||||||
if noRdata(h) {
|
|
||||||
return &h, off, nil
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
|
|
||||||
rr := mkPrivateRR(h.Rrtype)
|
|
||||||
rr.Hdr = h
|
|
||||||
|
|
||||||
off1, err := rr.Data.Unpack(msg[off:])
|
|
||||||
off += off1
|
|
||||||
if err != nil {
|
|
||||||
return rr, off, err
|
|
||||||
}
|
|
||||||
return rr, off, err
|
|
||||||
}
|
|
||||||
|
|
||||||
setPrivateRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|
||||||
rr := mkPrivateRR(h.Rrtype)
|
|
||||||
rr.Hdr = h
|
|
||||||
|
|
||||||
var l lex
|
|
||||||
text := make([]string, 0, 2) // could be 0..N elements, median is probably 1
|
|
||||||
Fetch:
|
|
||||||
for {
|
|
||||||
// TODO(miek): we could also be returning _QUOTE, this might or might not
|
|
||||||
// be an issue (basically parsing TXT becomes hard)
|
|
||||||
switch l = <-c; l.value {
|
|
||||||
case zNewline, zEOF:
|
|
||||||
break Fetch
|
|
||||||
case zString:
|
|
||||||
text = append(text, l.token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := rr.Data.Parse(text)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &ParseError{f, err.Error(), l}, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return rr, nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
typeToparserFunc[rtype] = parserFunc{setPrivateRR, true}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrivateHandleRemove removes defenitions required to support private RR type.
|
|
||||||
func PrivateHandleRemove(rtype uint16) {
|
|
||||||
rtypestr, ok := TypeToString[rtype]
|
|
||||||
if ok {
|
|
||||||
delete(TypeToRR, rtype)
|
|
||||||
delete(TypeToString, rtype)
|
|
||||||
delete(typeToparserFunc, rtype)
|
|
||||||
delete(StringToType, rtypestr)
|
|
||||||
delete(typeToUnpack, rtype)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
49
vendor/github.com/miekg/dns/rawmsg.go
generated
vendored
49
vendor/github.com/miekg/dns/rawmsg.go
generated
vendored
|
@ -1,49 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import "encoding/binary"
|
|
||||||
|
|
||||||
// rawSetRdlength sets the rdlength in the header of
|
|
||||||
// the RR. The offset 'off' must be positioned at the
|
|
||||||
// start of the header of the RR, 'end' must be the
|
|
||||||
// end of the RR.
|
|
||||||
func rawSetRdlength(msg []byte, off, end int) bool {
|
|
||||||
l := len(msg)
|
|
||||||
Loop:
|
|
||||||
for {
|
|
||||||
if off+1 > l {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c := int(msg[off])
|
|
||||||
off++
|
|
||||||
switch c & 0xC0 {
|
|
||||||
case 0x00:
|
|
||||||
if c == 0x00 {
|
|
||||||
// End of the domainname
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
if off+c > l {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
off += c
|
|
||||||
|
|
||||||
case 0xC0:
|
|
||||||
// pointer, next byte included, ends domainname
|
|
||||||
off++
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The domainname has been seen, we at the start of the fixed part in the header.
|
|
||||||
// Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length.
|
|
||||||
off += 2 + 2 + 4
|
|
||||||
if off+2 > l {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
//off+1 is the end of the header, 'end' is the end of the rr
|
|
||||||
//so 'end' - 'off+2' is the length of the rdata
|
|
||||||
rdatalen := end - (off + 2)
|
|
||||||
if rdatalen > 0xFFFF {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
binary.BigEndian.PutUint16(msg[off:], uint16(rdatalen))
|
|
||||||
return true
|
|
||||||
}
|
|
38
vendor/github.com/miekg/dns/reverse.go
generated
vendored
38
vendor/github.com/miekg/dns/reverse.go
generated
vendored
|
@ -1,38 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
// StringToType is the reverse of TypeToString, needed for string parsing.
|
|
||||||
var StringToType = reverseInt16(TypeToString)
|
|
||||||
|
|
||||||
// StringToClass is the reverse of ClassToString, needed for string parsing.
|
|
||||||
var StringToClass = reverseInt16(ClassToString)
|
|
||||||
|
|
||||||
// Map of opcodes strings.
|
|
||||||
var StringToOpcode = reverseInt(OpcodeToString)
|
|
||||||
|
|
||||||
// Map of rcodes strings.
|
|
||||||
var StringToRcode = reverseInt(RcodeToString)
|
|
||||||
|
|
||||||
// Reverse a map
|
|
||||||
func reverseInt8(m map[uint8]string) map[string]uint8 {
|
|
||||||
n := make(map[string]uint8, len(m))
|
|
||||||
for u, s := range m {
|
|
||||||
n[s] = u
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func reverseInt16(m map[uint16]string) map[string]uint16 {
|
|
||||||
n := make(map[string]uint16, len(m))
|
|
||||||
for u, s := range m {
|
|
||||||
n[s] = u
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func reverseInt(m map[int]string) map[string]int {
|
|
||||||
n := make(map[string]int, len(m))
|
|
||||||
for u, s := range m {
|
|
||||||
n[s] = u
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
84
vendor/github.com/miekg/dns/sanitize.go
generated
vendored
84
vendor/github.com/miekg/dns/sanitize.go
generated
vendored
|
@ -1,84 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
// Dedup removes identical RRs from rrs. It preserves the original ordering.
|
|
||||||
// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
|
|
||||||
// rrs.
|
|
||||||
// m is used to store the RRs temporay. If it is nil a new map will be allocated.
|
|
||||||
func Dedup(rrs []RR, m map[string]RR) []RR {
|
|
||||||
if m == nil {
|
|
||||||
m = make(map[string]RR)
|
|
||||||
}
|
|
||||||
// Save the keys, so we don't have to call normalizedString twice.
|
|
||||||
keys := make([]*string, 0, len(rrs))
|
|
||||||
|
|
||||||
for _, r := range rrs {
|
|
||||||
key := normalizedString(r)
|
|
||||||
keys = append(keys, &key)
|
|
||||||
if _, ok := m[key]; ok {
|
|
||||||
// Shortest TTL wins.
|
|
||||||
if m[key].Header().Ttl > r.Header().Ttl {
|
|
||||||
m[key].Header().Ttl = r.Header().Ttl
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
m[key] = r
|
|
||||||
}
|
|
||||||
// If the length of the result map equals the amount of RRs we got,
|
|
||||||
// it means they were all different. We can then just return the original rrset.
|
|
||||||
if len(m) == len(rrs) {
|
|
||||||
return rrs
|
|
||||||
}
|
|
||||||
|
|
||||||
j := 0
|
|
||||||
for i, r := range rrs {
|
|
||||||
// If keys[i] lives in the map, we should copy and remove it.
|
|
||||||
if _, ok := m[*keys[i]]; ok {
|
|
||||||
delete(m, *keys[i])
|
|
||||||
rrs[j] = r
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(m) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rrs[:j]
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalizedString returns a normalized string from r. The TTL
|
|
||||||
// is removed and the domain name is lowercased. We go from this:
|
|
||||||
// DomainName<TAB>TTL<TAB>CLASS<TAB>TYPE<TAB>RDATA to:
|
|
||||||
// lowercasename<TAB>CLASS<TAB>TYPE...
|
|
||||||
func normalizedString(r RR) string {
|
|
||||||
// A string Go DNS makes has: domainname<TAB>TTL<TAB>...
|
|
||||||
b := []byte(r.String())
|
|
||||||
|
|
||||||
// find the first non-escaped tab, then another, so we capture where the TTL lives.
|
|
||||||
esc := false
|
|
||||||
ttlStart, ttlEnd := 0, 0
|
|
||||||
for i := 0; i < len(b) && ttlEnd == 0; i++ {
|
|
||||||
switch {
|
|
||||||
case b[i] == '\\':
|
|
||||||
esc = !esc
|
|
||||||
case b[i] == '\t' && !esc:
|
|
||||||
if ttlStart == 0 {
|
|
||||||
ttlStart = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ttlEnd == 0 {
|
|
||||||
ttlEnd = i
|
|
||||||
}
|
|
||||||
case b[i] >= 'A' && b[i] <= 'Z' && !esc:
|
|
||||||
b[i] += 32
|
|
||||||
default:
|
|
||||||
esc = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove TTL.
|
|
||||||
copy(b[ttlStart:], b[ttlEnd:])
|
|
||||||
cut := ttlEnd - ttlStart
|
|
||||||
return string(b[:len(b)-cut])
|
|
||||||
}
|
|
981
vendor/github.com/miekg/dns/scan.go
generated
vendored
981
vendor/github.com/miekg/dns/scan.go
generated
vendored
|
@ -1,981 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type debugging bool
|
|
||||||
|
|
||||||
const debug debugging = false
|
|
||||||
|
|
||||||
func (d debugging) Printf(format string, args ...interface{}) {
|
|
||||||
if d {
|
|
||||||
log.Printf(format, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxTok = 2048 // Largest token we can return.
|
|
||||||
const maxUint16 = 1<<16 - 1
|
|
||||||
|
|
||||||
// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
|
|
||||||
// * Add ownernames if they are left blank;
|
|
||||||
// * Suppress sequences of spaces;
|
|
||||||
// * Make each RR fit on one line (_NEWLINE is send as last)
|
|
||||||
// * Handle comments: ;
|
|
||||||
// * Handle braces - anywhere.
|
|
||||||
const (
|
|
||||||
// Zonefile
|
|
||||||
zEOF = iota
|
|
||||||
zString
|
|
||||||
zBlank
|
|
||||||
zQuote
|
|
||||||
zNewline
|
|
||||||
zRrtpe
|
|
||||||
zOwner
|
|
||||||
zClass
|
|
||||||
zDirOrigin // $ORIGIN
|
|
||||||
zDirTtl // $TTL
|
|
||||||
zDirInclude // $INCLUDE
|
|
||||||
zDirGenerate // $GENERATE
|
|
||||||
|
|
||||||
// Privatekey file
|
|
||||||
zValue
|
|
||||||
zKey
|
|
||||||
|
|
||||||
zExpectOwnerDir // Ownername
|
|
||||||
zExpectOwnerBl // Whitespace after the ownername
|
|
||||||
zExpectAny // Expect rrtype, ttl or class
|
|
||||||
zExpectAnyNoClass // Expect rrtype or ttl
|
|
||||||
zExpectAnyNoClassBl // The whitespace after _EXPECT_ANY_NOCLASS
|
|
||||||
zExpectAnyNoTtl // Expect rrtype or class
|
|
||||||
zExpectAnyNoTtlBl // Whitespace after _EXPECT_ANY_NOTTL
|
|
||||||
zExpectRrtype // Expect rrtype
|
|
||||||
zExpectRrtypeBl // Whitespace BEFORE rrtype
|
|
||||||
zExpectRdata // The first element of the rdata
|
|
||||||
zExpectDirTtlBl // Space after directive $TTL
|
|
||||||
zExpectDirTtl // Directive $TTL
|
|
||||||
zExpectDirOriginBl // Space after directive $ORIGIN
|
|
||||||
zExpectDirOrigin // Directive $ORIGIN
|
|
||||||
zExpectDirIncludeBl // Space after directive $INCLUDE
|
|
||||||
zExpectDirInclude // Directive $INCLUDE
|
|
||||||
zExpectDirGenerate // Directive $GENERATE
|
|
||||||
zExpectDirGenerateBl // Space after directive $GENERATE
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseError is a parsing error. It contains the parse error and the location in the io.Reader
|
|
||||||
// where the error occurred.
|
|
||||||
type ParseError struct {
|
|
||||||
file string
|
|
||||||
err string
|
|
||||||
lex lex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ParseError) Error() (s string) {
|
|
||||||
if e.file != "" {
|
|
||||||
s = e.file + ": "
|
|
||||||
}
|
|
||||||
s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " +
|
|
||||||
strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type lex struct {
|
|
||||||
token string // text of the token
|
|
||||||
tokenUpper string // uppercase text of the token
|
|
||||||
length int // length of the token
|
|
||||||
err bool // when true, token text has lexer error
|
|
||||||
value uint8 // value: zString, _BLANK, etc.
|
|
||||||
line int // line in the file
|
|
||||||
column int // column in the file
|
|
||||||
torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar
|
|
||||||
comment string // any comment text seen
|
|
||||||
}
|
|
||||||
|
|
||||||
// Token holds the token that are returned when a zone file is parsed.
|
|
||||||
type Token struct {
|
|
||||||
// The scanned resource record when error is not nil.
|
|
||||||
RR
|
|
||||||
// When an error occurred, this has the error specifics.
|
|
||||||
Error *ParseError
|
|
||||||
// A potential comment positioned after the RR and on the same line.
|
|
||||||
Comment string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRR reads the RR contained in the string s. Only the first RR is
|
|
||||||
// returned. If s contains no RR, return nil with no error. The class
|
|
||||||
// defaults to IN and TTL defaults to 3600. The full zone file syntax
|
|
||||||
// like $TTL, $ORIGIN, etc. is supported. All fields of the returned
|
|
||||||
// RR are set, except RR.Header().Rdlength which is set to 0.
|
|
||||||
func NewRR(s string) (RR, error) {
|
|
||||||
if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline
|
|
||||||
return ReadRR(strings.NewReader(s+"\n"), "")
|
|
||||||
}
|
|
||||||
return ReadRR(strings.NewReader(s), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadRR reads the RR contained in q.
|
|
||||||
// See NewRR for more documentation.
|
|
||||||
func ReadRR(q io.Reader, filename string) (RR, error) {
|
|
||||||
r := <-parseZoneHelper(q, ".", filename, 1)
|
|
||||||
if r == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Error != nil {
|
|
||||||
return nil, r.Error
|
|
||||||
}
|
|
||||||
return r.RR, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 .
|
|
||||||
// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported.
|
|
||||||
// The channel t is closed by ParseZone when the end of r is reached.
|
|
||||||
//
|
|
||||||
// Basic usage pattern when reading from a string (z) containing the
|
|
||||||
// zone data:
|
|
||||||
//
|
|
||||||
// for x := range dns.ParseZone(strings.NewReader(z), "", "") {
|
|
||||||
// if x.Error != nil {
|
|
||||||
// // log.Println(x.Error)
|
|
||||||
// } else {
|
|
||||||
// // Do something with x.RR
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Comments specified after an RR (and on the same line!) are returned too:
|
|
||||||
//
|
|
||||||
// foo. IN A 10.0.0.1 ; this is a comment
|
|
||||||
//
|
|
||||||
// The text "; this is comment" is returned in Token.Comment. Comments inside the
|
|
||||||
// RR are 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseZoneHelper(r io.Reader, origin, file string, chansize int) chan *Token {
|
|
||||||
t := make(chan *Token, chansize)
|
|
||||||
go parseZone(r, origin, file, t, 0)
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
|
||||||
defer func() {
|
|
||||||
if include == 0 {
|
|
||||||
close(t)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
s := scanInit(r)
|
|
||||||
c := make(chan lex)
|
|
||||||
// Start the lexer
|
|
||||||
go zlexer(s, c)
|
|
||||||
// 6 possible beginnings of a line, _ is a space
|
|
||||||
// 0. zRRTYPE -> all omitted until the rrtype
|
|
||||||
// 1. zOwner _ zRrtype -> class/ttl omitted
|
|
||||||
// 2. zOwner _ zString _ zRrtype -> class omitted
|
|
||||||
// 3. zOwner _ zString _ zClass _ zRrtype -> ttl/class
|
|
||||||
// 4. zOwner _ zClass _ zRrtype -> ttl omitted
|
|
||||||
// 5. zOwner _ zClass _ zString _ zRrtype -> class/ttl (reversed)
|
|
||||||
// After detecting these, we know the zRrtype so we can jump to functions
|
|
||||||
// handling the rdata for each of these types.
|
|
||||||
|
|
||||||
if origin == "" {
|
|
||||||
origin = "."
|
|
||||||
}
|
|
||||||
origin = Fqdn(origin)
|
|
||||||
if _, ok := IsDomainName(origin); !ok {
|
|
||||||
t <- &Token{Error: &ParseError{f, "bad initial origin name", lex{}}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
st := zExpectOwnerDir // initial state
|
|
||||||
var h RR_Header
|
|
||||||
var defttl uint32 = defaultTtl
|
|
||||||
var prevName string
|
|
||||||
for l := range c {
|
|
||||||
// Lexer spotted an error already
|
|
||||||
if l.err == true {
|
|
||||||
t <- &Token{Error: &ParseError{f, l.token, l}}
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
switch st {
|
|
||||||
case zExpectOwnerDir:
|
|
||||||
// We can also expect a directive, like $TTL or $ORIGIN
|
|
||||||
h.Ttl = defttl
|
|
||||||
h.Class = ClassINET
|
|
||||||
switch l.value {
|
|
||||||
case zNewline:
|
|
||||||
st = zExpectOwnerDir
|
|
||||||
case zOwner:
|
|
||||||
h.Name = l.token
|
|
||||||
if l.token[0] == '@' {
|
|
||||||
h.Name = origin
|
|
||||||
prevName = h.Name
|
|
||||||
st = zExpectOwnerBl
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if h.Name[l.length-1] != '.' {
|
|
||||||
h.Name = appendOrigin(h.Name, origin)
|
|
||||||
}
|
|
||||||
_, ok := IsDomainName(l.token)
|
|
||||||
if !ok {
|
|
||||||
t <- &Token{Error: &ParseError{f, "bad owner name", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
prevName = h.Name
|
|
||||||
st = zExpectOwnerBl
|
|
||||||
case zDirTtl:
|
|
||||||
st = zExpectDirTtlBl
|
|
||||||
case zDirOrigin:
|
|
||||||
st = zExpectDirOriginBl
|
|
||||||
case zDirInclude:
|
|
||||||
st = zExpectDirIncludeBl
|
|
||||||
case zDirGenerate:
|
|
||||||
st = zExpectDirGenerateBl
|
|
||||||
case zRrtpe:
|
|
||||||
h.Name = prevName
|
|
||||||
h.Rrtype = l.torc
|
|
||||||
st = zExpectRdata
|
|
||||||
case zClass:
|
|
||||||
h.Name = prevName
|
|
||||||
h.Class = l.torc
|
|
||||||
st = zExpectAnyNoClassBl
|
|
||||||
case zBlank:
|
|
||||||
// Discard, can happen when there is nothing on the
|
|
||||||
// line except the RR type
|
|
||||||
case zString:
|
|
||||||
ttl, ok := stringToTtl(l.token)
|
|
||||||
if !ok {
|
|
||||||
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
h.Ttl = ttl
|
|
||||||
// Don't about the defttl, we should take the $TTL value
|
|
||||||
// defttl = ttl
|
|
||||||
st = zExpectAnyNoTtlBl
|
|
||||||
|
|
||||||
default:
|
|
||||||
t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case zExpectDirIncludeBl:
|
|
||||||
if l.value != zBlank {
|
|
||||||
t <- &Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectDirInclude
|
|
||||||
case zExpectDirInclude:
|
|
||||||
if l.value != zString {
|
|
||||||
t <- &Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
neworigin := origin // There may be optionally a new origin set after the filename, if not use current one
|
|
||||||
l := <-c
|
|
||||||
switch l.value {
|
|
||||||
case zBlank:
|
|
||||||
l := <-c
|
|
||||||
if l.value == zString {
|
|
||||||
if _, ok := IsDomainName(l.token); !ok || l.length == 0 || l.err {
|
|
||||||
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// a new origin is specified.
|
|
||||||
if l.token[l.length-1] != '.' {
|
|
||||||
if origin != "." { // Prevent .. endings
|
|
||||||
neworigin = l.token + "." + origin
|
|
||||||
} else {
|
|
||||||
neworigin = l.token + origin
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
neworigin = l.token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case zNewline, zEOF:
|
|
||||||
// Ok
|
|
||||||
default:
|
|
||||||
t <- &Token{Error: &ParseError{f, "garbage after $INCLUDE", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Start with the new file
|
|
||||||
r1, e1 := os.Open(l.token)
|
|
||||||
if e1 != nil {
|
|
||||||
t <- &Token{Error: &ParseError{f, "failed to open `" + l.token + "'", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if include+1 > 7 {
|
|
||||||
t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
parseZone(r1, l.token, neworigin, t, include+1)
|
|
||||||
st = zExpectOwnerDir
|
|
||||||
case zExpectDirTtlBl:
|
|
||||||
if l.value != zBlank {
|
|
||||||
t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectDirTtl
|
|
||||||
case zExpectDirTtl:
|
|
||||||
if l.value != zString {
|
|
||||||
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if e, _ := slurpRemainder(c, f); e != nil {
|
|
||||||
t <- &Token{Error: e}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ttl, ok := stringToTtl(l.token)
|
|
||||||
if !ok {
|
|
||||||
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defttl = ttl
|
|
||||||
st = zExpectOwnerDir
|
|
||||||
case zExpectDirOriginBl:
|
|
||||||
if l.value != zBlank {
|
|
||||||
t <- &Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectDirOrigin
|
|
||||||
case zExpectDirOrigin:
|
|
||||||
if l.value != zString {
|
|
||||||
t <- &Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if e, _ := slurpRemainder(c, f); e != nil {
|
|
||||||
t <- &Token{Error: e}
|
|
||||||
}
|
|
||||||
if _, ok := IsDomainName(l.token); !ok {
|
|
||||||
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if l.token[l.length-1] != '.' {
|
|
||||||
if origin != "." { // Prevent .. endings
|
|
||||||
origin = l.token + "." + origin
|
|
||||||
} else {
|
|
||||||
origin = l.token + origin
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
origin = l.token
|
|
||||||
}
|
|
||||||
st = zExpectOwnerDir
|
|
||||||
case zExpectDirGenerateBl:
|
|
||||||
if l.value != zBlank {
|
|
||||||
t <- &Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectDirGenerate
|
|
||||||
case zExpectDirGenerate:
|
|
||||||
if l.value != zString {
|
|
||||||
t <- &Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if errMsg := generate(l, c, t, origin); errMsg != "" {
|
|
||||||
t <- &Token{Error: &ParseError{f, errMsg, l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectOwnerDir
|
|
||||||
case zExpectOwnerBl:
|
|
||||||
if l.value != zBlank {
|
|
||||||
t <- &Token{Error: &ParseError{f, "no blank after owner", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectAny
|
|
||||||
case zExpectAny:
|
|
||||||
switch l.value {
|
|
||||||
case zRrtpe:
|
|
||||||
h.Rrtype = l.torc
|
|
||||||
st = zExpectRdata
|
|
||||||
case zClass:
|
|
||||||
h.Class = l.torc
|
|
||||||
st = zExpectAnyNoClassBl
|
|
||||||
case zString:
|
|
||||||
ttl, ok := stringToTtl(l.token)
|
|
||||||
if !ok {
|
|
||||||
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
h.Ttl = ttl
|
|
||||||
// defttl = ttl // don't set the defttl here
|
|
||||||
st = zExpectAnyNoTtlBl
|
|
||||||
default:
|
|
||||||
t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case zExpectAnyNoClassBl:
|
|
||||||
if l.value != zBlank {
|
|
||||||
t <- &Token{Error: &ParseError{f, "no blank before class", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectAnyNoClass
|
|
||||||
case zExpectAnyNoTtlBl:
|
|
||||||
if l.value != zBlank {
|
|
||||||
t <- &Token{Error: &ParseError{f, "no blank before TTL", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectAnyNoTtl
|
|
||||||
case zExpectAnyNoTtl:
|
|
||||||
switch l.value {
|
|
||||||
case zClass:
|
|
||||||
h.Class = l.torc
|
|
||||||
st = zExpectRrtypeBl
|
|
||||||
case zRrtpe:
|
|
||||||
h.Rrtype = l.torc
|
|
||||||
st = zExpectRdata
|
|
||||||
default:
|
|
||||||
t <- &Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case zExpectAnyNoClass:
|
|
||||||
switch l.value {
|
|
||||||
case zString:
|
|
||||||
ttl, ok := stringToTtl(l.token)
|
|
||||||
if !ok {
|
|
||||||
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
h.Ttl = ttl
|
|
||||||
// defttl = ttl // don't set the def ttl anymore
|
|
||||||
st = zExpectRrtypeBl
|
|
||||||
case zRrtpe:
|
|
||||||
h.Rrtype = l.torc
|
|
||||||
st = zExpectRdata
|
|
||||||
default:
|
|
||||||
t <- &Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case zExpectRrtypeBl:
|
|
||||||
if l.value != zBlank {
|
|
||||||
t <- &Token{Error: &ParseError{f, "no blank before RR type", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st = zExpectRrtype
|
|
||||||
case zExpectRrtype:
|
|
||||||
if l.value != zRrtpe {
|
|
||||||
t <- &Token{Error: &ParseError{f, "unknown RR type", l}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
h.Rrtype = l.torc
|
|
||||||
st = zExpectRdata
|
|
||||||
case zExpectRdata:
|
|
||||||
r, e, c1 := setRR(h, c, origin, f)
|
|
||||||
if e != nil {
|
|
||||||
// If e.lex is nil than we have encounter a unknown RR type
|
|
||||||
// in that case we substitute our current lex token
|
|
||||||
if e.lex.token == "" && e.lex.value == 0 {
|
|
||||||
e.lex = l // Uh, dirty
|
|
||||||
}
|
|
||||||
t <- &Token{Error: e}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t <- &Token{RR: r, Comment: c1}
|
|
||||||
st = zExpectOwnerDir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this
|
|
||||||
// is not an error, because an empty zone file is still a zone file.
|
|
||||||
}
|
|
||||||
|
|
||||||
// zlexer scans the sourcefile and returns tokens on the channel c.
|
|
||||||
func zlexer(s *scan, c chan lex) {
|
|
||||||
var l lex
|
|
||||||
str := make([]byte, maxTok) // Should be enough for any token
|
|
||||||
stri := 0 // Offset in str (0 means empty)
|
|
||||||
com := make([]byte, maxTok) // Hold comment text
|
|
||||||
comi := 0
|
|
||||||
quote := false
|
|
||||||
escape := false
|
|
||||||
space := false
|
|
||||||
commt := false
|
|
||||||
rrtype := false
|
|
||||||
owner := true
|
|
||||||
brace := 0
|
|
||||||
x, err := s.tokenText()
|
|
||||||
defer close(c)
|
|
||||||
for err == nil {
|
|
||||||
l.column = s.position.Column
|
|
||||||
l.line = s.position.Line
|
|
||||||
if stri >= maxTok {
|
|
||||||
l.token = "token length insufficient for parsing"
|
|
||||||
l.err = true
|
|
||||||
debug.Printf("[%+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if comi >= maxTok {
|
|
||||||
l.token = "comment length insufficient for parsing"
|
|
||||||
l.err = true
|
|
||||||
debug.Printf("[%+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch x {
|
|
||||||
case ' ', '\t':
|
|
||||||
if escape {
|
|
||||||
escape = false
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if quote {
|
|
||||||
// Inside quotes this is legal
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if commt {
|
|
||||||
com[comi] = x
|
|
||||||
comi++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if stri == 0 {
|
|
||||||
// Space directly in the beginning, handled in the grammar
|
|
||||||
} else if owner {
|
|
||||||
// If we have a string and its the first, make it an owner
|
|
||||||
l.value = zOwner
|
|
||||||
l.token = string(str[:stri])
|
|
||||||
l.tokenUpper = strings.ToUpper(l.token)
|
|
||||||
l.length = stri
|
|
||||||
// escape $... start with a \ not a $, so this will work
|
|
||||||
switch l.tokenUpper {
|
|
||||||
case "$TTL":
|
|
||||||
l.value = zDirTtl
|
|
||||||
case "$ORIGIN":
|
|
||||||
l.value = zDirOrigin
|
|
||||||
case "$INCLUDE":
|
|
||||||
l.value = zDirInclude
|
|
||||||
case "$GENERATE":
|
|
||||||
l.value = zDirGenerate
|
|
||||||
}
|
|
||||||
debug.Printf("[7 %+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
} else {
|
|
||||||
l.value = zString
|
|
||||||
l.token = string(str[:stri])
|
|
||||||
l.tokenUpper = strings.ToUpper(l.token)
|
|
||||||
l.length = stri
|
|
||||||
if !rrtype {
|
|
||||||
if t, ok := StringToType[l.tokenUpper]; ok {
|
|
||||||
l.value = zRrtpe
|
|
||||||
l.torc = t
|
|
||||||
rrtype = true
|
|
||||||
} else {
|
|
||||||
if strings.HasPrefix(l.tokenUpper, "TYPE") {
|
|
||||||
t, ok := typeToInt(l.token)
|
|
||||||
if !ok {
|
|
||||||
l.token = "unknown RR type"
|
|
||||||
l.err = true
|
|
||||||
c <- l
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l.value = zRrtpe
|
|
||||||
l.torc = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t, ok := StringToClass[l.tokenUpper]; ok {
|
|
||||||
l.value = zClass
|
|
||||||
l.torc = t
|
|
||||||
} else {
|
|
||||||
if strings.HasPrefix(l.tokenUpper, "CLASS") {
|
|
||||||
t, ok := classToInt(l.token)
|
|
||||||
if !ok {
|
|
||||||
l.token = "unknown class"
|
|
||||||
l.err = true
|
|
||||||
c <- l
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l.value = zClass
|
|
||||||
l.torc = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug.Printf("[6 %+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
}
|
|
||||||
stri = 0
|
|
||||||
// I reverse space stuff here
|
|
||||||
if !space && !commt {
|
|
||||||
l.value = zBlank
|
|
||||||
l.token = " "
|
|
||||||
l.length = 1
|
|
||||||
debug.Printf("[5 %+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
}
|
|
||||||
owner = false
|
|
||||||
space = true
|
|
||||||
case ';':
|
|
||||||
if escape {
|
|
||||||
escape = false
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if quote {
|
|
||||||
// Inside quotes this is legal
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if stri > 0 {
|
|
||||||
l.value = zString
|
|
||||||
l.token = string(str[:stri])
|
|
||||||
l.tokenUpper = strings.ToUpper(l.token)
|
|
||||||
l.length = stri
|
|
||||||
debug.Printf("[4 %+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
stri = 0
|
|
||||||
}
|
|
||||||
commt = true
|
|
||||||
com[comi] = ';'
|
|
||||||
comi++
|
|
||||||
case '\r':
|
|
||||||
escape = false
|
|
||||||
if quote {
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// discard if outside of quotes
|
|
||||||
case '\n':
|
|
||||||
escape = false
|
|
||||||
// Escaped newline
|
|
||||||
if quote {
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// inside quotes this is legal
|
|
||||||
if commt {
|
|
||||||
// Reset a comment
|
|
||||||
commt = false
|
|
||||||
rrtype = false
|
|
||||||
stri = 0
|
|
||||||
// If not in a brace this ends the comment AND the RR
|
|
||||||
if brace == 0 {
|
|
||||||
owner = true
|
|
||||||
owner = true
|
|
||||||
l.value = zNewline
|
|
||||||
l.token = "\n"
|
|
||||||
l.tokenUpper = l.token
|
|
||||||
l.length = 1
|
|
||||||
l.comment = string(com[:comi])
|
|
||||||
debug.Printf("[3 %+v %+v]", l.token, l.comment)
|
|
||||||
c <- l
|
|
||||||
l.comment = ""
|
|
||||||
comi = 0
|
|
||||||
break
|
|
||||||
}
|
|
||||||
com[comi] = ' ' // convert newline to space
|
|
||||||
comi++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if brace == 0 {
|
|
||||||
// If there is previous text, we should output it here
|
|
||||||
if stri != 0 {
|
|
||||||
l.value = zString
|
|
||||||
l.token = string(str[:stri])
|
|
||||||
l.tokenUpper = strings.ToUpper(l.token)
|
|
||||||
|
|
||||||
l.length = stri
|
|
||||||
if !rrtype {
|
|
||||||
if t, ok := StringToType[l.tokenUpper]; ok {
|
|
||||||
l.value = zRrtpe
|
|
||||||
l.torc = t
|
|
||||||
rrtype = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug.Printf("[2 %+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
}
|
|
||||||
l.value = zNewline
|
|
||||||
l.token = "\n"
|
|
||||||
l.tokenUpper = l.token
|
|
||||||
l.length = 1
|
|
||||||
debug.Printf("[1 %+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
stri = 0
|
|
||||||
commt = false
|
|
||||||
rrtype = false
|
|
||||||
owner = true
|
|
||||||
comi = 0
|
|
||||||
}
|
|
||||||
case '\\':
|
|
||||||
// comments do not get escaped chars, everything is copied
|
|
||||||
if commt {
|
|
||||||
com[comi] = x
|
|
||||||
comi++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// something already escaped must be in string
|
|
||||||
if escape {
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
escape = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// something escaped outside of string gets added to string
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
escape = true
|
|
||||||
case '"':
|
|
||||||
if commt {
|
|
||||||
com[comi] = x
|
|
||||||
comi++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if escape {
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
escape = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
space = false
|
|
||||||
// send previous gathered text and the quote
|
|
||||||
if stri != 0 {
|
|
||||||
l.value = zString
|
|
||||||
l.token = string(str[:stri])
|
|
||||||
l.tokenUpper = strings.ToUpper(l.token)
|
|
||||||
l.length = stri
|
|
||||||
|
|
||||||
debug.Printf("[%+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
stri = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// send quote itself as separate token
|
|
||||||
l.value = zQuote
|
|
||||||
l.token = "\""
|
|
||||||
l.tokenUpper = l.token
|
|
||||||
l.length = 1
|
|
||||||
c <- l
|
|
||||||
quote = !quote
|
|
||||||
case '(', ')':
|
|
||||||
if commt {
|
|
||||||
com[comi] = x
|
|
||||||
comi++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if escape {
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
escape = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if quote {
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch x {
|
|
||||||
case ')':
|
|
||||||
brace--
|
|
||||||
if brace < 0 {
|
|
||||||
l.token = "extra closing brace"
|
|
||||||
l.tokenUpper = l.token
|
|
||||||
l.err = true
|
|
||||||
debug.Printf("[%+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case '(':
|
|
||||||
brace++
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
escape = false
|
|
||||||
if commt {
|
|
||||||
com[comi] = x
|
|
||||||
comi++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
str[stri] = x
|
|
||||||
stri++
|
|
||||||
space = false
|
|
||||||
}
|
|
||||||
x, err = s.tokenText()
|
|
||||||
}
|
|
||||||
if stri > 0 {
|
|
||||||
// Send remainder
|
|
||||||
l.token = string(str[:stri])
|
|
||||||
l.tokenUpper = strings.ToUpper(l.token)
|
|
||||||
l.length = stri
|
|
||||||
l.value = zString
|
|
||||||
debug.Printf("[%+v]", l.token)
|
|
||||||
c <- l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the class number from CLASSxx
|
|
||||||
func classToInt(token string) (uint16, bool) {
|
|
||||||
offset := 5
|
|
||||||
if len(token) < offset+1 {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
class, ok := strconv.Atoi(token[offset:])
|
|
||||||
if ok != nil || class > maxUint16 {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
return uint16(class), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the rr number from TYPExxx
|
|
||||||
func typeToInt(token string) (uint16, bool) {
|
|
||||||
offset := 4
|
|
||||||
if len(token) < offset+1 {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
typ, ok := strconv.Atoi(token[offset:])
|
|
||||||
if ok != nil || typ > maxUint16 {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
return uint16(typ), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse things like 2w, 2m, etc, Return the time in seconds.
|
|
||||||
func stringToTtl(token string) (uint32, bool) {
|
|
||||||
s := uint32(0)
|
|
||||||
i := uint32(0)
|
|
||||||
for _, c := range token {
|
|
||||||
switch c {
|
|
||||||
case 's', 'S':
|
|
||||||
s += i
|
|
||||||
i = 0
|
|
||||||
case 'm', 'M':
|
|
||||||
s += i * 60
|
|
||||||
i = 0
|
|
||||||
case 'h', 'H':
|
|
||||||
s += i * 60 * 60
|
|
||||||
i = 0
|
|
||||||
case 'd', 'D':
|
|
||||||
s += i * 60 * 60 * 24
|
|
||||||
i = 0
|
|
||||||
case 'w', 'W':
|
|
||||||
s += i * 60 * 60 * 24 * 7
|
|
||||||
i = 0
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
i *= 10
|
|
||||||
i += uint32(c) - '0'
|
|
||||||
default:
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s + i, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse LOC records' <digits>[.<digits>][mM] into a
|
|
||||||
// mantissa exponent format. Token should contain the entire
|
|
||||||
// string (i.e. no spaces allowed)
|
|
||||||
func stringToCm(token string) (e, m uint8, ok bool) {
|
|
||||||
if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' {
|
|
||||||
token = token[0 : len(token)-1]
|
|
||||||
}
|
|
||||||
s := strings.SplitN(token, ".", 2)
|
|
||||||
var meters, cmeters, val int
|
|
||||||
var err error
|
|
||||||
switch len(s) {
|
|
||||||
case 2:
|
|
||||||
if cmeters, err = strconv.Atoi(s[1]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case 1:
|
|
||||||
if meters, err = strconv.Atoi(s[0]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case 0:
|
|
||||||
// huh?
|
|
||||||
return 0, 0, false
|
|
||||||
}
|
|
||||||
ok = true
|
|
||||||
if meters > 0 {
|
|
||||||
e = 2
|
|
||||||
val = meters
|
|
||||||
} else {
|
|
||||||
e = 0
|
|
||||||
val = cmeters
|
|
||||||
}
|
|
||||||
for val > 10 {
|
|
||||||
e++
|
|
||||||
val /= 10
|
|
||||||
}
|
|
||||||
if e > 9 {
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
m = uint8(val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendOrigin(name, origin string) string {
|
|
||||||
if origin == "." {
|
|
||||||
return name + origin
|
|
||||||
}
|
|
||||||
return name + "." + origin
|
|
||||||
}
|
|
||||||
|
|
||||||
// LOC record helper function
|
|
||||||
func locCheckNorth(token string, latitude uint32) (uint32, bool) {
|
|
||||||
switch token {
|
|
||||||
case "n", "N":
|
|
||||||
return LOC_EQUATOR + latitude, true
|
|
||||||
case "s", "S":
|
|
||||||
return LOC_EQUATOR - latitude, true
|
|
||||||
}
|
|
||||||
return latitude, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// LOC record helper function
|
|
||||||
func locCheckEast(token string, longitude uint32) (uint32, bool) {
|
|
||||||
switch token {
|
|
||||||
case "e", "E":
|
|
||||||
return LOC_EQUATOR + longitude, true
|
|
||||||
case "w", "W":
|
|
||||||
return LOC_EQUATOR - longitude, true
|
|
||||||
}
|
|
||||||
return longitude, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Eat" the rest of the "line". Return potential comments
|
|
||||||
func slurpRemainder(c chan lex, f string) (*ParseError, string) {
|
|
||||||
l := <-c
|
|
||||||
com := ""
|
|
||||||
switch l.value {
|
|
||||||
case zBlank:
|
|
||||||
l = <-c
|
|
||||||
com = l.comment
|
|
||||||
if l.value != zNewline && l.value != zEOF {
|
|
||||||
return &ParseError{f, "garbage after rdata", l}, ""
|
|
||||||
}
|
|
||||||
case zNewline:
|
|
||||||
com = l.comment
|
|
||||||
case zEOF:
|
|
||||||
default:
|
|
||||||
return &ParseError{f, "garbage after rdata", l}, ""
|
|
||||||
}
|
|
||||||
return nil, com
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64"
|
|
||||||
// Used for NID and L64 record.
|
|
||||||
func stringToNodeID(l lex) (uint64, *ParseError) {
|
|
||||||
if len(l.token) < 19 {
|
|
||||||
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
|
|
||||||
}
|
|
||||||
// There must be three colons at fixes postitions, if not its a parse error
|
|
||||||
if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
|
|
||||||
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
|
|
||||||
}
|
|
||||||
s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19]
|
|
||||||
u, err := strconv.ParseUint(s, 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
|
|
||||||
}
|
|
||||||
return u, nil
|
|
||||||
}
|
|
2179
vendor/github.com/miekg/dns/scan_rr.go
generated
vendored
2179
vendor/github.com/miekg/dns/scan_rr.go
generated
vendored
File diff suppressed because it is too large
Load diff
43
vendor/github.com/miekg/dns/scanner.go
generated
vendored
43
vendor/github.com/miekg/dns/scanner.go
generated
vendored
|
@ -1,43 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
// Implement a simple scanner, return a byte stream from an io reader.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"io"
|
|
||||||
"text/scanner"
|
|
||||||
)
|
|
||||||
|
|
||||||
type scan struct {
|
|
||||||
src *bufio.Reader
|
|
||||||
position scanner.Position
|
|
||||||
eof bool // Have we just seen a eof
|
|
||||||
}
|
|
||||||
|
|
||||||
func scanInit(r io.Reader) *scan {
|
|
||||||
s := new(scan)
|
|
||||||
s.src = bufio.NewReader(r)
|
|
||||||
s.position.Line = 1
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// tokenText returns the next byte from the input
|
|
||||||
func (s *scan) tokenText() (byte, error) {
|
|
||||||
c, err := s.src.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
// delay the newline handling until the next token is delivered,
|
|
||||||
// fixes off-by-one errors when reporting a parse error.
|
|
||||||
if s.eof == true {
|
|
||||||
s.position.Line++
|
|
||||||
s.position.Column = 0
|
|
||||||
s.eof = false
|
|
||||||
}
|
|
||||||
if c == '\n' {
|
|
||||||
s.eof = true
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
s.position.Column++
|
|
||||||
return c, nil
|
|
||||||
}
|
|
734
vendor/github.com/miekg/dns/server.go
generated
vendored
734
vendor/github.com/miekg/dns/server.go
generated
vendored
|
@ -1,734 +0,0 @@
|
||||||
// DNS server implementation.
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Maximum number of TCP queries before we close the socket.
|
|
||||||
const maxTCPQueries = 128
|
|
||||||
|
|
||||||
// Handler is implemented by any value that implements ServeDNS.
|
|
||||||
type Handler interface {
|
|
||||||
ServeDNS(w ResponseWriter, r *Msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A ResponseWriter interface is used by an DNS handler to
|
|
||||||
// construct an DNS response.
|
|
||||||
type ResponseWriter interface {
|
|
||||||
// LocalAddr returns the net.Addr of the server
|
|
||||||
LocalAddr() net.Addr
|
|
||||||
// RemoteAddr returns the net.Addr of the client that sent the current request.
|
|
||||||
RemoteAddr() net.Addr
|
|
||||||
// WriteMsg writes a reply back to the client.
|
|
||||||
WriteMsg(*Msg) error
|
|
||||||
// Write writes a raw buffer back to the client.
|
|
||||||
Write([]byte) (int, error)
|
|
||||||
// Close closes the connection.
|
|
||||||
Close() error
|
|
||||||
// TsigStatus returns the status of the Tsig.
|
|
||||||
TsigStatus() error
|
|
||||||
// TsigTimersOnly sets the tsig timers only boolean.
|
|
||||||
TsigTimersOnly(bool)
|
|
||||||
// Hijack lets the caller take over the connection.
|
|
||||||
// After a call to Hijack(), the DNS package will not do anything with the connection.
|
|
||||||
Hijack()
|
|
||||||
}
|
|
||||||
|
|
||||||
type response struct {
|
|
||||||
hijacked bool // connection has been hijacked by handler
|
|
||||||
tsigStatus error
|
|
||||||
tsigTimersOnly bool
|
|
||||||
tsigRequestMAC string
|
|
||||||
tsigSecret map[string]string // the tsig secrets
|
|
||||||
udp *net.UDPConn // i/o connection if UDP was used
|
|
||||||
tcp net.Conn // 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
|
|
||||||
// zone name of each incoming request against a list of
|
|
||||||
// registered patterns add calls the handler for the pattern
|
|
||||||
// that most closely matches the zone name. ServeMux is DNSSEC aware, meaning
|
|
||||||
// that queries for the DS record are redirected to the parent zone (if that
|
|
||||||
// is also registered), otherwise the child gets the query.
|
|
||||||
// ServeMux is also safe for concurrent access from multiple goroutines.
|
|
||||||
type ServeMux struct {
|
|
||||||
z map[string]Handler
|
|
||||||
m *sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServeMux allocates and returns a new ServeMux.
|
|
||||||
func NewServeMux() *ServeMux { return &ServeMux{z: make(map[string]Handler), m: new(sync.RWMutex)} }
|
|
||||||
|
|
||||||
// DefaultServeMux is the default ServeMux used by Serve.
|
|
||||||
var DefaultServeMux = NewServeMux()
|
|
||||||
|
|
||||||
// The HandlerFunc type is an adapter to allow the use of
|
|
||||||
// ordinary functions as DNS handlers. If f is a function
|
|
||||||
// with the appropriate signature, HandlerFunc(f) is a
|
|
||||||
// Handler object that calls f.
|
|
||||||
type HandlerFunc func(ResponseWriter, *Msg)
|
|
||||||
|
|
||||||
// ServeDNS calls f(w, r).
|
|
||||||
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
|
|
||||||
f(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
|
|
||||||
func HandleFailed(w ResponseWriter, r *Msg) {
|
|
||||||
m := new(Msg)
|
|
||||||
m.SetRcode(r, RcodeServerFailure)
|
|
||||||
// does not matter if this write fails
|
|
||||||
w.WriteMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
|
|
||||||
|
|
||||||
// ListenAndServe Starts a server on address and network specified Invoke handler
|
|
||||||
// for incoming queries.
|
|
||||||
func ListenAndServe(addr string, network string, handler Handler) error {
|
|
||||||
server := &Server{Addr: addr, Net: network, Handler: handler}
|
|
||||||
return server.ListenAndServe()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenAndServeTLS acts like http.ListenAndServeTLS, more information in
|
|
||||||
// http://golang.org/pkg/net/http/#ListenAndServeTLS
|
|
||||||
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {
|
|
||||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
config := tls.Config{
|
|
||||||
Certificates: []tls.Certificate{cert},
|
|
||||||
}
|
|
||||||
|
|
||||||
server := &Server{
|
|
||||||
Addr: addr,
|
|
||||||
Net: "tcp-tls",
|
|
||||||
TLSConfig: &config,
|
|
||||||
Handler: handler,
|
|
||||||
}
|
|
||||||
|
|
||||||
return server.ListenAndServe()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActivateAndServe activates a server with a listener from systemd,
|
|
||||||
// l and p should not both be non-nil.
|
|
||||||
// If both l and p are not nil only p will be used.
|
|
||||||
// Invoke handler for incoming queries.
|
|
||||||
func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {
|
|
||||||
server := &Server{Listener: l, PacketConn: p, Handler: handler}
|
|
||||||
return server.ActivateAndServe()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mux *ServeMux) match(q string, t uint16) Handler {
|
|
||||||
mux.m.RLock()
|
|
||||||
defer mux.m.RUnlock()
|
|
||||||
var handler Handler
|
|
||||||
b := make([]byte, len(q)) // worst case, one label of length q
|
|
||||||
off := 0
|
|
||||||
end := false
|
|
||||||
for {
|
|
||||||
l := len(q[off:])
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
b[i] = q[off+i]
|
|
||||||
if b[i] >= 'A' && b[i] <= 'Z' {
|
|
||||||
b[i] |= ('a' - 'A')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if h, ok := mux.z[string(b[:l])]; ok { // causes garbage, might want to change the map key
|
|
||||||
if t != TypeDS {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
// Continue for DS to see if we have a parent too, if so delegeate to the parent
|
|
||||||
handler = h
|
|
||||||
}
|
|
||||||
off, end = NextLabel(q, off)
|
|
||||||
if end {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Wildcard match, if we have found nothing try the root zone as a last resort.
|
|
||||||
if h, ok := mux.z["."]; ok {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
return handler
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle adds a handler to the ServeMux for pattern.
|
|
||||||
func (mux *ServeMux) Handle(pattern string, handler Handler) {
|
|
||||||
if pattern == "" {
|
|
||||||
panic("dns: invalid pattern " + pattern)
|
|
||||||
}
|
|
||||||
mux.m.Lock()
|
|
||||||
mux.z[Fqdn(pattern)] = handler
|
|
||||||
mux.m.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleFunc adds a handler function to the ServeMux for pattern.
|
|
||||||
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
|
||||||
mux.Handle(pattern, HandlerFunc(handler))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleRemove deregistrars the handler specific for pattern from the ServeMux.
|
|
||||||
func (mux *ServeMux) HandleRemove(pattern string) {
|
|
||||||
if pattern == "" {
|
|
||||||
panic("dns: invalid pattern " + pattern)
|
|
||||||
}
|
|
||||||
mux.m.Lock()
|
|
||||||
delete(mux.z, Fqdn(pattern))
|
|
||||||
mux.m.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeDNS dispatches the request to the handler whose
|
|
||||||
// pattern most closely matches the request message. If DefaultServeMux
|
|
||||||
// is used the correct thing for DS queries is done: a possible parent
|
|
||||||
// is sought.
|
|
||||||
// If no handler is found a standard SERVFAIL message is returned
|
|
||||||
// If the request message does not have exactly one question in the
|
|
||||||
// question section a SERVFAIL is returned, unlesss Unsafe is true.
|
|
||||||
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
|
|
||||||
var h Handler
|
|
||||||
if len(request.Question) < 1 { // allow more than one question
|
|
||||||
h = failedHandler()
|
|
||||||
} else {
|
|
||||||
if h = mux.match(request.Question[0].Name, request.Question[0].Qtype); h == nil {
|
|
||||||
h = failedHandler()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.ServeDNS(w, request)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle registers the handler with the given pattern
|
|
||||||
// in the DefaultServeMux. The documentation for
|
|
||||||
// ServeMux explains how patterns are matched.
|
|
||||||
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
|
|
||||||
|
|
||||||
// HandleRemove deregisters the handle with the given pattern
|
|
||||||
// in the DefaultServeMux.
|
|
||||||
func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
|
|
||||||
|
|
||||||
// HandleFunc registers the handler function with the given pattern
|
|
||||||
// in the DefaultServeMux.
|
|
||||||
func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
|
||||||
DefaultServeMux.HandleFunc(pattern, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writer writes raw DNS messages; each call to Write should send an entire message.
|
|
||||||
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.Conn, 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.Conn, 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.
|
|
||||||
Addr string
|
|
||||||
// if "tcp" or "tcp-tls" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one
|
|
||||||
Net string
|
|
||||||
// TCP Listener to use, this is to aid in systemd's socket activation.
|
|
||||||
Listener net.Listener
|
|
||||||
// TLS connection configuration
|
|
||||||
TLSConfig *tls.Config
|
|
||||||
// UDP "Listener" to use, this is to aid in systemd's socket activation.
|
|
||||||
PacketConn net.PacketConn
|
|
||||||
// Handler to invoke, dns.DefaultServeMux if nil.
|
|
||||||
Handler Handler
|
|
||||||
// Default buffer size to use to read incoming UDP messages. If not set
|
|
||||||
// it defaults to MinMsgSize (512 B).
|
|
||||||
UDPSize int
|
|
||||||
// The net.Conn.SetReadTimeout value for new connections, defaults to 2 * time.Second.
|
|
||||||
ReadTimeout time.Duration
|
|
||||||
// The net.Conn.SetWriteTimeout value for new connections, defaults to 2 * time.Second.
|
|
||||||
WriteTimeout time.Duration
|
|
||||||
// TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966).
|
|
||||||
IdleTimeout func() time.Duration
|
|
||||||
// Secret(s) for Tsig map[<zonename>]<base64 secret>.
|
|
||||||
TsigSecret map[string]string
|
|
||||||
// Unsafe instructs the server to disregard any sanity checks and directly hand the message to
|
|
||||||
// the handler. It will specifically not check if the query has the QR bit not set.
|
|
||||||
Unsafe bool
|
|
||||||
// If NotifyStartedFunc is set it is called once the server has started listening.
|
|
||||||
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
|
|
||||||
|
|
||||||
// Graceful shutdown handling
|
|
||||||
|
|
||||||
inFlight sync.WaitGroup
|
|
||||||
|
|
||||||
lock sync.RWMutex
|
|
||||||
started bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenAndServe starts a nameserver on the configured address in *Server.
|
|
||||||
func (srv *Server) ListenAndServe() error {
|
|
||||||
srv.lock.Lock()
|
|
||||||
defer srv.lock.Unlock()
|
|
||||||
if srv.started {
|
|
||||||
return &Error{err: "server already started"}
|
|
||||||
}
|
|
||||||
addr := srv.Addr
|
|
||||||
if addr == "" {
|
|
||||||
addr = ":domain"
|
|
||||||
}
|
|
||||||
if srv.UDPSize == 0 {
|
|
||||||
srv.UDPSize = MinMsgSize
|
|
||||||
}
|
|
||||||
switch srv.Net {
|
|
||||||
case "tcp", "tcp4", "tcp6":
|
|
||||||
a, err := net.ResolveTCPAddr(srv.Net, addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
l, err := net.ListenTCP(srv.Net, a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
srv.Listener = l
|
|
||||||
srv.started = true
|
|
||||||
srv.lock.Unlock()
|
|
||||||
err = srv.serveTCP(l)
|
|
||||||
srv.lock.Lock() // to satisfy the defer at the top
|
|
||||||
return err
|
|
||||||
case "tcp-tls", "tcp4-tls", "tcp6-tls":
|
|
||||||
network := "tcp"
|
|
||||||
if srv.Net == "tcp4-tls" {
|
|
||||||
network = "tcp4"
|
|
||||||
} else if srv.Net == "tcp6" {
|
|
||||||
network = "tcp6"
|
|
||||||
}
|
|
||||||
|
|
||||||
l, err := tls.Listen(network, addr, srv.TLSConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
srv.Listener = l
|
|
||||||
srv.started = true
|
|
||||||
srv.lock.Unlock()
|
|
||||||
err = srv.serveTCP(l)
|
|
||||||
srv.lock.Lock() // to satisfy the defer at the top
|
|
||||||
return err
|
|
||||||
case "udp", "udp4", "udp6":
|
|
||||||
a, err := net.ResolveUDPAddr(srv.Net, addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
l, err := net.ListenUDP(srv.Net, a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if e := setUDPSocketOptions(l); e != nil {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
srv.PacketConn = l
|
|
||||||
srv.started = true
|
|
||||||
srv.lock.Unlock()
|
|
||||||
err = srv.serveUDP(l)
|
|
||||||
srv.lock.Lock() // to satisfy the defer at the top
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return &Error{err: "bad network"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActivateAndServe starts a nameserver with the PacketConn or Listener
|
|
||||||
// configured in *Server. Its main use is to start a server from systemd.
|
|
||||||
func (srv *Server) ActivateAndServe() error {
|
|
||||||
srv.lock.Lock()
|
|
||||||
defer srv.lock.Unlock()
|
|
||||||
if srv.started {
|
|
||||||
return &Error{err: "server already started"}
|
|
||||||
}
|
|
||||||
pConn := srv.PacketConn
|
|
||||||
l := srv.Listener
|
|
||||||
if pConn != nil {
|
|
||||||
if srv.UDPSize == 0 {
|
|
||||||
srv.UDPSize = MinMsgSize
|
|
||||||
}
|
|
||||||
// Check PacketConn interface's type is valid and value
|
|
||||||
// is not nil
|
|
||||||
if t, ok := pConn.(*net.UDPConn); ok && t != nil {
|
|
||||||
if e := setUDPSocketOptions(t); e != nil {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
srv.started = true
|
|
||||||
srv.lock.Unlock()
|
|
||||||
e := srv.serveUDP(t)
|
|
||||||
srv.lock.Lock() // to satisfy the defer at the top
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if l != nil {
|
|
||||||
srv.started = true
|
|
||||||
srv.lock.Unlock()
|
|
||||||
e := srv.serveTCP(l)
|
|
||||||
srv.lock.Lock() // to satisfy the defer at the top
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
return &Error{err: "bad listeners"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 an error
|
|
||||||
// is returned.
|
|
||||||
func (srv *Server) Shutdown() error {
|
|
||||||
srv.lock.Lock()
|
|
||||||
if !srv.started {
|
|
||||||
srv.lock.Unlock()
|
|
||||||
return &Error{err: "server not started"}
|
|
||||||
}
|
|
||||||
srv.started = false
|
|
||||||
srv.lock.Unlock()
|
|
||||||
|
|
||||||
if srv.PacketConn != nil {
|
|
||||||
srv.PacketConn.Close()
|
|
||||||
}
|
|
||||||
if srv.Listener != nil {
|
|
||||||
srv.Listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
fin := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
srv.inFlight.Wait()
|
|
||||||
fin <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(srv.getReadTimeout()):
|
|
||||||
return &Error{err: "server shutdown is pending"}
|
|
||||||
case <-fin:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getReadTimeout is a helper func to use system timeout if server did not intend to change it.
|
|
||||||
func (srv *Server) getReadTimeout() time.Duration {
|
|
||||||
rtimeout := dnsTimeout
|
|
||||||
if srv.ReadTimeout != 0 {
|
|
||||||
rtimeout = srv.ReadTimeout
|
|
||||||
}
|
|
||||||
return rtimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// serveTCP starts a TCP listener for the server.
|
|
||||||
// Each request is handled in a separate goroutine.
|
|
||||||
func (srv *Server) serveTCP(l net.Listener) error {
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
if srv.NotifyStartedFunc != nil {
|
|
||||||
srv.NotifyStartedFunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := Reader(&defaultReader{srv})
|
|
||||||
if srv.DecorateReader != nil {
|
|
||||||
reader = srv.DecorateReader(reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := srv.Handler
|
|
||||||
if handler == nil {
|
|
||||||
handler = DefaultServeMux
|
|
||||||
}
|
|
||||||
rtimeout := srv.getReadTimeout()
|
|
||||||
// deadline is not used here
|
|
||||||
for {
|
|
||||||
rw, err := l.Accept()
|
|
||||||
if err != nil {
|
|
||||||
if neterr, ok := err.(net.Error); ok && neterr.Temporary() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m, err := reader.ReadTCP(rw, rtimeout)
|
|
||||||
srv.lock.RLock()
|
|
||||||
if !srv.started {
|
|
||||||
srv.lock.RUnlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
srv.lock.RUnlock()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
srv.inFlight.Add(1)
|
|
||||||
go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// serveUDP starts a UDP listener for the server.
|
|
||||||
// Each request is handled in a separate goroutine.
|
|
||||||
func (srv *Server) serveUDP(l *net.UDPConn) error {
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
if srv.NotifyStartedFunc != nil {
|
|
||||||
srv.NotifyStartedFunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := Reader(&defaultReader{srv})
|
|
||||||
if srv.DecorateReader != nil {
|
|
||||||
reader = srv.DecorateReader(reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := srv.Handler
|
|
||||||
if handler == nil {
|
|
||||||
handler = DefaultServeMux
|
|
||||||
}
|
|
||||||
rtimeout := srv.getReadTimeout()
|
|
||||||
// deadline is not used here
|
|
||||||
for {
|
|
||||||
m, s, err := reader.ReadUDP(l, rtimeout)
|
|
||||||
srv.lock.RLock()
|
|
||||||
if !srv.started {
|
|
||||||
srv.lock.RUnlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
srv.lock.RUnlock()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
srv.inFlight.Add(1)
|
|
||||||
go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serve a new connection.
|
|
||||||
func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t net.Conn) {
|
|
||||||
defer srv.inFlight.Done()
|
|
||||||
|
|
||||||
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 // counter for the amount of TCP queries we get
|
|
||||||
|
|
||||||
reader := Reader(&defaultReader{srv})
|
|
||||||
if srv.DecorateReader != nil {
|
|
||||||
reader = srv.DecorateReader(reader)
|
|
||||||
}
|
|
||||||
Redo:
|
|
||||||
req := new(Msg)
|
|
||||||
err := req.Unpack(m)
|
|
||||||
if err != nil { // Send a FormatError back
|
|
||||||
x := new(Msg)
|
|
||||||
x.SetRcodeFormatError(req)
|
|
||||||
w.WriteMsg(x)
|
|
||||||
goto Exit
|
|
||||||
}
|
|
||||||
if !srv.Unsafe && req.Response {
|
|
||||||
goto Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
w.tsigStatus = nil
|
|
||||||
if w.tsigSecret != nil {
|
|
||||||
if t := req.IsTsig(); t != nil {
|
|
||||||
secret := t.Hdr.Name
|
|
||||||
if _, ok := w.tsigSecret[secret]; !ok {
|
|
||||||
w.tsigStatus = ErrKeyAlg
|
|
||||||
}
|
|
||||||
w.tsigStatus = TsigVerify(m, w.tsigSecret[secret], "", false)
|
|
||||||
w.tsigTimersOnly = false
|
|
||||||
w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.ServeDNS(w, req) // Writes back to the client
|
|
||||||
|
|
||||||
Exit:
|
|
||||||
if w.tcp == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO(miek): make this number configurable?
|
|
||||||
if q > maxTCPQueries { // close socket after this many queries
|
|
||||||
w.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.hijacked {
|
|
||||||
return // client calls Close()
|
|
||||||
}
|
|
||||||
if u != nil { // UDP, "close" and return
|
|
||||||
w.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
idleTimeout := tcpIdleTimeout
|
|
||||||
if srv.IdleTimeout != nil {
|
|
||||||
idleTimeout = srv.IdleTimeout()
|
|
||||||
}
|
|
||||||
m, err = reader.ReadTCP(w.tcp, idleTimeout)
|
|
||||||
if err == nil {
|
|
||||||
q++
|
|
||||||
goto Redo
|
|
||||||
}
|
|
||||||
w.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
|
|
||||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
|
||||||
l := make([]byte, 2)
|
|
||||||
n, err := conn.Read(l)
|
|
||||||
if err != nil || n != 2 {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return nil, ErrShortRead
|
|
||||||
}
|
|
||||||
length := binary.BigEndian.Uint16(l)
|
|
||||||
if length == 0 {
|
|
||||||
return nil, ErrShortRead
|
|
||||||
}
|
|
||||||
m := make([]byte, int(length))
|
|
||||||
n, err = conn.Read(m[:int(length)])
|
|
||||||
if err != nil || n == 0 {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return nil, ErrShortRead
|
|
||||||
}
|
|
||||||
i := n
|
|
||||||
for i < int(length) {
|
|
||||||
j, err := conn.Read(m[i:int(length)])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
i += j
|
|
||||||
}
|
|
||||||
n = i
|
|
||||||
m = m[:n]
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
|
||||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
|
||||||
m := make([]byte, srv.UDPSize)
|
|
||||||
n, s, err := ReadFromSessionUDP(conn, m)
|
|
||||||
if err != nil || n == 0 {
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return nil, nil, ErrShortRead
|
|
||||||
}
|
|
||||||
m = m[:n]
|
|
||||||
return m, s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteMsg implements the ResponseWriter.WriteMsg method.
|
|
||||||
func (w *response) WriteMsg(m *Msg) (err error) {
|
|
||||||
var data []byte
|
|
||||||
if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check)
|
|
||||||
if t := m.IsTsig(); t != nil {
|
|
||||||
data, w.tsigRequestMAC, err = TsigGenerate(m, w.tsigSecret[t.Hdr.Name], w.tsigRequestMAC, w.tsigTimersOnly)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = w.writer.Write(data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data, err = m.Pack()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = w.writer.Write(data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write implements the ResponseWriter.Write method.
|
|
||||||
func (w *response) Write(m []byte) (int, error) {
|
|
||||||
switch {
|
|
||||||
case w.udp != nil:
|
|
||||||
n, err := WriteToSessionUDP(w.udp, m, w.udpSession)
|
|
||||||
return n, err
|
|
||||||
case w.tcp != nil:
|
|
||||||
lm := len(m)
|
|
||||||
if lm < 2 {
|
|
||||||
return 0, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
if lm > MaxMsgSize {
|
|
||||||
return 0, &Error{err: "message too large"}
|
|
||||||
}
|
|
||||||
l := make([]byte, 2, 2+lm)
|
|
||||||
binary.BigEndian.PutUint16(l, uint16(lm))
|
|
||||||
m = append(l, m...)
|
|
||||||
|
|
||||||
n, err := io.Copy(w.tcp, bytes.NewReader(m))
|
|
||||||
return int(n), err
|
|
||||||
}
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalAddr implements the ResponseWriter.LocalAddr method.
|
|
||||||
func (w *response) LocalAddr() net.Addr {
|
|
||||||
if w.tcp != nil {
|
|
||||||
return w.tcp.LocalAddr()
|
|
||||||
}
|
|
||||||
return w.udp.LocalAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoteAddr implements the ResponseWriter.RemoteAddr method.
|
|
||||||
func (w *response) RemoteAddr() net.Addr { return w.remoteAddr }
|
|
||||||
|
|
||||||
// TsigStatus implements the ResponseWriter.TsigStatus method.
|
|
||||||
func (w *response) TsigStatus() error { return w.tsigStatus }
|
|
||||||
|
|
||||||
// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method.
|
|
||||||
func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b }
|
|
||||||
|
|
||||||
// Hijack implements the ResponseWriter.Hijack method.
|
|
||||||
func (w *response) Hijack() { w.hijacked = true }
|
|
||||||
|
|
||||||
// Close implements the ResponseWriter.Close method
|
|
||||||
func (w *response) Close() error {
|
|
||||||
// Can't close the udp conn, as that is actually the listener.
|
|
||||||
if w.tcp != nil {
|
|
||||||
e := w.tcp.Close()
|
|
||||||
w.tcp = nil
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
219
vendor/github.com/miekg/dns/sig0.go
generated
vendored
219
vendor/github.com/miekg/dns/sig0.go
generated
vendored
|
@ -1,219 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/dsa"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/binary"
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sign signs a dns.Msg. It fills the signature with the appropriate data.
|
|
||||||
// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
|
|
||||||
// and Expiration set.
|
|
||||||
func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
|
|
||||||
if k == nil {
|
|
||||||
return nil, ErrPrivKey
|
|
||||||
}
|
|
||||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
|
||||||
return nil, ErrKey
|
|
||||||
}
|
|
||||||
rr.Header().Rrtype = TypeSIG
|
|
||||||
rr.Header().Class = ClassANY
|
|
||||||
rr.Header().Ttl = 0
|
|
||||||
rr.Header().Name = "."
|
|
||||||
rr.OrigTtl = 0
|
|
||||||
rr.TypeCovered = 0
|
|
||||||
rr.Labels = 0
|
|
||||||
|
|
||||||
buf := make([]byte, m.Len()+rr.len())
|
|
||||||
mbuf, err := m.PackBuffer(buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if &buf[0] != &mbuf[0] {
|
|
||||||
return nil, ErrBuf
|
|
||||||
}
|
|
||||||
off, err := PackRR(rr, buf, len(mbuf), nil, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
buf = buf[:off:cap(buf)]
|
|
||||||
|
|
||||||
hash, ok := AlgorithmToHash[rr.Algorithm]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrAlg
|
|
||||||
}
|
|
||||||
|
|
||||||
hasher := hash.New()
|
|
||||||
// Write SIG rdata
|
|
||||||
hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
|
|
||||||
// Write message
|
|
||||||
hasher.Write(buf[:len(mbuf)])
|
|
||||||
|
|
||||||
signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rr.Signature = toBase64(signature)
|
|
||||||
sig := string(signature)
|
|
||||||
|
|
||||||
buf = append(buf, sig...)
|
|
||||||
if len(buf) > int(^uint16(0)) {
|
|
||||||
return nil, ErrBuf
|
|
||||||
}
|
|
||||||
// Adjust sig data length
|
|
||||||
rdoff := len(mbuf) + 1 + 2 + 2 + 4
|
|
||||||
rdlen := binary.BigEndian.Uint16(buf[rdoff:])
|
|
||||||
rdlen += uint16(len(sig))
|
|
||||||
binary.BigEndian.PutUint16(buf[rdoff:], rdlen)
|
|
||||||
// Adjust additional count
|
|
||||||
adc := binary.BigEndian.Uint16(buf[10:])
|
|
||||||
adc++
|
|
||||||
binary.BigEndian.PutUint16(buf[10:], adc)
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify validates the message buf using the key k.
|
|
||||||
// It's assumed that buf is a valid message from which rr was unpacked.
|
|
||||||
func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
|
||||||
if k == nil {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
|
||||||
return ErrKey
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash crypto.Hash
|
|
||||||
switch rr.Algorithm {
|
|
||||||
case DSA, RSASHA1:
|
|
||||||
hash = crypto.SHA1
|
|
||||||
case RSASHA256, ECDSAP256SHA256:
|
|
||||||
hash = crypto.SHA256
|
|
||||||
case ECDSAP384SHA384:
|
|
||||||
hash = crypto.SHA384
|
|
||||||
case RSASHA512:
|
|
||||||
hash = crypto.SHA512
|
|
||||||
default:
|
|
||||||
return ErrAlg
|
|
||||||
}
|
|
||||||
hasher := hash.New()
|
|
||||||
|
|
||||||
buflen := len(buf)
|
|
||||||
qdc := binary.BigEndian.Uint16(buf[4:])
|
|
||||||
anc := binary.BigEndian.Uint16(buf[6:])
|
|
||||||
auc := binary.BigEndian.Uint16(buf[8:])
|
|
||||||
adc := binary.BigEndian.Uint16(buf[10:])
|
|
||||||
offset := 12
|
|
||||||
var err error
|
|
||||||
for i := uint16(0); i < qdc && offset < buflen; i++ {
|
|
||||||
_, offset, err = UnpackDomainName(buf, offset)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Skip past Type and Class
|
|
||||||
offset += 2 + 2
|
|
||||||
}
|
|
||||||
for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
|
|
||||||
_, offset, err = UnpackDomainName(buf, offset)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Skip past Type, Class and TTL
|
|
||||||
offset += 2 + 2 + 4
|
|
||||||
if offset+1 >= buflen {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var rdlen uint16
|
|
||||||
rdlen = binary.BigEndian.Uint16(buf[offset:])
|
|
||||||
offset += 2
|
|
||||||
offset += int(rdlen)
|
|
||||||
}
|
|
||||||
if offset >= buflen {
|
|
||||||
return &Error{err: "overflowing unpacking signed message"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// offset should be just prior to SIG
|
|
||||||
bodyend := offset
|
|
||||||
// owner name SHOULD be root
|
|
||||||
_, offset, err = UnpackDomainName(buf, offset)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Skip Type, Class, TTL, RDLen
|
|
||||||
offset += 2 + 2 + 4 + 2
|
|
||||||
sigstart := offset
|
|
||||||
// Skip Type Covered, Algorithm, Labels, Original TTL
|
|
||||||
offset += 2 + 1 + 1 + 4
|
|
||||||
if offset+4+4 >= buflen {
|
|
||||||
return &Error{err: "overflow unpacking signed message"}
|
|
||||||
}
|
|
||||||
expire := binary.BigEndian.Uint32(buf[offset:])
|
|
||||||
offset += 4
|
|
||||||
incept := binary.BigEndian.Uint32(buf[offset:])
|
|
||||||
offset += 4
|
|
||||||
now := uint32(time.Now().Unix())
|
|
||||||
if now < incept || now > expire {
|
|
||||||
return ErrTime
|
|
||||||
}
|
|
||||||
// Skip key tag
|
|
||||||
offset += 2
|
|
||||||
var signername string
|
|
||||||
signername, offset, err = UnpackDomainName(buf, offset)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// If key has come from the DNS name compression might
|
|
||||||
// have mangled the case of the name
|
|
||||||
if strings.ToLower(signername) != strings.ToLower(k.Header().Name) {
|
|
||||||
return &Error{err: "signer name doesn't match key name"}
|
|
||||||
}
|
|
||||||
sigend := offset
|
|
||||||
hasher.Write(buf[sigstart:sigend])
|
|
||||||
hasher.Write(buf[:10])
|
|
||||||
hasher.Write([]byte{
|
|
||||||
byte((adc - 1) << 8),
|
|
||||||
byte(adc - 1),
|
|
||||||
})
|
|
||||||
hasher.Write(buf[12:bodyend])
|
|
||||||
|
|
||||||
hashed := hasher.Sum(nil)
|
|
||||||
sig := buf[sigend:]
|
|
||||||
switch k.Algorithm {
|
|
||||||
case DSA:
|
|
||||||
pk := k.publicKeyDSA()
|
|
||||||
sig = sig[1:]
|
|
||||||
r := big.NewInt(0)
|
|
||||||
r.SetBytes(sig[:len(sig)/2])
|
|
||||||
s := big.NewInt(0)
|
|
||||||
s.SetBytes(sig[len(sig)/2:])
|
|
||||||
if pk != nil {
|
|
||||||
if dsa.Verify(pk, hashed, r, s) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ErrSig
|
|
||||||
}
|
|
||||||
case RSASHA1, RSASHA256, RSASHA512:
|
|
||||||
pk := k.publicKeyRSA()
|
|
||||||
if pk != nil {
|
|
||||||
return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
|
|
||||||
}
|
|
||||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
|
||||||
pk := k.publicKeyECDSA()
|
|
||||||
r := big.NewInt(0)
|
|
||||||
r.SetBytes(sig[:len(sig)/2])
|
|
||||||
s := big.NewInt(0)
|
|
||||||
s.SetBytes(sig[len(sig)/2:])
|
|
||||||
if pk != nil {
|
|
||||||
if ecdsa.Verify(pk, hashed, r, s) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ErrSig
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ErrKeyAlg
|
|
||||||
}
|
|
57
vendor/github.com/miekg/dns/singleinflight.go
generated
vendored
57
vendor/github.com/miekg/dns/singleinflight.go
generated
vendored
|
@ -1,57 +0,0 @@
|
||||||
// Copyright 2013 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Adapted for dns package usage by Miek Gieben.
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// call is an in-flight or completed singleflight.Do call
|
|
||||||
type call struct {
|
|
||||||
wg sync.WaitGroup
|
|
||||||
val *Msg
|
|
||||||
rtt time.Duration
|
|
||||||
err error
|
|
||||||
dups int
|
|
||||||
}
|
|
||||||
|
|
||||||
// singleflight represents a class of work and forms a namespace in
|
|
||||||
// which units of work can be executed with duplicate suppression.
|
|
||||||
type singleflight struct {
|
|
||||||
sync.Mutex // protects m
|
|
||||||
m map[string]*call // lazily initialized
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do executes and returns the results of the given function, making
|
|
||||||
// sure that only one execution is in-flight for a given key at a
|
|
||||||
// time. If a duplicate comes in, the duplicate caller waits for the
|
|
||||||
// original to complete and receives the same results.
|
|
||||||
// The return value shared indicates whether v was given to multiple callers.
|
|
||||||
func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) {
|
|
||||||
g.Lock()
|
|
||||||
if g.m == nil {
|
|
||||||
g.m = make(map[string]*call)
|
|
||||||
}
|
|
||||||
if c, ok := g.m[key]; ok {
|
|
||||||
c.dups++
|
|
||||||
g.Unlock()
|
|
||||||
c.wg.Wait()
|
|
||||||
return c.val, c.rtt, c.err, true
|
|
||||||
}
|
|
||||||
c := new(call)
|
|
||||||
c.wg.Add(1)
|
|
||||||
g.m[key] = c
|
|
||||||
g.Unlock()
|
|
||||||
|
|
||||||
c.val, c.rtt, c.err = fn()
|
|
||||||
c.wg.Done()
|
|
||||||
|
|
||||||
g.Lock()
|
|
||||||
delete(g.m, key)
|
|
||||||
g.Unlock()
|
|
||||||
|
|
||||||
return c.val, c.rtt, c.err, c.dups > 0
|
|
||||||
}
|
|
47
vendor/github.com/miekg/dns/smimea.go
generated
vendored
47
vendor/github.com/miekg/dns/smimea.go
generated
vendored
|
@ -1,47 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/hex"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sign creates a SMIMEA record from an SSL certificate.
|
|
||||||
func (r *SMIMEA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
|
|
||||||
r.Hdr.Rrtype = TypeSMIMEA
|
|
||||||
r.Usage = uint8(usage)
|
|
||||||
r.Selector = uint8(selector)
|
|
||||||
r.MatchingType = uint8(matchingType)
|
|
||||||
|
|
||||||
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify verifies a SMIMEA record against an SSL certificate. If it is OK
|
|
||||||
// a nil error is returned.
|
|
||||||
func (r *SMIMEA) Verify(cert *x509.Certificate) error {
|
|
||||||
c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
|
|
||||||
if err != nil {
|
|
||||||
return err // Not also ErrSig?
|
|
||||||
}
|
|
||||||
if r.Certificate == c {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ErrSig // ErrSig, really?
|
|
||||||
}
|
|
||||||
|
|
||||||
// SIMEAName returns the ownername of a SMIMEA resource record as per the
|
|
||||||
// format specified in RFC 'draft-ietf-dane-smime-12' Section 2 and 3
|
|
||||||
func SMIMEAName(email_address string, domain_name string) (string, error) {
|
|
||||||
hasher := sha256.New()
|
|
||||||
hasher.Write([]byte(email_address))
|
|
||||||
|
|
||||||
// RFC Section 3: "The local-part is hashed using the SHA2-256
|
|
||||||
// algorithm with the hash truncated to 28 octets and
|
|
||||||
// represented in its hexadecimal representation to become the
|
|
||||||
// left-most label in the prepared domain name"
|
|
||||||
return hex.EncodeToString(hasher.Sum(nil)[:28]) + "." + "_smimecert." + domain_name, nil
|
|
||||||
}
|
|
47
vendor/github.com/miekg/dns/tlsa.go
generated
vendored
47
vendor/github.com/miekg/dns/tlsa.go
generated
vendored
|
@ -1,47 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/x509"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sign creates a TLSA record from an SSL certificate.
|
|
||||||
func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
|
|
||||||
r.Hdr.Rrtype = TypeTLSA
|
|
||||||
r.Usage = uint8(usage)
|
|
||||||
r.Selector = uint8(selector)
|
|
||||||
r.MatchingType = uint8(matchingType)
|
|
||||||
|
|
||||||
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify verifies a TLSA record against an SSL certificate. If it is OK
|
|
||||||
// a nil error is returned.
|
|
||||||
func (r *TLSA) Verify(cert *x509.Certificate) error {
|
|
||||||
c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
|
|
||||||
if err != nil {
|
|
||||||
return err // Not also ErrSig?
|
|
||||||
}
|
|
||||||
if r.Certificate == c {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ErrSig // ErrSig, really?
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLSAName returns the ownername of a TLSA resource record as per the
|
|
||||||
// rules specified in RFC 6698, Section 3.
|
|
||||||
func TLSAName(name, service, network string) (string, error) {
|
|
||||||
if !IsFqdn(name) {
|
|
||||||
return "", ErrFqdn
|
|
||||||
}
|
|
||||||
p, err := net.LookupPort(network, service)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil
|
|
||||||
}
|
|
384
vendor/github.com/miekg/dns/tsig.go
generated
vendored
384
vendor/github.com/miekg/dns/tsig.go
generated
vendored
|
@ -1,384 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HMAC hashing codes. These are transmitted as domain names.
|
|
||||||
const (
|
|
||||||
HmacMD5 = "hmac-md5.sig-alg.reg.int."
|
|
||||||
HmacSHA1 = "hmac-sha1."
|
|
||||||
HmacSHA256 = "hmac-sha256."
|
|
||||||
HmacSHA512 = "hmac-sha512."
|
|
||||||
)
|
|
||||||
|
|
||||||
// TSIG is the RR the holds the transaction signature of a message.
|
|
||||||
// See RFC 2845 and RFC 4635.
|
|
||||||
type TSIG struct {
|
|
||||||
Hdr RR_Header
|
|
||||||
Algorithm string `dns:"domain-name"`
|
|
||||||
TimeSigned uint64 `dns:"uint48"`
|
|
||||||
Fudge uint16
|
|
||||||
MACSize uint16
|
|
||||||
MAC string `dns:"size-hex:MACSize"`
|
|
||||||
OrigId uint16
|
|
||||||
Error uint16
|
|
||||||
OtherLen uint16
|
|
||||||
OtherData string `dns:"size-hex:OtherLen"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TSIG has no official presentation format, but this will suffice.
|
|
||||||
|
|
||||||
func (rr *TSIG) String() string {
|
|
||||||
s := "\n;; TSIG PSEUDOSECTION:\n"
|
|
||||||
s += rr.Hdr.String() +
|
|
||||||
" " + rr.Algorithm +
|
|
||||||
" " + tsigTimeToString(rr.TimeSigned) +
|
|
||||||
" " + strconv.Itoa(int(rr.Fudge)) +
|
|
||||||
" " + strconv.Itoa(int(rr.MACSize)) +
|
|
||||||
" " + strings.ToUpper(rr.MAC) +
|
|
||||||
" " + strconv.Itoa(int(rr.OrigId)) +
|
|
||||||
" " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
|
|
||||||
" " + strconv.Itoa(int(rr.OtherLen)) +
|
|
||||||
" " + rr.OtherData
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following values must be put in wireformat, so that the MAC can be calculated.
|
|
||||||
// RFC 2845, section 3.4.2. TSIG Variables.
|
|
||||||
type tsigWireFmt struct {
|
|
||||||
// From RR_Header
|
|
||||||
Name string `dns:"domain-name"`
|
|
||||||
Class uint16
|
|
||||||
Ttl uint32
|
|
||||||
// Rdata of the TSIG
|
|
||||||
Algorithm string `dns:"domain-name"`
|
|
||||||
TimeSigned uint64 `dns:"uint48"`
|
|
||||||
Fudge uint16
|
|
||||||
// MACSize, MAC and OrigId excluded
|
|
||||||
Error uint16
|
|
||||||
OtherLen uint16
|
|
||||||
OtherData string `dns:"size-hex:OtherLen"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC
|
|
||||||
type macWireFmt struct {
|
|
||||||
MACSize uint16
|
|
||||||
MAC string `dns:"size-hex:MACSize"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.3. Time values used in TSIG calculations
|
|
||||||
type timerWireFmt struct {
|
|
||||||
TimeSigned uint64 `dns:"uint48"`
|
|
||||||
Fudge uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// TsigGenerate fills out the TSIG record attached to the message.
|
|
||||||
// The message should contain
|
|
||||||
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
|
|
||||||
// time fudge (defaults to 300 seconds) and the current time
|
|
||||||
// The TSIG MAC is saved in that Tsig RR.
|
|
||||||
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
|
|
||||||
// timersOnly is false.
|
|
||||||
// If something goes wrong an error is returned, otherwise it is nil.
|
|
||||||
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
|
|
||||||
if m.IsTsig() == nil {
|
|
||||||
panic("dns: TSIG not last RR in additional")
|
|
||||||
}
|
|
||||||
// If we barf here, the caller is to blame
|
|
||||||
rawsecret, err := fromBase64([]byte(secret))
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
rr := m.Extra[len(m.Extra)-1].(*TSIG)
|
|
||||||
m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
|
|
||||||
mbuf, err := m.Pack()
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
|
|
||||||
|
|
||||||
t := new(TSIG)
|
|
||||||
var h hash.Hash
|
|
||||||
switch strings.ToLower(rr.Algorithm) {
|
|
||||||
case HmacMD5:
|
|
||||||
h = hmac.New(md5.New, []byte(rawsecret))
|
|
||||||
case HmacSHA1:
|
|
||||||
h = hmac.New(sha1.New, []byte(rawsecret))
|
|
||||||
case HmacSHA256:
|
|
||||||
h = hmac.New(sha256.New, []byte(rawsecret))
|
|
||||||
case HmacSHA512:
|
|
||||||
h = hmac.New(sha512.New, []byte(rawsecret))
|
|
||||||
default:
|
|
||||||
return nil, "", ErrKeyAlg
|
|
||||||
}
|
|
||||||
io.WriteString(h, string(buf))
|
|
||||||
t.MAC = hex.EncodeToString(h.Sum(nil))
|
|
||||||
t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
|
|
||||||
|
|
||||||
t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
|
|
||||||
t.Fudge = rr.Fudge
|
|
||||||
t.TimeSigned = rr.TimeSigned
|
|
||||||
t.Algorithm = rr.Algorithm
|
|
||||||
t.OrigId = m.Id
|
|
||||||
|
|
||||||
tbuf := make([]byte, t.len())
|
|
||||||
if off, err := PackRR(t, tbuf, 0, nil, false); err == nil {
|
|
||||||
tbuf = tbuf[:off] // reset to actual size used
|
|
||||||
} else {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
mbuf = append(mbuf, tbuf...)
|
|
||||||
// Update the ArCount directly in the buffer.
|
|
||||||
binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))
|
|
||||||
|
|
||||||
return mbuf, t.MAC, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TsigVerify verifies the TSIG on a message.
|
|
||||||
// If the signature does not validate err contains the
|
|
||||||
// error, otherwise it is nil.
|
|
||||||
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
|
|
||||||
rawsecret, err := fromBase64([]byte(secret))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Strip the TSIG from the incoming msg
|
|
||||||
stripped, tsig, err := stripTsig(msg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msgMAC, err := hex.DecodeString(tsig.MAC)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
|
|
||||||
|
|
||||||
// Fudge factor works both ways. A message can arrive before it was signed because
|
|
||||||
// of clock skew.
|
|
||||||
now := uint64(time.Now().Unix())
|
|
||||||
ti := now - tsig.TimeSigned
|
|
||||||
if now < tsig.TimeSigned {
|
|
||||||
ti = tsig.TimeSigned - now
|
|
||||||
}
|
|
||||||
if uint64(tsig.Fudge) < ti {
|
|
||||||
return ErrTime
|
|
||||||
}
|
|
||||||
|
|
||||||
var h hash.Hash
|
|
||||||
switch strings.ToLower(tsig.Algorithm) {
|
|
||||||
case HmacMD5:
|
|
||||||
h = hmac.New(md5.New, rawsecret)
|
|
||||||
case HmacSHA1:
|
|
||||||
h = hmac.New(sha1.New, rawsecret)
|
|
||||||
case HmacSHA256:
|
|
||||||
h = hmac.New(sha256.New, rawsecret)
|
|
||||||
case HmacSHA512:
|
|
||||||
h = hmac.New(sha512.New, rawsecret)
|
|
||||||
default:
|
|
||||||
return ErrKeyAlg
|
|
||||||
}
|
|
||||||
h.Write(buf)
|
|
||||||
if !hmac.Equal(h.Sum(nil), msgMAC) {
|
|
||||||
return ErrSig
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a wiredata buffer for the MAC calculation.
|
|
||||||
func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte {
|
|
||||||
var buf []byte
|
|
||||||
if rr.TimeSigned == 0 {
|
|
||||||
rr.TimeSigned = uint64(time.Now().Unix())
|
|
||||||
}
|
|
||||||
if rr.Fudge == 0 {
|
|
||||||
rr.Fudge = 300 // Standard (RFC) default.
|
|
||||||
}
|
|
||||||
|
|
||||||
if requestMAC != "" {
|
|
||||||
m := new(macWireFmt)
|
|
||||||
m.MACSize = uint16(len(requestMAC) / 2)
|
|
||||||
m.MAC = requestMAC
|
|
||||||
buf = make([]byte, len(requestMAC)) // long enough
|
|
||||||
n, _ := packMacWire(m, buf)
|
|
||||||
buf = buf[:n]
|
|
||||||
}
|
|
||||||
|
|
||||||
tsigvar := make([]byte, DefaultMsgSize)
|
|
||||||
if timersOnly {
|
|
||||||
tsig := new(timerWireFmt)
|
|
||||||
tsig.TimeSigned = rr.TimeSigned
|
|
||||||
tsig.Fudge = rr.Fudge
|
|
||||||
n, _ := packTimerWire(tsig, tsigvar)
|
|
||||||
tsigvar = tsigvar[:n]
|
|
||||||
} else {
|
|
||||||
tsig := new(tsigWireFmt)
|
|
||||||
tsig.Name = strings.ToLower(rr.Hdr.Name)
|
|
||||||
tsig.Class = ClassANY
|
|
||||||
tsig.Ttl = rr.Hdr.Ttl
|
|
||||||
tsig.Algorithm = strings.ToLower(rr.Algorithm)
|
|
||||||
tsig.TimeSigned = rr.TimeSigned
|
|
||||||
tsig.Fudge = rr.Fudge
|
|
||||||
tsig.Error = rr.Error
|
|
||||||
tsig.OtherLen = rr.OtherLen
|
|
||||||
tsig.OtherData = rr.OtherData
|
|
||||||
n, _ := packTsigWire(tsig, tsigvar)
|
|
||||||
tsigvar = tsigvar[:n]
|
|
||||||
}
|
|
||||||
|
|
||||||
if requestMAC != "" {
|
|
||||||
x := append(buf, msgbuf...)
|
|
||||||
buf = append(x, tsigvar...)
|
|
||||||
} else {
|
|
||||||
buf = append(msgbuf, tsigvar...)
|
|
||||||
}
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip the TSIG from the raw message.
|
|
||||||
func stripTsig(msg []byte) ([]byte, *TSIG, error) {
|
|
||||||
// Copied from msg.go's Unpack() Header, but modified.
|
|
||||||
var (
|
|
||||||
dh Header
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
off, tsigoff := 0, 0
|
|
||||||
|
|
||||||
if dh, off, err = unpackMsgHdr(msg, off); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if dh.Arcount == 0 {
|
|
||||||
return nil, nil, ErrNoSig
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rcode, see msg.go Unpack()
|
|
||||||
if int(dh.Bits&0xF) == RcodeNotAuth {
|
|
||||||
return nil, nil, ErrAuth
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < int(dh.Qdcount); i++ {
|
|
||||||
_, off, err = unpackQuestion(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, off, err = unpackRRslice(int(dh.Ancount), msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
_, off, err = unpackRRslice(int(dh.Nscount), msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rr := new(TSIG)
|
|
||||||
var extra RR
|
|
||||||
for i := 0; i < int(dh.Arcount); i++ {
|
|
||||||
tsigoff = off
|
|
||||||
extra, off, err = UnpackRR(msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if extra.Header().Rrtype == TypeTSIG {
|
|
||||||
rr = extra.(*TSIG)
|
|
||||||
// Adjust Arcount.
|
|
||||||
arcount := binary.BigEndian.Uint16(msg[10:])
|
|
||||||
binary.BigEndian.PutUint16(msg[10:], arcount-1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rr == nil {
|
|
||||||
return nil, nil, ErrNoSig
|
|
||||||
}
|
|
||||||
return msg[:tsigoff], rr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate the TSIG time signed into a date. There is no
|
|
||||||
// need for RFC1982 calculations as this date is 48 bits.
|
|
||||||
func tsigTimeToString(t uint64) string {
|
|
||||||
ti := time.Unix(int64(t), 0).UTC()
|
|
||||||
return ti.Format("20060102150405")
|
|
||||||
}
|
|
||||||
|
|
||||||
func packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) {
|
|
||||||
// copied from zmsg.go TSIG packing
|
|
||||||
// RR_Header
|
|
||||||
off, err := PackDomainName(tw.Name, msg, 0, nil, false)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint16(tw.Class, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint32(tw.Ttl, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
|
|
||||||
off, err = PackDomainName(tw.Algorithm, msg, off, nil, false)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint48(tw.TimeSigned, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint16(tw.Fudge, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
|
|
||||||
off, err = packUint16(tw.Error, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint16(tw.OtherLen, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packStringHex(tw.OtherData, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packMacWire(mw *macWireFmt, msg []byte) (int, error) {
|
|
||||||
off, err := packUint16(mw.MACSize, msg, 0)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packStringHex(mw.MAC, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packTimerWire(tw *timerWireFmt, msg []byte) (int, error) {
|
|
||||||
off, err := packUint48(tw.TimeSigned, msg, 0)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
off, err = packUint16(tw.Fudge, msg, off)
|
|
||||||
if err != nil {
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
return off, nil
|
|
||||||
}
|
|
1294
vendor/github.com/miekg/dns/types.go
generated
vendored
1294
vendor/github.com/miekg/dns/types.go
generated
vendored
File diff suppressed because it is too large
Load diff
58
vendor/github.com/miekg/dns/udp.go
generated
vendored
58
vendor/github.com/miekg/dns/udp.go
generated
vendored
|
@ -1,58 +0,0 @@
|
||||||
// +build !windows,!plan9
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SessionUDP holds the remote address and the associated
|
|
||||||
// out-of-band data.
|
|
||||||
type SessionUDP struct {
|
|
||||||
raddr *net.UDPAddr
|
|
||||||
context []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoteAddr returns the remote network address.
|
|
||||||
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
|
||||||
|
|
||||||
// setUDPSocketOptions sets the UDP socket options.
|
|
||||||
// This function is implemented on a per platform basis. See udp_*.go for more details
|
|
||||||
func setUDPSocketOptions(conn *net.UDPConn) error {
|
|
||||||
sa, err := getUDPSocketName(conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch sa.(type) {
|
|
||||||
case *syscall.SockaddrInet6:
|
|
||||||
v6only, err := getUDPSocketOptions6Only(conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
setUDPSocketOptions6(conn)
|
|
||||||
if !v6only {
|
|
||||||
setUDPSocketOptions4(conn)
|
|
||||||
}
|
|
||||||
case *syscall.SockaddrInet4:
|
|
||||||
setUDPSocketOptions4(conn)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
|
||||||
// net.UDPAddr.
|
|
||||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
|
||||||
oob := make([]byte, 40)
|
|
||||||
n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
|
|
||||||
if err != nil {
|
|
||||||
return n, nil, err
|
|
||||||
}
|
|
||||||
return n, &SessionUDP{raddr, oob[:oobn]}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr.
|
|
||||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
|
||||||
n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr)
|
|
||||||
return n, err
|
|
||||||
}
|
|
73
vendor/github.com/miekg/dns/udp_linux.go
generated
vendored
73
vendor/github.com/miekg/dns/udp_linux.go
generated
vendored
|
@ -1,73 +0,0 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
// See:
|
|
||||||
// * http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket and
|
|
||||||
// * http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/
|
|
||||||
//
|
|
||||||
// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outgoing
|
|
||||||
// interface, this might not always be the correct one. This code will make sure the egress
|
|
||||||
// packet's interface matched the ingress' one.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// setUDPSocketOptions4 prepares the v4 socket for sessions.
|
|
||||||
func setUDPSocketOptions4(conn *net.UDPConn) error {
|
|
||||||
file, err := conn.File()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Calling File() above results in the connection becoming blocking, we must fix that.
|
|
||||||
// See https://github.com/miekg/dns/issues/279
|
|
||||||
err = syscall.SetNonblock(int(file.Fd()), true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setUDPSocketOptions6 prepares the v6 socket for sessions.
|
|
||||||
func setUDPSocketOptions6(conn *net.UDPConn) error {
|
|
||||||
file, err := conn.File()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = syscall.SetNonblock(int(file.Fd()), true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getUDPSocketOption6Only return true if the socket is v6 only and false when it is v4/v6 combined
|
|
||||||
// (dualstack).
|
|
||||||
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
|
|
||||||
file, err := conn.File()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
// dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
|
|
||||||
v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return v6only == 1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
|
|
||||||
file, err := conn.File()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return syscall.Getsockname(int(file.Fd()))
|
|
||||||
}
|
|
17
vendor/github.com/miekg/dns/udp_other.go
generated
vendored
17
vendor/github.com/miekg/dns/udp_other.go
generated
vendored
|
@ -1,17 +0,0 @@
|
||||||
// +build !linux,!plan9
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// These do nothing. See udp_linux.go for an example of how to implement this.
|
|
||||||
|
|
||||||
// We tried to adhire to some kind of naming scheme.
|
|
||||||
|
|
||||||
func setUDPSocketOptions4(conn *net.UDPConn) error { return nil }
|
|
||||||
func setUDPSocketOptions6(conn *net.UDPConn) error { return nil }
|
|
||||||
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) { return false, nil }
|
|
||||||
func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) { return nil, nil }
|
|
34
vendor/github.com/miekg/dns/udp_plan9.go
generated
vendored
34
vendor/github.com/miekg/dns/udp_plan9.go
generated
vendored
|
@ -1,34 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setUDPSocketOptions(conn *net.UDPConn) error { return nil }
|
|
||||||
|
|
||||||
// SessionUDP holds the remote address and the associated
|
|
||||||
// out-of-band data.
|
|
||||||
type SessionUDP struct {
|
|
||||||
raddr *net.UDPAddr
|
|
||||||
context []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoteAddr returns the remote network address.
|
|
||||||
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
|
||||||
|
|
||||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
|
||||||
// net.UDPAddr.
|
|
||||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
|
||||||
oob := make([]byte, 40)
|
|
||||||
n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
|
|
||||||
if err != nil {
|
|
||||||
return n, nil, err
|
|
||||||
}
|
|
||||||
return n, &SessionUDP{raddr, oob[:oobn]}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr.
|
|
||||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
|
||||||
n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr)
|
|
||||||
return n, err
|
|
||||||
}
|
|
34
vendor/github.com/miekg/dns/udp_windows.go
generated
vendored
34
vendor/github.com/miekg/dns/udp_windows.go
generated
vendored
|
@ -1,34 +0,0 @@
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
type SessionUDP struct {
|
|
||||||
raddr *net.UDPAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
|
||||||
// net.UDPAddr.
|
|
||||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
|
||||||
n, raddr, err := conn.ReadFrom(b)
|
|
||||||
if err != nil {
|
|
||||||
return n, nil, err
|
|
||||||
}
|
|
||||||
session := &SessionUDP{raddr.(*net.UDPAddr)}
|
|
||||||
return n, session, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr.
|
|
||||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
|
||||||
n, err := conn.WriteTo(b, session.raddr)
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
|
||||||
|
|
||||||
// setUDPSocketOptions sets the UDP socket options.
|
|
||||||
// This function is implemented on a per platform basis. See udp_*.go for more details
|
|
||||||
func setUDPSocketOptions(conn *net.UDPConn) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
106
vendor/github.com/miekg/dns/update.go
generated
vendored
106
vendor/github.com/miekg/dns/update.go
generated
vendored
|
@ -1,106 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
// NameUsed sets the RRs in the prereq section to
|
|
||||||
// "Name is in use" RRs. RFC 2136 section 2.4.4.
|
|
||||||
func (u *Msg) NameUsed(rr []RR) {
|
|
||||||
if u.Answer == nil {
|
|
||||||
u.Answer = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NameNotUsed sets the RRs in the prereq section to
|
|
||||||
// "Name is in not use" RRs. RFC 2136 section 2.4.5.
|
|
||||||
func (u *Msg) NameNotUsed(rr []RR) {
|
|
||||||
if u.Answer == nil {
|
|
||||||
u.Answer = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used sets the RRs in the prereq section to
|
|
||||||
// "RRset exists (value dependent -- with rdata)" RRs. RFC 2136 section 2.4.2.
|
|
||||||
func (u *Msg) Used(rr []RR) {
|
|
||||||
if len(u.Question) == 0 {
|
|
||||||
panic("dns: empty question section")
|
|
||||||
}
|
|
||||||
if u.Answer == nil {
|
|
||||||
u.Answer = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
r.Header().Class = u.Question[0].Qclass
|
|
||||||
u.Answer = append(u.Answer, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RRsetUsed sets the RRs in the prereq section to
|
|
||||||
// "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1.
|
|
||||||
func (u *Msg) RRsetUsed(rr []RR) {
|
|
||||||
if u.Answer == nil {
|
|
||||||
u.Answer = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RRsetNotUsed sets the RRs in the prereq section to
|
|
||||||
// "RRset does not exist" RRs. RFC 2136 section 2.4.3.
|
|
||||||
func (u *Msg) RRsetNotUsed(rr []RR) {
|
|
||||||
if u.Answer == nil {
|
|
||||||
u.Answer = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassNONE}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1.
|
|
||||||
func (u *Msg) Insert(rr []RR) {
|
|
||||||
if len(u.Question) == 0 {
|
|
||||||
panic("dns: empty question section")
|
|
||||||
}
|
|
||||||
if u.Ns == nil {
|
|
||||||
u.Ns = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
r.Header().Class = u.Question[0].Qclass
|
|
||||||
u.Ns = append(u.Ns, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2.
|
|
||||||
func (u *Msg) RemoveRRset(rr []RR) {
|
|
||||||
if u.Ns == nil {
|
|
||||||
u.Ns = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3
|
|
||||||
func (u *Msg) RemoveName(rr []RR) {
|
|
||||||
if u.Ns == nil {
|
|
||||||
u.Ns = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4
|
|
||||||
func (u *Msg) Remove(rr []RR) {
|
|
||||||
if u.Ns == nil {
|
|
||||||
u.Ns = make([]RR, 0, len(rr))
|
|
||||||
}
|
|
||||||
for _, r := range rr {
|
|
||||||
r.Header().Class = ClassNONE
|
|
||||||
r.Header().Ttl = 0
|
|
||||||
u.Ns = append(u.Ns, r)
|
|
||||||
}
|
|
||||||
}
|
|
244
vendor/github.com/miekg/dns/xfr.go
generated
vendored
244
vendor/github.com/miekg/dns/xfr.go
generated
vendored
|
@ -1,244 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Envelope is used when doing a zone transfer with a remote server.
|
|
||||||
type Envelope struct {
|
|
||||||
RR []RR // The set of RRs in the answer section of the xfr reply message.
|
|
||||||
Error error // If something went wrong, this contains the error.
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Transfer defines parameters that are used during a zone transfer.
|
|
||||||
type Transfer struct {
|
|
||||||
*Conn
|
|
||||||
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds
|
|
||||||
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
|
|
||||||
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
|
|
||||||
TsigSecret map[string]string // Secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
|
|
||||||
tsigTimersOnly bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Think we need to away to stop the transfer
|
|
||||||
|
|
||||||
// In performs an incoming transfer with the server in a.
|
|
||||||
// If you would like to set the source IP, or some other attribute
|
|
||||||
// of a Dialer for a Transfer, you can do so by specifying the attributes
|
|
||||||
// in the Transfer.Conn:
|
|
||||||
//
|
|
||||||
// d := net.Dialer{LocalAddr: transfer_source}
|
|
||||||
// con, err := d.Dial("tcp", master)
|
|
||||||
// dnscon := &dns.Conn{Conn:con}
|
|
||||||
// transfer = &dns.Transfer{Conn: dnscon}
|
|
||||||
// channel, err := transfer.In(message, master)
|
|
||||||
//
|
|
||||||
func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
|
|
||||||
timeout := dnsTimeout
|
|
||||||
if t.DialTimeout != 0 {
|
|
||||||
timeout = t.DialTimeout
|
|
||||||
}
|
|
||||||
if t.Conn == nil {
|
|
||||||
t.Conn, err = DialTimeout("tcp", a, timeout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := t.WriteMsg(q); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
env = make(chan *Envelope)
|
|
||||||
go func() {
|
|
||||||
if q.Question[0].Qtype == TypeAXFR {
|
|
||||||
go t.inAxfr(q.Id, env)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if q.Question[0].Qtype == TypeIXFR {
|
|
||||||
go t.inIxfr(q.Id, env)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return env, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Transfer) inAxfr(id uint16, c chan *Envelope) {
|
|
||||||
first := true
|
|
||||||
defer t.Close()
|
|
||||||
defer close(c)
|
|
||||||
timeout := dnsTimeout
|
|
||||||
if t.ReadTimeout != 0 {
|
|
||||||
timeout = t.ReadTimeout
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
t.Conn.SetReadDeadline(time.Now().Add(timeout))
|
|
||||||
in, err := t.ReadMsg()
|
|
||||||
if err != nil {
|
|
||||||
c <- &Envelope{nil, err}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if id != in.Id {
|
|
||||||
c <- &Envelope{in.Answer, ErrId}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if first {
|
|
||||||
if !isSOAFirst(in) {
|
|
||||||
c <- &Envelope{in.Answer, ErrSoa}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
first = !first
|
|
||||||
// only one answer that is SOA, receive more
|
|
||||||
if len(in.Answer) == 1 {
|
|
||||||
t.tsigTimersOnly = true
|
|
||||||
c <- &Envelope{in.Answer, nil}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !first {
|
|
||||||
t.tsigTimersOnly = true // Subsequent envelopes use this.
|
|
||||||
if isSOALast(in) {
|
|
||||||
c <- &Envelope{in.Answer, nil}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c <- &Envelope{in.Answer, nil}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Transfer) inIxfr(id uint16, c chan *Envelope) {
|
|
||||||
serial := uint32(0) // The first serial seen is the current server serial
|
|
||||||
first := true
|
|
||||||
defer t.Close()
|
|
||||||
defer close(c)
|
|
||||||
timeout := dnsTimeout
|
|
||||||
if t.ReadTimeout != 0 {
|
|
||||||
timeout = t.ReadTimeout
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
t.SetReadDeadline(time.Now().Add(timeout))
|
|
||||||
in, err := t.ReadMsg()
|
|
||||||
if err != nil {
|
|
||||||
c <- &Envelope{nil, err}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if id != in.Id {
|
|
||||||
c <- &Envelope{in.Answer, ErrId}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if first {
|
|
||||||
// A single SOA RR signals "no changes"
|
|
||||||
if len(in.Answer) == 1 && isSOAFirst(in) {
|
|
||||||
c <- &Envelope{in.Answer, nil}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the returned answer is ok
|
|
||||||
if !isSOAFirst(in) {
|
|
||||||
c <- &Envelope{in.Answer, ErrSoa}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// This serial is important
|
|
||||||
serial = in.Answer[0].(*SOA).Serial
|
|
||||||
first = !first
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we need to check each message for SOA records, to see what we need to do
|
|
||||||
if !first {
|
|
||||||
t.tsigTimersOnly = true
|
|
||||||
// If the last record in the IXFR contains the servers' SOA, we should quit
|
|
||||||
if v, ok := in.Answer[len(in.Answer)-1].(*SOA); ok {
|
|
||||||
if v.Serial == serial {
|
|
||||||
c <- &Envelope{in.Answer, nil}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c <- &Envelope{in.Answer, nil}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Out performs an outgoing transfer with the client connecting in w.
|
|
||||||
// Basic use pattern:
|
|
||||||
//
|
|
||||||
// ch := make(chan *dns.Envelope)
|
|
||||||
// tr := new(dns.Transfer)
|
|
||||||
// go tr.Out(w, r, ch)
|
|
||||||
// ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
|
|
||||||
// close(ch)
|
|
||||||
// w.Hijack()
|
|
||||||
// // w.Close() // Client closes connection
|
|
||||||
//
|
|
||||||
// The server is responsible for sending the correct sequence of RRs through the
|
|
||||||
// channel ch.
|
|
||||||
func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
|
|
||||||
for x := range ch {
|
|
||||||
r := new(Msg)
|
|
||||||
// Compress?
|
|
||||||
r.SetReply(q)
|
|
||||||
r.Authoritative = true
|
|
||||||
// assume it fits TODO(miek): fix
|
|
||||||
r.Answer = append(r.Answer, x.RR...)
|
|
||||||
if err := w.WriteMsg(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.TsigTimersOnly(true)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMsg reads a message from the transfer connection t.
|
|
||||||
func (t *Transfer) ReadMsg() (*Msg, error) {
|
|
||||||
m := new(Msg)
|
|
||||||
p := make([]byte, MaxMsgSize)
|
|
||||||
n, err := t.Read(p)
|
|
||||||
if err != nil && n == 0 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p = p[:n]
|
|
||||||
if err := m.Unpack(p); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil {
|
|
||||||
if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok {
|
|
||||||
return m, ErrSecret
|
|
||||||
}
|
|
||||||
// Need to work on the original message p, as that was used to calculate the tsig.
|
|
||||||
err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly)
|
|
||||||
t.tsigRequestMAC = ts.MAC
|
|
||||||
}
|
|
||||||
return m, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteMsg writes a message through the transfer connection t.
|
|
||||||
func (t *Transfer) WriteMsg(m *Msg) (err error) {
|
|
||||||
var out []byte
|
|
||||||
if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil {
|
|
||||||
if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok {
|
|
||||||
return ErrSecret
|
|
||||||
}
|
|
||||||
out, t.tsigRequestMAC, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly)
|
|
||||||
} else {
|
|
||||||
out, err = m.Pack()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = t.Write(out); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSOAFirst(in *Msg) bool {
|
|
||||||
if len(in.Answer) > 0 {
|
|
||||||
return in.Answer[0].Header().Rrtype == TypeSOA
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSOALast(in *Msg) bool {
|
|
||||||
if len(in.Answer) > 0 {
|
|
||||||
return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
3529
vendor/github.com/miekg/dns/zmsg.go
generated
vendored
3529
vendor/github.com/miekg/dns/zmsg.go
generated
vendored
File diff suppressed because it is too large
Load diff
842
vendor/github.com/miekg/dns/ztypes.go
generated
vendored
842
vendor/github.com/miekg/dns/ztypes.go
generated
vendored
|
@ -1,842 +0,0 @@
|
||||||
// *** DO NOT MODIFY ***
|
|
||||||
// AUTOGENERATED BY go generate from type_generate.go
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TypeToRR is a map of constructors for each RR type.
|
|
||||||
var TypeToRR = map[uint16]func() RR{
|
|
||||||
TypeA: func() RR { return new(A) },
|
|
||||||
TypeAAAA: func() RR { return new(AAAA) },
|
|
||||||
TypeAFSDB: func() RR { return new(AFSDB) },
|
|
||||||
TypeANY: func() RR { return new(ANY) },
|
|
||||||
TypeCAA: func() RR { return new(CAA) },
|
|
||||||
TypeCDNSKEY: func() RR { return new(CDNSKEY) },
|
|
||||||
TypeCDS: func() RR { return new(CDS) },
|
|
||||||
TypeCERT: func() RR { return new(CERT) },
|
|
||||||
TypeCNAME: func() RR { return new(CNAME) },
|
|
||||||
TypeDHCID: func() RR { return new(DHCID) },
|
|
||||||
TypeDLV: func() RR { return new(DLV) },
|
|
||||||
TypeDNAME: func() RR { return new(DNAME) },
|
|
||||||
TypeDNSKEY: func() RR { return new(DNSKEY) },
|
|
||||||
TypeDS: func() RR { return new(DS) },
|
|
||||||
TypeEID: func() RR { return new(EID) },
|
|
||||||
TypeEUI48: func() RR { return new(EUI48) },
|
|
||||||
TypeEUI64: func() RR { return new(EUI64) },
|
|
||||||
TypeGID: func() RR { return new(GID) },
|
|
||||||
TypeGPOS: func() RR { return new(GPOS) },
|
|
||||||
TypeHINFO: func() RR { return new(HINFO) },
|
|
||||||
TypeHIP: func() RR { return new(HIP) },
|
|
||||||
TypeKEY: func() RR { return new(KEY) },
|
|
||||||
TypeKX: func() RR { return new(KX) },
|
|
||||||
TypeL32: func() RR { return new(L32) },
|
|
||||||
TypeL64: func() RR { return new(L64) },
|
|
||||||
TypeLOC: func() RR { return new(LOC) },
|
|
||||||
TypeLP: func() RR { return new(LP) },
|
|
||||||
TypeMB: func() RR { return new(MB) },
|
|
||||||
TypeMD: func() RR { return new(MD) },
|
|
||||||
TypeMF: func() RR { return new(MF) },
|
|
||||||
TypeMG: func() RR { return new(MG) },
|
|
||||||
TypeMINFO: func() RR { return new(MINFO) },
|
|
||||||
TypeMR: func() RR { return new(MR) },
|
|
||||||
TypeMX: func() RR { return new(MX) },
|
|
||||||
TypeNAPTR: func() RR { return new(NAPTR) },
|
|
||||||
TypeNID: func() RR { return new(NID) },
|
|
||||||
TypeNIMLOC: func() RR { return new(NIMLOC) },
|
|
||||||
TypeNINFO: func() RR { return new(NINFO) },
|
|
||||||
TypeNS: func() RR { return new(NS) },
|
|
||||||
TypeNSAPPTR: func() RR { return new(NSAPPTR) },
|
|
||||||
TypeNSEC: func() RR { return new(NSEC) },
|
|
||||||
TypeNSEC3: func() RR { return new(NSEC3) },
|
|
||||||
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
|
|
||||||
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
|
|
||||||
TypeOPT: func() RR { return new(OPT) },
|
|
||||||
TypePTR: func() RR { return new(PTR) },
|
|
||||||
TypePX: func() RR { return new(PX) },
|
|
||||||
TypeRKEY: func() RR { return new(RKEY) },
|
|
||||||
TypeRP: func() RR { return new(RP) },
|
|
||||||
TypeRRSIG: func() RR { return new(RRSIG) },
|
|
||||||
TypeRT: func() RR { return new(RT) },
|
|
||||||
TypeSIG: func() RR { return new(SIG) },
|
|
||||||
TypeSMIMEA: func() RR { return new(SMIMEA) },
|
|
||||||
TypeSOA: func() RR { return new(SOA) },
|
|
||||||
TypeSPF: func() RR { return new(SPF) },
|
|
||||||
TypeSRV: func() RR { return new(SRV) },
|
|
||||||
TypeSSHFP: func() RR { return new(SSHFP) },
|
|
||||||
TypeTA: func() RR { return new(TA) },
|
|
||||||
TypeTALINK: func() RR { return new(TALINK) },
|
|
||||||
TypeTKEY: func() RR { return new(TKEY) },
|
|
||||||
TypeTLSA: func() RR { return new(TLSA) },
|
|
||||||
TypeTSIG: func() RR { return new(TSIG) },
|
|
||||||
TypeTXT: func() RR { return new(TXT) },
|
|
||||||
TypeUID: func() RR { return new(UID) },
|
|
||||||
TypeUINFO: func() RR { return new(UINFO) },
|
|
||||||
TypeURI: func() RR { return new(URI) },
|
|
||||||
TypeX25: func() RR { return new(X25) },
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypeToString is a map of strings for each RR type.
|
|
||||||
var TypeToString = map[uint16]string{
|
|
||||||
TypeA: "A",
|
|
||||||
TypeAAAA: "AAAA",
|
|
||||||
TypeAFSDB: "AFSDB",
|
|
||||||
TypeANY: "ANY",
|
|
||||||
TypeATMA: "ATMA",
|
|
||||||
TypeAXFR: "AXFR",
|
|
||||||
TypeCAA: "CAA",
|
|
||||||
TypeCDNSKEY: "CDNSKEY",
|
|
||||||
TypeCDS: "CDS",
|
|
||||||
TypeCERT: "CERT",
|
|
||||||
TypeCNAME: "CNAME",
|
|
||||||
TypeDHCID: "DHCID",
|
|
||||||
TypeDLV: "DLV",
|
|
||||||
TypeDNAME: "DNAME",
|
|
||||||
TypeDNSKEY: "DNSKEY",
|
|
||||||
TypeDS: "DS",
|
|
||||||
TypeEID: "EID",
|
|
||||||
TypeEUI48: "EUI48",
|
|
||||||
TypeEUI64: "EUI64",
|
|
||||||
TypeGID: "GID",
|
|
||||||
TypeGPOS: "GPOS",
|
|
||||||
TypeHINFO: "HINFO",
|
|
||||||
TypeHIP: "HIP",
|
|
||||||
TypeISDN: "ISDN",
|
|
||||||
TypeIXFR: "IXFR",
|
|
||||||
TypeKEY: "KEY",
|
|
||||||
TypeKX: "KX",
|
|
||||||
TypeL32: "L32",
|
|
||||||
TypeL64: "L64",
|
|
||||||
TypeLOC: "LOC",
|
|
||||||
TypeLP: "LP",
|
|
||||||
TypeMAILA: "MAILA",
|
|
||||||
TypeMAILB: "MAILB",
|
|
||||||
TypeMB: "MB",
|
|
||||||
TypeMD: "MD",
|
|
||||||
TypeMF: "MF",
|
|
||||||
TypeMG: "MG",
|
|
||||||
TypeMINFO: "MINFO",
|
|
||||||
TypeMR: "MR",
|
|
||||||
TypeMX: "MX",
|
|
||||||
TypeNAPTR: "NAPTR",
|
|
||||||
TypeNID: "NID",
|
|
||||||
TypeNIMLOC: "NIMLOC",
|
|
||||||
TypeNINFO: "NINFO",
|
|
||||||
TypeNS: "NS",
|
|
||||||
TypeNSEC: "NSEC",
|
|
||||||
TypeNSEC3: "NSEC3",
|
|
||||||
TypeNSEC3PARAM: "NSEC3PARAM",
|
|
||||||
TypeNULL: "NULL",
|
|
||||||
TypeNXT: "NXT",
|
|
||||||
TypeNone: "None",
|
|
||||||
TypeOPENPGPKEY: "OPENPGPKEY",
|
|
||||||
TypeOPT: "OPT",
|
|
||||||
TypePTR: "PTR",
|
|
||||||
TypePX: "PX",
|
|
||||||
TypeRKEY: "RKEY",
|
|
||||||
TypeRP: "RP",
|
|
||||||
TypeRRSIG: "RRSIG",
|
|
||||||
TypeRT: "RT",
|
|
||||||
TypeReserved: "Reserved",
|
|
||||||
TypeSIG: "SIG",
|
|
||||||
TypeSMIMEA: "SMIMEA",
|
|
||||||
TypeSOA: "SOA",
|
|
||||||
TypeSPF: "SPF",
|
|
||||||
TypeSRV: "SRV",
|
|
||||||
TypeSSHFP: "SSHFP",
|
|
||||||
TypeTA: "TA",
|
|
||||||
TypeTALINK: "TALINK",
|
|
||||||
TypeTKEY: "TKEY",
|
|
||||||
TypeTLSA: "TLSA",
|
|
||||||
TypeTSIG: "TSIG",
|
|
||||||
TypeTXT: "TXT",
|
|
||||||
TypeUID: "UID",
|
|
||||||
TypeUINFO: "UINFO",
|
|
||||||
TypeUNSPEC: "UNSPEC",
|
|
||||||
TypeURI: "URI",
|
|
||||||
TypeX25: "X25",
|
|
||||||
TypeNSAPPTR: "NSAP-PTR",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header() functions
|
|
||||||
func (rr *A) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *AAAA) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *ANY) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *CDS) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *CERT) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *CNAME) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *DHCID) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *DLV) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *DNAME) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *DS) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *EID) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *EUI48) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *EUI64) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *GID) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *GPOS) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *HINFO) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *HIP) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *KEY) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *KX) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *L32) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *L64) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *LOC) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *LP) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *MB) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *MD) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *MF) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *MG) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *MINFO) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *MR) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *MX) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NID) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NINFO) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NS) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NSEC) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *OPT) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *PX) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *RKEY) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *RP) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *RT) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *SIG) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *SMIMEA) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *SOA) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *SPF) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *SRV) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *TA) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *TALINK) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *TKEY) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *TLSA) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *TSIG) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *TXT) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *UID) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *UINFO) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *URI) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
|
|
||||||
// len() functions
|
|
||||||
func (rr *A) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += net.IPv4len // A
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *AAAA) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += net.IPv6len // AAAA
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *AFSDB) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Subtype
|
|
||||||
l += len(rr.Hostname) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *ANY) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *CAA) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 1 // Flag
|
|
||||||
l += len(rr.Tag) + 1
|
|
||||||
l += len(rr.Value)
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *CERT) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Type
|
|
||||||
l += 2 // KeyTag
|
|
||||||
l += 1 // Algorithm
|
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.Certificate))
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *CNAME) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Target) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *DHCID) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.Digest))
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *DNAME) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Target) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *DNSKEY) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Flags
|
|
||||||
l += 1 // Protocol
|
|
||||||
l += 1 // Algorithm
|
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *DS) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // KeyTag
|
|
||||||
l += 1 // Algorithm
|
|
||||||
l += 1 // DigestType
|
|
||||||
l += len(rr.Digest)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *EID) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Endpoint)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *EUI48) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 6 // Address
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *EUI64) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 8 // Address
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *GID) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 4 // Gid
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *GPOS) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Longitude) + 1
|
|
||||||
l += len(rr.Latitude) + 1
|
|
||||||
l += len(rr.Altitude) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *HINFO) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Cpu) + 1
|
|
||||||
l += len(rr.Os) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *HIP) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 1 // HitLength
|
|
||||||
l += 1 // PublicKeyAlgorithm
|
|
||||||
l += 2 // PublicKeyLength
|
|
||||||
l += len(rr.Hit)/2 + 1
|
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
|
||||||
for _, x := range rr.RendezvousServers {
|
|
||||||
l += len(x) + 1
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *KX) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Preference
|
|
||||||
l += len(rr.Exchanger) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *L32) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Preference
|
|
||||||
l += net.IPv4len // Locator32
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *L64) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Preference
|
|
||||||
l += 8 // Locator64
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *LOC) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 1 // Version
|
|
||||||
l += 1 // Size
|
|
||||||
l += 1 // HorizPre
|
|
||||||
l += 1 // VertPre
|
|
||||||
l += 4 // Latitude
|
|
||||||
l += 4 // Longitude
|
|
||||||
l += 4 // Altitude
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *LP) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Preference
|
|
||||||
l += len(rr.Fqdn) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *MB) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Mb) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *MD) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Md) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *MF) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Mf) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *MG) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Mg) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *MINFO) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Rmail) + 1
|
|
||||||
l += len(rr.Email) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *MR) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Mr) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *MX) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Preference
|
|
||||||
l += len(rr.Mx) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *NAPTR) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Order
|
|
||||||
l += 2 // Preference
|
|
||||||
l += len(rr.Flags) + 1
|
|
||||||
l += len(rr.Service) + 1
|
|
||||||
l += len(rr.Regexp) + 1
|
|
||||||
l += len(rr.Replacement) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *NID) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Preference
|
|
||||||
l += 8 // NodeID
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *NIMLOC) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Locator)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *NINFO) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
for _, x := range rr.ZSData {
|
|
||||||
l += len(x) + 1
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *NS) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Ns) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *NSAPPTR) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Ptr) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *NSEC3PARAM) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 1 // Hash
|
|
||||||
l += 1 // Flags
|
|
||||||
l += 2 // Iterations
|
|
||||||
l += 1 // SaltLength
|
|
||||||
l += len(rr.Salt)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *OPENPGPKEY) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *PTR) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Ptr) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *PX) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Preference
|
|
||||||
l += len(rr.Map822) + 1
|
|
||||||
l += len(rr.Mapx400) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *RFC3597) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Rdata)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *RKEY) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Flags
|
|
||||||
l += 1 // Protocol
|
|
||||||
l += 1 // Algorithm
|
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *RP) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Mbox) + 1
|
|
||||||
l += len(rr.Txt) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *RRSIG) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // TypeCovered
|
|
||||||
l += 1 // Algorithm
|
|
||||||
l += 1 // Labels
|
|
||||||
l += 4 // OrigTtl
|
|
||||||
l += 4 // Expiration
|
|
||||||
l += 4 // Inception
|
|
||||||
l += 2 // KeyTag
|
|
||||||
l += len(rr.SignerName) + 1
|
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.Signature))
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *RT) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Preference
|
|
||||||
l += len(rr.Host) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *SMIMEA) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 1 // Usage
|
|
||||||
l += 1 // Selector
|
|
||||||
l += 1 // MatchingType
|
|
||||||
l += len(rr.Certificate)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *SOA) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Ns) + 1
|
|
||||||
l += len(rr.Mbox) + 1
|
|
||||||
l += 4 // Serial
|
|
||||||
l += 4 // Refresh
|
|
||||||
l += 4 // Retry
|
|
||||||
l += 4 // Expire
|
|
||||||
l += 4 // Minttl
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *SPF) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
for _, x := range rr.Txt {
|
|
||||||
l += len(x) + 1
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *SRV) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Priority
|
|
||||||
l += 2 // Weight
|
|
||||||
l += 2 // Port
|
|
||||||
l += len(rr.Target) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *SSHFP) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 1 // Algorithm
|
|
||||||
l += 1 // Type
|
|
||||||
l += len(rr.FingerPrint)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *TA) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // KeyTag
|
|
||||||
l += 1 // Algorithm
|
|
||||||
l += 1 // DigestType
|
|
||||||
l += len(rr.Digest)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *TALINK) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.PreviousName) + 1
|
|
||||||
l += len(rr.NextName) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *TKEY) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Algorithm) + 1
|
|
||||||
l += 4 // Inception
|
|
||||||
l += 4 // Expiration
|
|
||||||
l += 2 // Mode
|
|
||||||
l += 2 // Error
|
|
||||||
l += 2 // KeySize
|
|
||||||
l += len(rr.Key) + 1
|
|
||||||
l += 2 // OtherLen
|
|
||||||
l += len(rr.OtherData) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *TLSA) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 1 // Usage
|
|
||||||
l += 1 // Selector
|
|
||||||
l += 1 // MatchingType
|
|
||||||
l += len(rr.Certificate)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *TSIG) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Algorithm) + 1
|
|
||||||
l += 6 // TimeSigned
|
|
||||||
l += 2 // Fudge
|
|
||||||
l += 2 // MACSize
|
|
||||||
l += len(rr.MAC)/2 + 1
|
|
||||||
l += 2 // OrigId
|
|
||||||
l += 2 // Error
|
|
||||||
l += 2 // OtherLen
|
|
||||||
l += len(rr.OtherData)/2 + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *TXT) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
for _, x := range rr.Txt {
|
|
||||||
l += len(x) + 1
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *UID) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 4 // Uid
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *UINFO) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.Uinfo) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *URI) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += 2 // Priority
|
|
||||||
l += 2 // Weight
|
|
||||||
l += len(rr.Target)
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *X25) len() int {
|
|
||||||
l := rr.Hdr.len()
|
|
||||||
l += len(rr.PSDNAddress) + 1
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy() functions
|
|
||||||
func (rr *A) copy() RR {
|
|
||||||
return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)}
|
|
||||||
}
|
|
||||||
func (rr *AAAA) copy() RR {
|
|
||||||
return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)}
|
|
||||||
}
|
|
||||||
func (rr *AFSDB) copy() RR {
|
|
||||||
return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname}
|
|
||||||
}
|
|
||||||
func (rr *ANY) copy() RR {
|
|
||||||
return &ANY{*rr.Hdr.copyHeader()}
|
|
||||||
}
|
|
||||||
func (rr *CAA) copy() RR {
|
|
||||||
return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value}
|
|
||||||
}
|
|
||||||
func (rr *CERT) copy() RR {
|
|
||||||
return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
|
|
||||||
}
|
|
||||||
func (rr *CNAME) copy() RR {
|
|
||||||
return &CNAME{*rr.Hdr.copyHeader(), rr.Target}
|
|
||||||
}
|
|
||||||
func (rr *DHCID) copy() RR {
|
|
||||||
return &DHCID{*rr.Hdr.copyHeader(), rr.Digest}
|
|
||||||
}
|
|
||||||
func (rr *DNAME) copy() RR {
|
|
||||||
return &DNAME{*rr.Hdr.copyHeader(), rr.Target}
|
|
||||||
}
|
|
||||||
func (rr *DNSKEY) copy() RR {
|
|
||||||
return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
|
|
||||||
}
|
|
||||||
func (rr *DS) copy() RR {
|
|
||||||
return &DS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
|
|
||||||
}
|
|
||||||
func (rr *EID) copy() RR {
|
|
||||||
return &EID{*rr.Hdr.copyHeader(), rr.Endpoint}
|
|
||||||
}
|
|
||||||
func (rr *EUI48) copy() RR {
|
|
||||||
return &EUI48{*rr.Hdr.copyHeader(), rr.Address}
|
|
||||||
}
|
|
||||||
func (rr *EUI64) copy() RR {
|
|
||||||
return &EUI64{*rr.Hdr.copyHeader(), rr.Address}
|
|
||||||
}
|
|
||||||
func (rr *GID) copy() RR {
|
|
||||||
return &GID{*rr.Hdr.copyHeader(), rr.Gid}
|
|
||||||
}
|
|
||||||
func (rr *GPOS) copy() RR {
|
|
||||||
return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude}
|
|
||||||
}
|
|
||||||
func (rr *HINFO) copy() RR {
|
|
||||||
return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os}
|
|
||||||
}
|
|
||||||
func (rr *HIP) copy() RR {
|
|
||||||
RendezvousServers := make([]string, len(rr.RendezvousServers))
|
|
||||||
copy(RendezvousServers, rr.RendezvousServers)
|
|
||||||
return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
|
|
||||||
}
|
|
||||||
func (rr *KX) copy() RR {
|
|
||||||
return &KX{*rr.Hdr.copyHeader(), rr.Preference, rr.Exchanger}
|
|
||||||
}
|
|
||||||
func (rr *L32) copy() RR {
|
|
||||||
return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)}
|
|
||||||
}
|
|
||||||
func (rr *L64) copy() RR {
|
|
||||||
return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64}
|
|
||||||
}
|
|
||||||
func (rr *LOC) copy() RR {
|
|
||||||
return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
|
|
||||||
}
|
|
||||||
func (rr *LP) copy() RR {
|
|
||||||
return &LP{*rr.Hdr.copyHeader(), rr.Preference, rr.Fqdn}
|
|
||||||
}
|
|
||||||
func (rr *MB) copy() RR {
|
|
||||||
return &MB{*rr.Hdr.copyHeader(), rr.Mb}
|
|
||||||
}
|
|
||||||
func (rr *MD) copy() RR {
|
|
||||||
return &MD{*rr.Hdr.copyHeader(), rr.Md}
|
|
||||||
}
|
|
||||||
func (rr *MF) copy() RR {
|
|
||||||
return &MF{*rr.Hdr.copyHeader(), rr.Mf}
|
|
||||||
}
|
|
||||||
func (rr *MG) copy() RR {
|
|
||||||
return &MG{*rr.Hdr.copyHeader(), rr.Mg}
|
|
||||||
}
|
|
||||||
func (rr *MINFO) copy() RR {
|
|
||||||
return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email}
|
|
||||||
}
|
|
||||||
func (rr *MR) copy() RR {
|
|
||||||
return &MR{*rr.Hdr.copyHeader(), rr.Mr}
|
|
||||||
}
|
|
||||||
func (rr *MX) copy() RR {
|
|
||||||
return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx}
|
|
||||||
}
|
|
||||||
func (rr *NAPTR) copy() RR {
|
|
||||||
return &NAPTR{*rr.Hdr.copyHeader(), rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
|
|
||||||
}
|
|
||||||
func (rr *NID) copy() RR {
|
|
||||||
return &NID{*rr.Hdr.copyHeader(), rr.Preference, rr.NodeID}
|
|
||||||
}
|
|
||||||
func (rr *NIMLOC) copy() RR {
|
|
||||||
return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator}
|
|
||||||
}
|
|
||||||
func (rr *NINFO) copy() RR {
|
|
||||||
ZSData := make([]string, len(rr.ZSData))
|
|
||||||
copy(ZSData, rr.ZSData)
|
|
||||||
return &NINFO{*rr.Hdr.copyHeader(), ZSData}
|
|
||||||
}
|
|
||||||
func (rr *NS) copy() RR {
|
|
||||||
return &NS{*rr.Hdr.copyHeader(), rr.Ns}
|
|
||||||
}
|
|
||||||
func (rr *NSAPPTR) copy() RR {
|
|
||||||
return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr}
|
|
||||||
}
|
|
||||||
func (rr *NSEC) copy() RR {
|
|
||||||
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
|
||||||
copy(TypeBitMap, rr.TypeBitMap)
|
|
||||||
return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, TypeBitMap}
|
|
||||||
}
|
|
||||||
func (rr *NSEC3) copy() RR {
|
|
||||||
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
|
||||||
copy(TypeBitMap, rr.TypeBitMap)
|
|
||||||
return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap}
|
|
||||||
}
|
|
||||||
func (rr *NSEC3PARAM) copy() RR {
|
|
||||||
return &NSEC3PARAM{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
|
|
||||||
}
|
|
||||||
func (rr *OPENPGPKEY) copy() RR {
|
|
||||||
return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey}
|
|
||||||
}
|
|
||||||
func (rr *OPT) copy() RR {
|
|
||||||
Option := make([]EDNS0, len(rr.Option))
|
|
||||||
copy(Option, rr.Option)
|
|
||||||
return &OPT{*rr.Hdr.copyHeader(), Option}
|
|
||||||
}
|
|
||||||
func (rr *PTR) copy() RR {
|
|
||||||
return &PTR{*rr.Hdr.copyHeader(), rr.Ptr}
|
|
||||||
}
|
|
||||||
func (rr *PX) copy() RR {
|
|
||||||
return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400}
|
|
||||||
}
|
|
||||||
func (rr *RFC3597) copy() RR {
|
|
||||||
return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata}
|
|
||||||
}
|
|
||||||
func (rr *RKEY) copy() RR {
|
|
||||||
return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
|
|
||||||
}
|
|
||||||
func (rr *RP) copy() RR {
|
|
||||||
return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt}
|
|
||||||
}
|
|
||||||
func (rr *RRSIG) copy() RR {
|
|
||||||
return &RRSIG{*rr.Hdr.copyHeader(), rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature}
|
|
||||||
}
|
|
||||||
func (rr *RT) copy() RR {
|
|
||||||
return &RT{*rr.Hdr.copyHeader(), rr.Preference, rr.Host}
|
|
||||||
}
|
|
||||||
func (rr *SMIMEA) copy() RR {
|
|
||||||
return &SMIMEA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
|
|
||||||
}
|
|
||||||
func (rr *SOA) copy() RR {
|
|
||||||
return &SOA{*rr.Hdr.copyHeader(), rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl}
|
|
||||||
}
|
|
||||||
func (rr *SPF) copy() RR {
|
|
||||||
Txt := make([]string, len(rr.Txt))
|
|
||||||
copy(Txt, rr.Txt)
|
|
||||||
return &SPF{*rr.Hdr.copyHeader(), Txt}
|
|
||||||
}
|
|
||||||
func (rr *SRV) copy() RR {
|
|
||||||
return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target}
|
|
||||||
}
|
|
||||||
func (rr *SSHFP) copy() RR {
|
|
||||||
return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint}
|
|
||||||
}
|
|
||||||
func (rr *TA) copy() RR {
|
|
||||||
return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
|
|
||||||
}
|
|
||||||
func (rr *TALINK) copy() RR {
|
|
||||||
return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName}
|
|
||||||
}
|
|
||||||
func (rr *TKEY) copy() RR {
|
|
||||||
return &TKEY{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData}
|
|
||||||
}
|
|
||||||
func (rr *TLSA) copy() RR {
|
|
||||||
return &TLSA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
|
|
||||||
}
|
|
||||||
func (rr *TSIG) copy() RR {
|
|
||||||
return &TSIG{*rr.Hdr.copyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
|
|
||||||
}
|
|
||||||
func (rr *TXT) copy() RR {
|
|
||||||
Txt := make([]string, len(rr.Txt))
|
|
||||||
copy(Txt, rr.Txt)
|
|
||||||
return &TXT{*rr.Hdr.copyHeader(), Txt}
|
|
||||||
}
|
|
||||||
func (rr *UID) copy() RR {
|
|
||||||
return &UID{*rr.Hdr.copyHeader(), rr.Uid}
|
|
||||||
}
|
|
||||||
func (rr *UINFO) copy() RR {
|
|
||||||
return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo}
|
|
||||||
}
|
|
||||||
func (rr *URI) copy() RR {
|
|
||||||
return &URI{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Target}
|
|
||||||
}
|
|
||||||
func (rr *X25) copy() RR {
|
|
||||||
return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress}
|
|
||||||
}
|
|
21
vendor/github.com/xenolf/lego/LICENSE
generated
vendored
21
vendor/github.com/xenolf/lego/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Sebastian Erhart
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
248
vendor/github.com/xenolf/lego/README.md
generated
vendored
248
vendor/github.com/xenolf/lego/README.md
generated
vendored
|
@ -1,248 +0,0 @@
|
||||||
# lego
|
|
||||||
Let's Encrypt client and ACME library written in Go
|
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/xenolf/lego/acme?status.svg)](https://godoc.org/github.com/xenolf/lego/acme)
|
|
||||||
[![Build Status](https://travis-ci.org/xenolf/lego.svg?branch=master)](https://travis-ci.org/xenolf/lego)
|
|
||||||
[![Dev Chat](https://img.shields.io/badge/dev%20chat-gitter-blue.svg?label=dev+chat)](https://gitter.im/xenolf/lego)
|
|
||||||
|
|
||||||
#### General
|
|
||||||
This is a work in progress. Please do *NOT* run this on a production server and please report any bugs you find!
|
|
||||||
|
|
||||||
#### Installation
|
|
||||||
lego supports both binary installs and install from source.
|
|
||||||
|
|
||||||
To get the binary just download the latest release for your OS/Arch from [the release page](https://github.com/xenolf/lego/releases)
|
|
||||||
and put the binary somewhere convenient. lego does not assume anything about the location you run it from.
|
|
||||||
|
|
||||||
To install from source, just run
|
|
||||||
```
|
|
||||||
go get -u github.com/xenolf/lego
|
|
||||||
```
|
|
||||||
|
|
||||||
To build lego inside a Docker container, just run
|
|
||||||
```
|
|
||||||
docker build -t lego .
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Features
|
|
||||||
|
|
||||||
- Register with CA
|
|
||||||
- Obtain certificates
|
|
||||||
- Renew certificates
|
|
||||||
- Revoke certificates
|
|
||||||
- Robust implementation of all ACME challenges
|
|
||||||
- HTTP (http-01)
|
|
||||||
- TLS with Server Name Indication (tls-sni-01)
|
|
||||||
- DNS (dns-01)
|
|
||||||
- SAN certificate support
|
|
||||||
- Comes with multiple optional [DNS providers](https://github.com/xenolf/lego/tree/master/providers/dns)
|
|
||||||
- [Custom challenge solvers](https://github.com/xenolf/lego/wiki/Writing-a-Challenge-Solver)
|
|
||||||
- Certificate bundling
|
|
||||||
- OCSP helper function
|
|
||||||
|
|
||||||
Please keep in mind that CLI switches and APIs are still subject to change.
|
|
||||||
|
|
||||||
When using the standard `--path` option, all certificates and account configurations are saved to a folder *.lego* in the current working directory.
|
|
||||||
|
|
||||||
#### Sudo
|
|
||||||
The CLI does not require root permissions but needs to bind to port 80 and 443 for certain challenges.
|
|
||||||
To run the CLI without sudo, you have four options:
|
|
||||||
|
|
||||||
- Use setcap 'cap_net_bind_service=+ep' /path/to/program
|
|
||||||
- Pass the `--http` or/and the `--tls` option and specify a custom port to bind to. In this case you have to forward port 80/443 to these custom ports (see [Port Usage](#port-usage)).
|
|
||||||
- Pass the `--webroot` option and specify the path to your webroot folder. In this case the challenge will be written in a file in `.well-known/acme-challenge/` inside your webroot.
|
|
||||||
- Pass the `--dns` option and specify a DNS provider.
|
|
||||||
|
|
||||||
#### Port Usage
|
|
||||||
By default lego assumes it is able to bind to ports 80 and 443 to solve challenges.
|
|
||||||
If this is not possible in your environment, you can use the `--http` and `--tls` options to instruct
|
|
||||||
lego to listen on that interface:port for any incoming challenges.
|
|
||||||
|
|
||||||
If you are using this option, make sure you proxy all of the following traffic to these ports.
|
|
||||||
|
|
||||||
HTTP Port:
|
|
||||||
- All plaintext HTTP requests to port 80 which begin with a request path of `/.well-known/acme-challenge/` for the HTTP challenge.
|
|
||||||
|
|
||||||
TLS Port:
|
|
||||||
- All TLS handshakes on port 443 for the TLS-SNI challenge.
|
|
||||||
|
|
||||||
This traffic redirection is only needed as long as lego solves challenges. As soon as you have received your certificates you can deactivate the forwarding.
|
|
||||||
|
|
||||||
#### Usage
|
|
||||||
|
|
||||||
```
|
|
||||||
NAME:
|
|
||||||
lego - Let's Encrypt client written in Go
|
|
||||||
|
|
||||||
USAGE:
|
|
||||||
lego [global options] command [command options] [arguments...]
|
|
||||||
|
|
||||||
VERSION:
|
|
||||||
0.3.0
|
|
||||||
|
|
||||||
COMMANDS:
|
|
||||||
run Register an account, then create and install a certificate
|
|
||||||
revoke Revoke a certificate
|
|
||||||
renew Renew a certificate
|
|
||||||
dnshelp Shows additional help for the --dns global option
|
|
||||||
help, h Shows a list of commands or help for one command
|
|
||||||
|
|
||||||
GLOBAL OPTIONS:
|
|
||||||
--domains, -d [--domains option --domains option] Add domains to the process
|
|
||||||
--server, -s "https://acme-v01.api.letsencrypt.org/directory" CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.
|
|
||||||
--email, -m Email used for registration and recovery contact.
|
|
||||||
--accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
|
|
||||||
--key-type, -k "rsa2048" Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384
|
|
||||||
--path "${CWD}/.lego" Directory to use for storing the data
|
|
||||||
--exclude, -x [--exclude option --exclude option] Explicitly disallow solvers by name from being used. Solvers: "http-01", "tls-sni-01".
|
|
||||||
--webroot Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge
|
|
||||||
--http Set the port and interface to use for HTTP based challenges to listen on. Supported: interface:port or :port
|
|
||||||
--tls Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port
|
|
||||||
--dns Solve a DNS challenge using the specified provider. Disables all other challenges. Run 'lego dnshelp' for help on usage.
|
|
||||||
--help, -h show help
|
|
||||||
--version, -v print the version
|
|
||||||
```
|
|
||||||
|
|
||||||
##### CLI Example
|
|
||||||
|
|
||||||
Assumes the `lego` binary has permission to bind to ports 80 and 443. You can get a pre-built binary from the [releases](https://github.com/xenolf/lego/releases) page.
|
|
||||||
If your environment does not allow you to bind to these ports, please read [Port Usage](#port-usage).
|
|
||||||
|
|
||||||
Obtain a certificate:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ lego --email="foo@bar.com" --domains="example.com" run
|
|
||||||
```
|
|
||||||
|
|
||||||
(Find your certificate in the `.lego` folder of current working directory.)
|
|
||||||
|
|
||||||
To renew the certificate:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ lego --email="foo@bar.com" --domains="example.com" renew
|
|
||||||
```
|
|
||||||
|
|
||||||
Obtain a certificate using the DNS challenge and AWS Route 53:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID=my_id AWS_SECRET_ACCESS_KEY=my_key lego --email="foo@bar.com" --domains="example.com" --dns="route53" run
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that `--dns=foo` implies `--exclude=http-01` and `--exclude=tls-sni-01`. lego will not attempt other challenges if you've told it to use DNS instead.
|
|
||||||
|
|
||||||
lego defaults to communicating with the production Let's Encrypt ACME server. If you'd like to test something without issuing real certificates, consider using the staging endpoint instead:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ lego --server=https://acme-staging.api.letsencrypt.org/directory …
|
|
||||||
```
|
|
||||||
|
|
||||||
#### DNS Challenge API Details
|
|
||||||
|
|
||||||
##### AWS Route 53
|
|
||||||
|
|
||||||
The following AWS IAM policy document describes the permissions required for lego to complete the DNS challenge.
|
|
||||||
Replace `<INSERT_YOUR_HOSTED_ZONE_ID_HERE>` with the Route 53 zone ID of the domain you are authorizing.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"Version": "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"route53:GetChange",
|
|
||||||
"route53:ListHostedZonesByName"
|
|
||||||
],
|
|
||||||
"Resource": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"route53:ChangeResourceRecordSets"
|
|
||||||
],
|
|
||||||
"Resource": [
|
|
||||||
"arn:aws:route53:::hostedzone/<INSERT_YOUR_HOSTED_ZONE_ID_HERE>"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ACME Library Usage
|
|
||||||
|
|
||||||
A valid, but bare-bones example use of the acme package:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// You'll need a user or account type that implements acme.User
|
|
||||||
type MyUser struct {
|
|
||||||
Email string
|
|
||||||
Registration *acme.RegistrationResource
|
|
||||||
key crypto.PrivateKey
|
|
||||||
}
|
|
||||||
func (u MyUser) GetEmail() string {
|
|
||||||
return u.Email
|
|
||||||
}
|
|
||||||
func (u MyUser) GetRegistration() *acme.RegistrationResource {
|
|
||||||
return u.Registration
|
|
||||||
}
|
|
||||||
func (u MyUser) GetPrivateKey() crypto.PrivateKey {
|
|
||||||
return u.key
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a user. New accounts need an email and private key to start.
|
|
||||||
const rsaKeySize = 2048
|
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
myUser := MyUser{
|
|
||||||
Email: "you@yours.com",
|
|
||||||
key: privateKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
// A client facilitates communication with the CA server. This CA URL is
|
|
||||||
// configured for a local dev instance of Boulder running in Docker in a VM.
|
|
||||||
client, err := acme.NewClient("http://192.168.99.100:4000", &myUser, acme.RSA2048)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We specify an http port of 5002 and an tls port of 5001 on all interfaces
|
|
||||||
// because we aren't running as root and can't bind a listener to port 80 and 443
|
|
||||||
// (used later when we attempt to pass challenges). Keep in mind that we still
|
|
||||||
// need to proxy challenge traffic to port 5002 and 5001.
|
|
||||||
client.SetHTTPAddress(":5002")
|
|
||||||
client.SetTLSAddress(":5001")
|
|
||||||
|
|
||||||
// New users will need to register
|
|
||||||
reg, err := client.Register()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
myUser.Registration = reg
|
|
||||||
|
|
||||||
// SAVE THE USER.
|
|
||||||
|
|
||||||
// The client has a URL to the current Let's Encrypt Subscriber
|
|
||||||
// Agreement. The user will need to agree to it.
|
|
||||||
err = client.AgreeToTOS()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The acme library takes care of completing the challenges to obtain the certificate(s).
|
|
||||||
// The domains must resolve to this machine or you have to use the DNS challenge.
|
|
||||||
bundle := false
|
|
||||||
certificates, failures := client.ObtainCertificate([]string{"mydomain.com"}, bundle, nil)
|
|
||||||
if len(failures) > 0 {
|
|
||||||
log.Fatal(failures)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Each certificate comes back with the cert bytes, the bytes of the client's
|
|
||||||
// private key, and a certificate URL. SAVE THESE TO DISK.
|
|
||||||
fmt.Printf("%#v\n", certificates)
|
|
||||||
|
|
||||||
// ... all done.
|
|
||||||
```
|
|
16
vendor/github.com/xenolf/lego/acme/challenges.go
generated
vendored
16
vendor/github.com/xenolf/lego/acme/challenges.go
generated
vendored
|
@ -1,16 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
// Challenge is a string that identifies a particular type and version of ACME challenge.
|
|
||||||
type Challenge string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// HTTP01 is the "http-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http
|
|
||||||
// Note: HTTP01ChallengePath returns the URL path to fulfill this challenge
|
|
||||||
HTTP01 = Challenge("http-01")
|
|
||||||
// TLSSNI01 is the "tls-sni-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni
|
|
||||||
// Note: TLSSNI01ChallengeCert returns a certificate to fulfill this challenge
|
|
||||||
TLSSNI01 = Challenge("tls-sni-01")
|
|
||||||
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns
|
|
||||||
// Note: DNS01Record returns a DNS record which will fulfill this challenge
|
|
||||||
DNS01 = Challenge("dns-01")
|
|
||||||
)
|
|
702
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
702
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
|
@ -1,702 +0,0 @@
|
||||||
// Package acme implements the ACME protocol for Let's Encrypt and other conforming providers.
|
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Logger is an optional custom logger.
|
|
||||||
Logger *log.Logger
|
|
||||||
)
|
|
||||||
|
|
||||||
// logf writes a log entry. It uses Logger if not
|
|
||||||
// nil, otherwise it uses the default log.Logger.
|
|
||||||
func logf(format string, args ...interface{}) {
|
|
||||||
if Logger != nil {
|
|
||||||
Logger.Printf(format, args...)
|
|
||||||
} else {
|
|
||||||
log.Printf(format, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// User interface is to be implemented by users of this library.
|
|
||||||
// It is used by the client type to get user specific information.
|
|
||||||
type User interface {
|
|
||||||
GetEmail() string
|
|
||||||
GetRegistration() *RegistrationResource
|
|
||||||
GetPrivateKey() crypto.PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface for all challenge solvers to implement.
|
|
||||||
type solver interface {
|
|
||||||
Solve(challenge challenge, domain string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type validateFunc func(j *jws, domain, uri string, chlng challenge) error
|
|
||||||
|
|
||||||
// Client is the user-friendy way to ACME
|
|
||||||
type Client struct {
|
|
||||||
directory directory
|
|
||||||
user User
|
|
||||||
jws *jws
|
|
||||||
keyType KeyType
|
|
||||||
issuerCert []byte
|
|
||||||
solvers map[Challenge]solver
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient creates a new ACME client on behalf of the user. The client will depend on
|
|
||||||
// the ACME directory located at caDirURL for the rest of its actions. It will
|
|
||||||
// generate private keys for certificates of size keyBits.
|
|
||||||
func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
|
|
||||||
privKey := user.GetPrivateKey()
|
|
||||||
if privKey == nil {
|
|
||||||
return nil, errors.New("private key was nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
var dir directory
|
|
||||||
if _, err := getJSON(caDirURL, &dir); err != nil {
|
|
||||||
return nil, fmt.Errorf("get directory at '%s': %v", caDirURL, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dir.NewRegURL == "" {
|
|
||||||
return nil, errors.New("directory missing new registration URL")
|
|
||||||
}
|
|
||||||
if dir.NewAuthzURL == "" {
|
|
||||||
return nil, errors.New("directory missing new authz URL")
|
|
||||||
}
|
|
||||||
if dir.NewCertURL == "" {
|
|
||||||
return nil, errors.New("directory missing new certificate URL")
|
|
||||||
}
|
|
||||||
if dir.RevokeCertURL == "" {
|
|
||||||
return nil, errors.New("directory missing revoke certificate URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
jws := &jws{privKey: privKey, directoryURL: caDirURL}
|
|
||||||
|
|
||||||
// REVIEW: best possibility?
|
|
||||||
// Add all available solvers with the right index as per ACME
|
|
||||||
// spec to this map. Otherwise they won`t be found.
|
|
||||||
solvers := make(map[Challenge]solver)
|
|
||||||
solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}
|
|
||||||
solvers[TLSSNI01] = &tlsSNIChallenge{jws: jws, validate: validate, provider: &TLSProviderServer{}}
|
|
||||||
|
|
||||||
return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetChallengeProvider specifies a custom provider that will make the solution available
|
|
||||||
func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) error {
|
|
||||||
switch challenge {
|
|
||||||
case HTTP01:
|
|
||||||
c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p}
|
|
||||||
case TLSSNI01:
|
|
||||||
c.solvers[challenge] = &tlsSNIChallenge{jws: c.jws, validate: validate, provider: p}
|
|
||||||
case DNS01:
|
|
||||||
c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, provider: p}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Unknown challenge %v", challenge)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHTTPAddress specifies a custom interface:port to be used for HTTP based challenges.
|
|
||||||
// If this option is not used, the default port 80 and all interfaces will be used.
|
|
||||||
// To only specify a port and no interface use the ":port" notation.
|
|
||||||
func (c *Client) SetHTTPAddress(iface string) error {
|
|
||||||
host, port, err := net.SplitHostPort(iface)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if chlng, ok := c.solvers[HTTP01]; ok {
|
|
||||||
chlng.(*httpChallenge).provider = NewHTTPProviderServer(host, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTLSAddress specifies a custom interface:port to be used for TLS based challenges.
|
|
||||||
// If this option is not used, the default port 443 and all interfaces will be used.
|
|
||||||
// To only specify a port and no interface use the ":port" notation.
|
|
||||||
func (c *Client) SetTLSAddress(iface string) error {
|
|
||||||
host, port, err := net.SplitHostPort(iface)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if chlng, ok := c.solvers[TLSSNI01]; ok {
|
|
||||||
chlng.(*tlsSNIChallenge).provider = NewTLSProviderServer(host, port)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExcludeChallenges explicitly removes challenges from the pool for solving.
|
|
||||||
func (c *Client) ExcludeChallenges(challenges []Challenge) {
|
|
||||||
// Loop through all challenges and delete the requested one if found.
|
|
||||||
for _, challenge := range challenges {
|
|
||||||
delete(c.solvers, challenge)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the current account to the ACME server.
|
|
||||||
func (c *Client) Register() (*RegistrationResource, error) {
|
|
||||||
if c == nil || c.user == nil {
|
|
||||||
return nil, errors.New("acme: cannot register a nil client or user")
|
|
||||||
}
|
|
||||||
logf("[INFO] acme: Registering account for %s", c.user.GetEmail())
|
|
||||||
|
|
||||||
regMsg := registrationMessage{
|
|
||||||
Resource: "new-reg",
|
|
||||||
}
|
|
||||||
if c.user.GetEmail() != "" {
|
|
||||||
regMsg.Contact = []string{"mailto:" + c.user.GetEmail()}
|
|
||||||
} else {
|
|
||||||
regMsg.Contact = []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverReg Registration
|
|
||||||
hdr, err := postJSON(c.jws, c.directory.NewRegURL, regMsg, &serverReg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
reg := &RegistrationResource{Body: serverReg}
|
|
||||||
|
|
||||||
links := parseLinks(hdr["Link"])
|
|
||||||
reg.URI = hdr.Get("Location")
|
|
||||||
if links["terms-of-service"] != "" {
|
|
||||||
reg.TosURL = links["terms-of-service"]
|
|
||||||
}
|
|
||||||
|
|
||||||
if links["next"] != "" {
|
|
||||||
reg.NewAuthzURL = links["next"]
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("acme: The server did not return 'next' link to proceed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return reg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRegistration deletes the client's user registration from the ACME
|
|
||||||
// server.
|
|
||||||
func (c *Client) DeleteRegistration() error {
|
|
||||||
if c == nil || c.user == nil {
|
|
||||||
return errors.New("acme: cannot unregister a nil client or user")
|
|
||||||
}
|
|
||||||
logf("[INFO] acme: Deleting account for %s", c.user.GetEmail())
|
|
||||||
|
|
||||||
regMsg := registrationMessage{
|
|
||||||
Resource: "reg",
|
|
||||||
Delete: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := postJSON(c.jws, c.user.GetRegistration().URI, regMsg, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryRegistration runs a POST request on the client's registration and
|
|
||||||
// returns the result.
|
|
||||||
//
|
|
||||||
// This is similar to the Register function, but acting on an existing
|
|
||||||
// registration link and resource.
|
|
||||||
func (c *Client) QueryRegistration() (*RegistrationResource, error) {
|
|
||||||
if c == nil || c.user == nil {
|
|
||||||
return nil, errors.New("acme: cannot query the registration of a nil client or user")
|
|
||||||
}
|
|
||||||
// Log the URL here instead of the email as the email may not be set
|
|
||||||
logf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI)
|
|
||||||
|
|
||||||
regMsg := registrationMessage{
|
|
||||||
Resource: "reg",
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverReg Registration
|
|
||||||
hdr, err := postJSON(c.jws, c.user.GetRegistration().URI, regMsg, &serverReg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
reg := &RegistrationResource{Body: serverReg}
|
|
||||||
|
|
||||||
links := parseLinks(hdr["Link"])
|
|
||||||
// Location: header is not returned so this needs to be populated off of
|
|
||||||
// existing URI
|
|
||||||
reg.URI = c.user.GetRegistration().URI
|
|
||||||
if links["terms-of-service"] != "" {
|
|
||||||
reg.TosURL = links["terms-of-service"]
|
|
||||||
}
|
|
||||||
|
|
||||||
if links["next"] != "" {
|
|
||||||
reg.NewAuthzURL = links["next"]
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("acme: No new-authz link in response to registration query")
|
|
||||||
}
|
|
||||||
|
|
||||||
return reg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AgreeToTOS updates the Client registration and sends the agreement to
|
|
||||||
// the server.
|
|
||||||
func (c *Client) AgreeToTOS() error {
|
|
||||||
reg := c.user.GetRegistration()
|
|
||||||
|
|
||||||
reg.Body.Agreement = c.user.GetRegistration().TosURL
|
|
||||||
reg.Body.Resource = "reg"
|
|
||||||
_, err := postJSON(c.jws, c.user.GetRegistration().URI, c.user.GetRegistration().Body, nil)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ObtainCertificate tries to obtain a single certificate using all domains passed into it.
|
|
||||||
// The first domain in domains is used for the CommonName field of the certificate, all other
|
|
||||||
// domains are added using the Subject Alternate Names extension. A new private key is generated
|
|
||||||
// for every invocation of this function. If you do not want that you can supply your own private key
|
|
||||||
// in the privKey parameter. If this parameter is non-nil it will be used instead of generating a new one.
|
|
||||||
// If bundle is true, the []byte contains both the issuer certificate and
|
|
||||||
// your issued certificate as a bundle.
|
|
||||||
// This function will never return a partial certificate. If one domain in the list fails,
|
|
||||||
// the whole certificate will fail.
|
|
||||||
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey) (CertificateResource, map[string]error) {
|
|
||||||
if bundle {
|
|
||||||
logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
|
||||||
} else {
|
|
||||||
logf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
challenges, failures := c.getChallenges(domains)
|
|
||||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
|
||||||
if len(failures) > 0 {
|
|
||||||
return CertificateResource{}, failures
|
|
||||||
}
|
|
||||||
|
|
||||||
errs := c.solveChallenges(challenges)
|
|
||||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
|
||||||
if len(errs) > 0 {
|
|
||||||
return CertificateResource{}, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
|
||||||
|
|
||||||
cert, err := c.requestCertificate(challenges, bundle, privKey)
|
|
||||||
if err != nil {
|
|
||||||
for _, chln := range challenges {
|
|
||||||
failures[chln.Domain] = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cert, failures
|
|
||||||
}
|
|
||||||
|
|
||||||
// RevokeCertificate takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
|
|
||||||
func (c *Client) RevokeCertificate(certificate []byte) error {
|
|
||||||
certificates, err := parsePEMBundle(certificate)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
x509Cert := certificates[0]
|
|
||||||
if x509Cert.IsCA {
|
|
||||||
return fmt.Errorf("Certificate bundle starts with a CA certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
encodedCert := base64.URLEncoding.EncodeToString(x509Cert.Raw)
|
|
||||||
|
|
||||||
_, err = postJSON(c.jws, c.directory.RevokeCertURL, revokeCertMessage{Resource: "revoke-cert", Certificate: encodedCert}, nil)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenewCertificate takes a CertificateResource and tries to renew the certificate.
|
|
||||||
// If the renewal process succeeds, the new certificate will ge returned in a new CertResource.
|
|
||||||
// Please be aware that this function will return a new certificate in ANY case that is not an error.
|
|
||||||
// If the server does not provide us with a new cert on a GET request to the CertURL
|
|
||||||
// this function will start a new-cert flow where a new certificate gets generated.
|
|
||||||
// If bundle is true, the []byte contains both the issuer certificate and
|
|
||||||
// your issued certificate as a bundle.
|
|
||||||
// For private key reuse the PrivateKey property of the passed in CertificateResource should be non-nil.
|
|
||||||
func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (CertificateResource, error) {
|
|
||||||
// Input certificate is PEM encoded. Decode it here as we may need the decoded
|
|
||||||
// cert later on in the renewal process. The input may be a bundle or a single certificate.
|
|
||||||
certificates, err := parsePEMBundle(cert.Certificate)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
x509Cert := certificates[0]
|
|
||||||
if x509Cert.IsCA {
|
|
||||||
return CertificateResource{}, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", cert.Domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is just meant to be informal for the user.
|
|
||||||
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
|
|
||||||
logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
|
|
||||||
|
|
||||||
// The first step of renewal is to check if we get a renewed cert
|
|
||||||
// directly from the cert URL.
|
|
||||||
resp, err := httpGet(cert.CertURL)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
serverCertBytes, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCert, err := x509.ParseCertificate(serverCertBytes)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the server responds with a different certificate we are effectively renewed.
|
|
||||||
// TODO: Further test if we can actually use the new certificate (Our private key works)
|
|
||||||
if !x509Cert.Equal(serverCert) {
|
|
||||||
logf("[INFO][%s] acme: Server responded with renewed certificate", cert.Domain)
|
|
||||||
issuedCert := pemEncode(derCertificateBytes(serverCertBytes))
|
|
||||||
// If bundle is true, we want to return a certificate bundle.
|
|
||||||
// To do this, we need the issuer certificate.
|
|
||||||
if bundle {
|
|
||||||
// The issuer certificate link is always supplied via an "up" link
|
|
||||||
// in the response headers of a new certificate.
|
|
||||||
links := parseLinks(resp.Header["Link"])
|
|
||||||
issuerCert, err := c.getIssuerCertificate(links["up"])
|
|
||||||
if err != nil {
|
|
||||||
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
|
|
||||||
logf("[ERROR][%s] acme: Could not bundle issuer certificate: %v", cert.Domain, err)
|
|
||||||
} else {
|
|
||||||
// Success - append the issuer cert to the issued cert.
|
|
||||||
issuerCert = pemEncode(derCertificateBytes(issuerCert))
|
|
||||||
issuedCert = append(issuedCert, issuerCert...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cert.Certificate = issuedCert
|
|
||||||
return cert, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var privKey crypto.PrivateKey
|
|
||||||
if cert.PrivateKey != nil {
|
|
||||||
privKey, err = parsePEMPrivateKey(cert.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var domains []string
|
|
||||||
var failures map[string]error
|
|
||||||
// check for SAN certificate
|
|
||||||
if len(x509Cert.DNSNames) > 1 {
|
|
||||||
domains = append(domains, x509Cert.Subject.CommonName)
|
|
||||||
for _, sanDomain := range x509Cert.DNSNames {
|
|
||||||
if sanDomain == x509Cert.Subject.CommonName {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
domains = append(domains, sanDomain)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
domains = append(domains, x509Cert.Subject.CommonName)
|
|
||||||
}
|
|
||||||
|
|
||||||
newCert, failures := c.ObtainCertificate(domains, bundle, privKey)
|
|
||||||
return newCert, failures[cert.Domain]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looks through the challenge combinations to find a solvable match.
|
|
||||||
// Then solves the challenges in series and returns.
|
|
||||||
func (c *Client) solveChallenges(challenges []authorizationResource) map[string]error {
|
|
||||||
// loop through the resources, basically through the domains.
|
|
||||||
failures := make(map[string]error)
|
|
||||||
for _, authz := range challenges {
|
|
||||||
// no solvers - no solving
|
|
||||||
if solvers := c.chooseSolvers(authz.Body, authz.Domain); solvers != nil {
|
|
||||||
for i, solver := range solvers {
|
|
||||||
// TODO: do not immediately fail if one domain fails to validate.
|
|
||||||
err := solver.Solve(authz.Body.Challenges[i], authz.Domain)
|
|
||||||
if err != nil {
|
|
||||||
failures[authz.Domain] = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
failures[authz.Domain] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Domain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return failures
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks all combinations from the server and returns an array of
|
|
||||||
// solvers which should get executed in series.
|
|
||||||
func (c *Client) chooseSolvers(auth authorization, domain string) map[int]solver {
|
|
||||||
for _, combination := range auth.Combinations {
|
|
||||||
solvers := make(map[int]solver)
|
|
||||||
for _, idx := range combination {
|
|
||||||
if solver, ok := c.solvers[auth.Challenges[idx].Type]; ok {
|
|
||||||
solvers[idx] = solver
|
|
||||||
} else {
|
|
||||||
logf("[INFO][%s] acme: Could not find solver for: %s", domain, auth.Challenges[idx].Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can solve the whole combination, return the solvers
|
|
||||||
if len(solvers) == len(combination) {
|
|
||||||
return solvers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the challenges needed to proof our identifier to the ACME server.
|
|
||||||
func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[string]error) {
|
|
||||||
resc, errc := make(chan authorizationResource), make(chan domainError)
|
|
||||||
|
|
||||||
for _, domain := range domains {
|
|
||||||
go func(domain string) {
|
|
||||||
authMsg := authorization{Resource: "new-authz", Identifier: identifier{Type: "dns", Value: domain}}
|
|
||||||
var authz authorization
|
|
||||||
hdr, err := postJSON(c.jws, c.user.GetRegistration().NewAuthzURL, authMsg, &authz)
|
|
||||||
if err != nil {
|
|
||||||
errc <- domainError{Domain: domain, Error: err}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
links := parseLinks(hdr["Link"])
|
|
||||||
if links["next"] == "" {
|
|
||||||
logf("[ERROR][%s] acme: Server did not provide next link to proceed", domain)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resc <- authorizationResource{Body: authz, NewCertURL: links["next"], AuthURL: hdr.Get("Location"), Domain: domain}
|
|
||||||
}(domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
responses := make(map[string]authorizationResource)
|
|
||||||
failures := make(map[string]error)
|
|
||||||
for i := 0; i < len(domains); i++ {
|
|
||||||
select {
|
|
||||||
case res := <-resc:
|
|
||||||
responses[res.Domain] = res
|
|
||||||
case err := <-errc:
|
|
||||||
failures[err.Domain] = err.Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
challenges := make([]authorizationResource, 0, len(responses))
|
|
||||||
for _, domain := range domains {
|
|
||||||
if challenge, ok := responses[domain]; ok {
|
|
||||||
challenges = append(challenges, challenge)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(resc)
|
|
||||||
close(errc)
|
|
||||||
|
|
||||||
return challenges, failures
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey) (CertificateResource, error) {
|
|
||||||
if len(authz) == 0 {
|
|
||||||
return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!")
|
|
||||||
}
|
|
||||||
|
|
||||||
commonName := authz[0]
|
|
||||||
var err error
|
|
||||||
if privKey == nil {
|
|
||||||
privKey, err = generatePrivateKey(c.keyType)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var san []string
|
|
||||||
var authURLs []string
|
|
||||||
for _, auth := range authz[1:] {
|
|
||||||
san = append(san, auth.Domain)
|
|
||||||
authURLs = append(authURLs, auth.AuthURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: should the CSR be customizable?
|
|
||||||
csr, err := generateCsr(privKey, commonName.Domain, san)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
csrString := base64.URLEncoding.EncodeToString(csr)
|
|
||||||
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: authURLs})
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.jws.post(commonName.NewCertURL, jsonBytes)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
privateKeyPem := pemEncode(privKey)
|
|
||||||
cerRes := CertificateResource{
|
|
||||||
Domain: commonName.Domain,
|
|
||||||
CertURL: resp.Header.Get("Location"),
|
|
||||||
PrivateKey: privateKeyPem}
|
|
||||||
|
|
||||||
for {
|
|
||||||
switch resp.StatusCode {
|
|
||||||
case 201, 202:
|
|
||||||
cert, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
|
|
||||||
resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The server returns a body with a length of zero if the
|
|
||||||
// certificate was not ready at the time this request completed.
|
|
||||||
// Otherwise the body is the certificate.
|
|
||||||
if len(cert) > 0 {
|
|
||||||
|
|
||||||
cerRes.CertStableURL = resp.Header.Get("Content-Location")
|
|
||||||
cerRes.AccountRef = c.user.GetRegistration().URI
|
|
||||||
|
|
||||||
issuedCert := pemEncode(derCertificateBytes(cert))
|
|
||||||
// If bundle is true, we want to return a certificate bundle.
|
|
||||||
// To do this, we need the issuer certificate.
|
|
||||||
if bundle {
|
|
||||||
// The issuer certificate link is always supplied via an "up" link
|
|
||||||
// in the response headers of a new certificate.
|
|
||||||
links := parseLinks(resp.Header["Link"])
|
|
||||||
issuerCert, err := c.getIssuerCertificate(links["up"])
|
|
||||||
if err != nil {
|
|
||||||
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
|
|
||||||
logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err)
|
|
||||||
} else {
|
|
||||||
// Success - append the issuer cert to the issued cert.
|
|
||||||
issuerCert = pemEncode(derCertificateBytes(issuerCert))
|
|
||||||
issuedCert = append(issuedCert, issuerCert...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cerRes.Certificate = issuedCert
|
|
||||||
logf("[INFO][%s] Server responded with a certificate.", commonName.Domain)
|
|
||||||
return cerRes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The certificate was granted but is not yet issued.
|
|
||||||
// Check retry-after and loop.
|
|
||||||
ra := resp.Header.Get("Retry-After")
|
|
||||||
retryAfter, err := strconv.Atoi(ra)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", commonName.Domain, retryAfter)
|
|
||||||
time.Sleep(time.Duration(retryAfter) * time.Second)
|
|
||||||
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return CertificateResource{}, handleHTTPError(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err = httpGet(cerRes.CertURL)
|
|
||||||
if err != nil {
|
|
||||||
return CertificateResource{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getIssuerCertificate requests the issuer certificate and caches it for
|
|
||||||
// subsequent requests.
|
|
||||||
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
|
||||||
logf("[INFO] acme: Requesting issuer cert from %s", url)
|
|
||||||
if c.issuerCert != nil {
|
|
||||||
return c.issuerCert, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := httpGet(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = x509.ParseCertificate(issuerBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.issuerCert = issuerBytes
|
|
||||||
return issuerBytes, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseLinks(links []string) map[string]string {
|
|
||||||
aBrkt := regexp.MustCompile("[<>]")
|
|
||||||
slver := regexp.MustCompile("(.+) *= *\"(.+)\"")
|
|
||||||
linkMap := make(map[string]string)
|
|
||||||
|
|
||||||
for _, link := range links {
|
|
||||||
|
|
||||||
link = aBrkt.ReplaceAllString(link, "")
|
|
||||||
parts := strings.Split(link, ";")
|
|
||||||
|
|
||||||
matches := slver.FindStringSubmatch(parts[1])
|
|
||||||
if len(matches) > 0 {
|
|
||||||
linkMap[matches[2]] = parts[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return linkMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate makes the ACME server start validating a
|
|
||||||
// challenge response, only returning once it is done.
|
|
||||||
func validate(j *jws, domain, uri string, chlng challenge) error {
|
|
||||||
var challengeResponse challenge
|
|
||||||
|
|
||||||
hdr, err := postJSON(j, uri, chlng, &challengeResponse)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// After the path is sent, the ACME server will access our server.
|
|
||||||
// Repeatedly check the server for an updated status on our request.
|
|
||||||
for {
|
|
||||||
switch challengeResponse.Status {
|
|
||||||
case "valid":
|
|
||||||
logf("[INFO][%s] The server validated our request", domain)
|
|
||||||
return nil
|
|
||||||
case "pending":
|
|
||||||
break
|
|
||||||
case "invalid":
|
|
||||||
return handleChallengeError(challengeResponse)
|
|
||||||
default:
|
|
||||||
return errors.New("The server returned an unexpected state.")
|
|
||||||
}
|
|
||||||
|
|
||||||
ra, err := strconv.Atoi(hdr.Get("Retry-After"))
|
|
||||||
if err != nil {
|
|
||||||
// The ACME server MUST return a Retry-After.
|
|
||||||
// If it doesn't, we'll just poll hard.
|
|
||||||
ra = 1
|
|
||||||
}
|
|
||||||
time.Sleep(time.Duration(ra) * time.Second)
|
|
||||||
|
|
||||||
hdr, err = getJSON(uri, &challengeResponse)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
323
vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
323
vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
|
@ -1,323 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/big"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ocsp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// KeyType represents the key algo as well as the key size or curve to use.
|
|
||||||
type KeyType string
|
|
||||||
type derCertificateBytes []byte
|
|
||||||
|
|
||||||
// Constants for all key types we support.
|
|
||||||
const (
|
|
||||||
EC256 = KeyType("P256")
|
|
||||||
EC384 = KeyType("P384")
|
|
||||||
RSA2048 = KeyType("2048")
|
|
||||||
RSA4096 = KeyType("4096")
|
|
||||||
RSA8192 = KeyType("8192")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// OCSPGood means that the certificate is valid.
|
|
||||||
OCSPGood = ocsp.Good
|
|
||||||
// OCSPRevoked means that the certificate has been deliberately revoked.
|
|
||||||
OCSPRevoked = ocsp.Revoked
|
|
||||||
// OCSPUnknown means that the OCSP responder doesn't know about the certificate.
|
|
||||||
OCSPUnknown = ocsp.Unknown
|
|
||||||
// OCSPServerFailed means that the OCSP responder failed to process the request.
|
|
||||||
OCSPServerFailed = ocsp.ServerFailed
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetOCSPForCert takes a PEM encoded cert or cert bundle returning the raw OCSP response,
|
|
||||||
// the parsed response, and an error, if any. The returned []byte can be passed directly
|
|
||||||
// into the OCSPStaple property of a tls.Certificate. If the bundle only contains the
|
|
||||||
// issued certificate, this function will try to get the issuer certificate from the
|
|
||||||
// IssuingCertificateURL in the certificate. If the []byte and/or ocsp.Response return
|
|
||||||
// values are nil, the OCSP status may be assumed OCSPUnknown.
|
|
||||||
func GetOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
|
|
||||||
certificates, err := parsePEMBundle(bundle)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We expect the certificate slice to be ordered downwards the chain.
|
|
||||||
// SRV CRT -> CA. We need to pull the leaf and issuer certs out of it,
|
|
||||||
// which should always be the first two certificates. If there's no
|
|
||||||
// OCSP server listed in the leaf cert, there's nothing to do. And if
|
|
||||||
// we have only one certificate so far, we need to get the issuer cert.
|
|
||||||
issuedCert := certificates[0]
|
|
||||||
if len(issuedCert.OCSPServer) == 0 {
|
|
||||||
return nil, nil, errors.New("no OCSP server specified in cert")
|
|
||||||
}
|
|
||||||
if len(certificates) == 1 {
|
|
||||||
// TODO: build fallback. If this fails, check the remaining array entries.
|
|
||||||
if len(issuedCert.IssuingCertificateURL) == 0 {
|
|
||||||
return nil, nil, errors.New("no issuing certificate URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := httpGet(issuedCert.IssuingCertificateURL[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
issuerCert, err := x509.ParseCertificate(issuerBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert it into the slice on position 0
|
|
||||||
// We want it ordered right SRV CRT -> CA
|
|
||||||
certificates = append(certificates, issuerCert)
|
|
||||||
}
|
|
||||||
issuerCert := certificates[1]
|
|
||||||
|
|
||||||
// Finally kick off the OCSP request.
|
|
||||||
ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := bytes.NewReader(ocspReq)
|
|
||||||
req, err := httpPost(issuedCert.OCSPServer[0], "application/ocsp-request", reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
defer req.Body.Close()
|
|
||||||
|
|
||||||
ocspResBytes, err := ioutil.ReadAll(limitReader(req.Body, 1024*1024))
|
|
||||||
ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ocspRes.Certificate == nil {
|
|
||||||
err = ocspRes.CheckSignatureFrom(issuerCert)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ocspResBytes, ocspRes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getKeyAuthorization(token string, key interface{}) (string, error) {
|
|
||||||
var publicKey crypto.PublicKey
|
|
||||||
switch k := key.(type) {
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
publicKey = k.Public()
|
|
||||||
case *rsa.PrivateKey:
|
|
||||||
publicKey = k.Public()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the Key Authorization for the challenge
|
|
||||||
jwk := keyAsJWK(publicKey)
|
|
||||||
if jwk == nil {
|
|
||||||
return "", errors.New("Could not generate JWK from key.")
|
|
||||||
}
|
|
||||||
thumbBytes, err := jwk.Thumbprint(crypto.SHA256)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpad the base64URL
|
|
||||||
keyThumb := base64.URLEncoding.EncodeToString(thumbBytes)
|
|
||||||
index := strings.Index(keyThumb, "=")
|
|
||||||
if index != -1 {
|
|
||||||
keyThumb = keyThumb[:index]
|
|
||||||
}
|
|
||||||
|
|
||||||
return token + "." + keyThumb, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsePEMBundle parses a certificate bundle from top to bottom and returns
|
|
||||||
// a slice of x509 certificates. This function will error if no certificates are found.
|
|
||||||
func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
|
|
||||||
var certificates []*x509.Certificate
|
|
||||||
var certDERBlock *pem.Block
|
|
||||||
|
|
||||||
for {
|
|
||||||
certDERBlock, bundle = pem.Decode(bundle)
|
|
||||||
if certDERBlock == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if certDERBlock.Type == "CERTIFICATE" {
|
|
||||||
cert, err := x509.ParseCertificate(certDERBlock.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
certificates = append(certificates, cert)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(certificates) == 0 {
|
|
||||||
return nil, errors.New("No certificates were found while parsing the bundle.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return certificates, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
|
||||||
keyBlock, _ := pem.Decode(key)
|
|
||||||
|
|
||||||
switch keyBlock.Type {
|
|
||||||
case "RSA PRIVATE KEY":
|
|
||||||
return x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
|
||||||
case "EC PRIVATE KEY":
|
|
||||||
return x509.ParseECPrivateKey(keyBlock.Bytes)
|
|
||||||
default:
|
|
||||||
return nil, errors.New("Unknown PEM header value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
|
|
||||||
|
|
||||||
switch keyType {
|
|
||||||
case EC256:
|
|
||||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
case EC384:
|
|
||||||
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
||||||
case RSA2048:
|
|
||||||
return rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
case RSA4096:
|
|
||||||
return rsa.GenerateKey(rand.Reader, 4096)
|
|
||||||
case RSA8192:
|
|
||||||
return rsa.GenerateKey(rand.Reader, 8192)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("Invalid KeyType: %s", keyType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateCsr(privateKey crypto.PrivateKey, domain string, san []string) ([]byte, error) {
|
|
||||||
template := x509.CertificateRequest{
|
|
||||||
Subject: pkix.Name{
|
|
||||||
CommonName: domain,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(san) > 0 {
|
|
||||||
template.DNSNames = san
|
|
||||||
}
|
|
||||||
|
|
||||||
return x509.CreateCertificateRequest(rand.Reader, &template, privateKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func pemEncode(data interface{}) []byte {
|
|
||||||
var pemBlock *pem.Block
|
|
||||||
switch key := data.(type) {
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
keyBytes, _ := x509.MarshalECPrivateKey(key)
|
|
||||||
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
|
|
||||||
case *rsa.PrivateKey:
|
|
||||||
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
|
|
||||||
break
|
|
||||||
case derCertificateBytes:
|
|
||||||
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.(derCertificateBytes))}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(pemBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
func pemDecode(data []byte) (*pem.Block, error) {
|
|
||||||
pemBlock, _ := pem.Decode(data)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return nil, fmt.Errorf("Pem decode did not yield a valid block. Is the certificate in the right format?")
|
|
||||||
}
|
|
||||||
|
|
||||||
return pemBlock, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func pemDecodeTox509(pem []byte) (*x509.Certificate, error) {
|
|
||||||
pemBlock, err := pemDecode(pem)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return x509.ParseCertificate(pemBlock.Bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPEMCertExpiration returns the "NotAfter" date of a PEM encoded certificate.
|
|
||||||
// The certificate has to be PEM encoded. Any other encodings like DER will fail.
|
|
||||||
func GetPEMCertExpiration(cert []byte) (time.Time, error) {
|
|
||||||
pemBlock, err := pemDecode(cert)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return getCertExpiration(pemBlock.Bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCertExpiration returns the "NotAfter" date of a DER encoded certificate.
|
|
||||||
func getCertExpiration(cert []byte) (time.Time, error) {
|
|
||||||
pCert, err := x509.ParseCertificate(cert)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pCert.NotAfter, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
|
||||||
derBytes, err := generateDerCert(privKey, time.Time{}, domain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
|
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
||||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if expiration.IsZero() {
|
|
||||||
expiration = time.Now().Add(365)
|
|
||||||
}
|
|
||||||
|
|
||||||
template := x509.Certificate{
|
|
||||||
SerialNumber: serialNumber,
|
|
||||||
Subject: pkix.Name{
|
|
||||||
CommonName: "ACME Challenge TEMP",
|
|
||||||
},
|
|
||||||
NotBefore: time.Now(),
|
|
||||||
NotAfter: expiration,
|
|
||||||
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
DNSNames: []string{domain},
|
|
||||||
}
|
|
||||||
|
|
||||||
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func limitReader(rd io.ReadCloser, numBytes int64) io.ReadCloser {
|
|
||||||
return http.MaxBytesReader(nil, rd, numBytes)
|
|
||||||
}
|
|
279
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
279
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
|
@ -1,279 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"golang.org/x/net/publicsuffix"
|
|
||||||
)
|
|
||||||
|
|
||||||
type preCheckDNSFunc func(fqdn, value string) (bool, error)
|
|
||||||
|
|
||||||
var (
|
|
||||||
preCheckDNS preCheckDNSFunc = checkDNSPropagation
|
|
||||||
fqdnToZone = map[string]string{}
|
|
||||||
)
|
|
||||||
|
|
||||||
var RecursiveNameservers = []string{
|
|
||||||
"google-public-dns-a.google.com:53",
|
|
||||||
"google-public-dns-b.google.com:53",
|
|
||||||
}
|
|
||||||
|
|
||||||
// DNSTimeout is used to override the default DNS timeout of 10 seconds.
|
|
||||||
var DNSTimeout = 10 * time.Second
|
|
||||||
|
|
||||||
// DNS01Record returns a DNS record which will fulfill the `dns-01` challenge
|
|
||||||
func DNS01Record(domain, keyAuth string) (fqdn string, value string, ttl int) {
|
|
||||||
keyAuthShaBytes := sha256.Sum256([]byte(keyAuth))
|
|
||||||
// base64URL encoding without padding
|
|
||||||
keyAuthSha := base64.URLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
|
|
||||||
value = strings.TrimRight(keyAuthSha, "=")
|
|
||||||
ttl = 120
|
|
||||||
fqdn = fmt.Sprintf("_acme-challenge.%s.", domain)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// dnsChallenge implements the dns-01 challenge according to ACME 7.5
|
|
||||||
type dnsChallenge struct {
|
|
||||||
jws *jws
|
|
||||||
validate validateFunc
|
|
||||||
provider ChallengeProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
|
||||||
logf("[INFO][%s] acme: Trying to solve DNS-01", domain)
|
|
||||||
|
|
||||||
if s.provider == nil {
|
|
||||||
return errors.New("No DNS Provider configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the Key Authorization for the challenge
|
|
||||||
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.provider.Present(domain, chlng.Token, keyAuth)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error presenting token: %s", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error cleaning up %s: %v ", domain, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
fqdn, value, _ := DNS01Record(domain, keyAuth)
|
|
||||||
|
|
||||||
logf("[INFO][%s] Checking DNS record propagation...", domain)
|
|
||||||
|
|
||||||
var timeout, interval time.Duration
|
|
||||||
switch provider := s.provider.(type) {
|
|
||||||
case ChallengeProviderTimeout:
|
|
||||||
timeout, interval = provider.Timeout()
|
|
||||||
default:
|
|
||||||
timeout, interval = 60*time.Second, 2*time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
err = WaitFor(timeout, interval, func() (bool, error) {
|
|
||||||
return preCheckDNS(fqdn, value)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.validate(s.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers.
|
|
||||||
func checkDNSPropagation(fqdn, value string) (bool, error) {
|
|
||||||
// Initial attempt to resolve at the recursive NS
|
|
||||||
r, err := dnsQuery(fqdn, dns.TypeTXT, RecursiveNameservers, true)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if r.Rcode == dns.RcodeSuccess {
|
|
||||||
// If we see a CNAME here then use the alias
|
|
||||||
for _, rr := range r.Answer {
|
|
||||||
if cn, ok := rr.(*dns.CNAME); ok {
|
|
||||||
if cn.Hdr.Name == fqdn {
|
|
||||||
fqdn = cn.Target
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
authoritativeNss, err := lookupNameservers(fqdn)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return checkAuthoritativeNss(fqdn, value, authoritativeNss)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkAuthoritativeNss queries each of the given nameservers for the expected TXT record.
|
|
||||||
func checkAuthoritativeNss(fqdn, value string, nameservers []string) (bool, error) {
|
|
||||||
for _, ns := range nameservers {
|
|
||||||
r, err := dnsQuery(fqdn, dns.TypeTXT, []string{net.JoinHostPort(ns, "53")}, false)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
|
||||||
return false, fmt.Errorf("NS %s returned %s for %s", ns, dns.RcodeToString[r.Rcode], fqdn)
|
|
||||||
}
|
|
||||||
|
|
||||||
var found bool
|
|
||||||
for _, rr := range r.Answer {
|
|
||||||
if txt, ok := rr.(*dns.TXT); ok {
|
|
||||||
if strings.Join(txt.Txt, "") == value {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return false, fmt.Errorf("NS %s did not return the expected TXT record", ns)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// dnsQuery will query a nameserver, iterating through the supplied servers as it retries
|
|
||||||
// The nameserver should include a port, to facilitate testing where we talk to a mock dns server.
|
|
||||||
func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (in *dns.Msg, err error) {
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetQuestion(fqdn, rtype)
|
|
||||||
m.SetEdns0(4096, false)
|
|
||||||
|
|
||||||
if !recursive {
|
|
||||||
m.RecursionDesired = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Will retry the request based on the number of servers (n+1)
|
|
||||||
for i := 1; i <= len(nameservers)+1; i++ {
|
|
||||||
ns := nameservers[i%len(nameservers)]
|
|
||||||
udp := &dns.Client{Net: "udp", Timeout: DNSTimeout}
|
|
||||||
in, _, err = udp.Exchange(m, ns)
|
|
||||||
|
|
||||||
if err == dns.ErrTruncated {
|
|
||||||
tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout}
|
|
||||||
// If the TCP request suceeds, the err will reset to nil
|
|
||||||
in, _, err = tcp.Exchange(m, ns)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookupNameservers returns the authoritative nameservers for the given fqdn.
|
|
||||||
func lookupNameservers(fqdn string) ([]string, error) {
|
|
||||||
var authoritativeNss []string
|
|
||||||
|
|
||||||
zone, err := FindZoneByFqdn(fqdn, RecursiveNameservers)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := dnsQuery(zone, dns.TypeNS, RecursiveNameservers, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rr := range r.Answer {
|
|
||||||
if ns, ok := rr.(*dns.NS); ok {
|
|
||||||
authoritativeNss = append(authoritativeNss, strings.ToLower(ns.Ns))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(authoritativeNss) > 0 {
|
|
||||||
return authoritativeNss, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("Could not determine authoritative nameservers")
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindZoneByFqdn determines the zone of the given fqdn
|
|
||||||
func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) {
|
|
||||||
// Do we have it cached?
|
|
||||||
if zone, ok := fqdnToZone[fqdn]; ok {
|
|
||||||
return zone, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query the authoritative nameserver for a hopefully non-existing SOA record,
|
|
||||||
// in the authority section of the reply it will have the SOA of the
|
|
||||||
// containing zone. rfc2308 has this to say on the subject:
|
|
||||||
// Name servers authoritative for a zone MUST include the SOA record of
|
|
||||||
// the zone in the authority section of the response when reporting an
|
|
||||||
// NXDOMAIN or indicating that no data (NODATA) of the requested type exists
|
|
||||||
in, err := dnsQuery(fqdn, dns.TypeSOA, nameservers, true)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if in.Rcode != dns.RcodeNameError {
|
|
||||||
if in.Rcode != dns.RcodeSuccess {
|
|
||||||
return "", fmt.Errorf("The NS returned %s for %s", dns.RcodeToString[in.Rcode], fqdn)
|
|
||||||
}
|
|
||||||
// We have a success, so one of the answers has to be a SOA RR
|
|
||||||
for _, ans := range in.Answer {
|
|
||||||
if soa, ok := ans.(*dns.SOA); ok {
|
|
||||||
return checkIfTLD(fqdn, soa)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Or it is NODATA, fall through to NXDOMAIN
|
|
||||||
}
|
|
||||||
// Search the authority section for our precious SOA RR
|
|
||||||
for _, ns := range in.Ns {
|
|
||||||
if soa, ok := ns.(*dns.SOA); ok {
|
|
||||||
return checkIfTLD(fqdn, soa)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("The NS did not return the expected SOA record in the authority section")
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkIfTLD(fqdn string, soa *dns.SOA) (string, error) {
|
|
||||||
zone := soa.Hdr.Name
|
|
||||||
// If we ended up on one of the TLDs, it means the domain did not exist.
|
|
||||||
publicsuffix, _ := publicsuffix.PublicSuffix(UnFqdn(zone))
|
|
||||||
if publicsuffix == UnFqdn(zone) {
|
|
||||||
return "", fmt.Errorf("Could not determine zone authoritatively")
|
|
||||||
}
|
|
||||||
fqdnToZone[fqdn] = zone
|
|
||||||
return zone, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing.
|
|
||||||
func ClearFqdnCache() {
|
|
||||||
fqdnToZone = map[string]string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToFqdn converts the name into a fqdn appending a trailing dot.
|
|
||||||
func ToFqdn(name string) string {
|
|
||||||
n := len(name)
|
|
||||||
if n == 0 || name[n-1] == '.' {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
return name + "."
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnFqdn converts the fqdn into a name removing the trailing dot.
|
|
||||||
func UnFqdn(name string) string {
|
|
||||||
n := len(name)
|
|
||||||
if n != 0 && name[n-1] == '.' {
|
|
||||||
return name[:n-1]
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
53
vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go
generated
vendored
53
vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go
generated
vendored
|
@ -1,53 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
dnsTemplate = "%s %d IN TXT \"%s\""
|
|
||||||
)
|
|
||||||
|
|
||||||
// DNSProviderManual is an implementation of the ChallengeProvider interface
|
|
||||||
type DNSProviderManual struct{}
|
|
||||||
|
|
||||||
// NewDNSProviderManual returns a DNSProviderManual instance.
|
|
||||||
func NewDNSProviderManual() (*DNSProviderManual, error) {
|
|
||||||
return &DNSProviderManual{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Present prints instructions for manually creating the TXT record
|
|
||||||
func (*DNSProviderManual) Present(domain, token, keyAuth string) error {
|
|
||||||
fqdn, value, ttl := DNS01Record(domain, keyAuth)
|
|
||||||
dnsRecord := fmt.Sprintf(dnsTemplate, fqdn, ttl, value)
|
|
||||||
|
|
||||||
authZone, err := FindZoneByFqdn(fqdn, RecursiveNameservers)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logf("[INFO] acme: Please create the following TXT record in your %s zone:", authZone)
|
|
||||||
logf("[INFO] acme: %s", dnsRecord)
|
|
||||||
logf("[INFO] acme: Press 'Enter' when you are done")
|
|
||||||
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
_, _ = reader.ReadString('\n')
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanUp prints instructions for manually removing the TXT record
|
|
||||||
func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error {
|
|
||||||
fqdn, _, ttl := DNS01Record(domain, keyAuth)
|
|
||||||
dnsRecord := fmt.Sprintf(dnsTemplate, fqdn, ttl, "...")
|
|
||||||
|
|
||||||
authZone, err := FindZoneByFqdn(fqdn, RecursiveNameservers)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone)
|
|
||||||
logf("[INFO] acme: %s", dnsRecord)
|
|
||||||
return nil
|
|
||||||
}
|
|
86
vendor/github.com/xenolf/lego/acme/error.go
generated
vendored
86
vendor/github.com/xenolf/lego/acme/error.go
generated
vendored
|
@ -1,86 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
tosAgreementError = "Must agree to subscriber agreement before any further actions"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RemoteError is the base type for all errors specific to the ACME protocol.
|
|
||||||
type RemoteError struct {
|
|
||||||
StatusCode int `json:"status,omitempty"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Detail string `json:"detail"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e RemoteError) Error() string {
|
|
||||||
return fmt.Sprintf("acme: Error %d - %s - %s", e.StatusCode, e.Type, e.Detail)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TOSError represents the error which is returned if the user needs to
|
|
||||||
// accept the TOS.
|
|
||||||
// TODO: include the new TOS url if we can somehow obtain it.
|
|
||||||
type TOSError struct {
|
|
||||||
RemoteError
|
|
||||||
}
|
|
||||||
|
|
||||||
type domainError struct {
|
|
||||||
Domain string
|
|
||||||
Error error
|
|
||||||
}
|
|
||||||
|
|
||||||
type challengeError struct {
|
|
||||||
RemoteError
|
|
||||||
records []validationRecord
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c challengeError) Error() string {
|
|
||||||
|
|
||||||
var errStr string
|
|
||||||
for _, validation := range c.records {
|
|
||||||
errStr = errStr + fmt.Sprintf("\tValidation for %s:%s\n\tResolved to:\n\t\t%s\n\tUsed: %s\n\n",
|
|
||||||
validation.Hostname, validation.Port, strings.Join(validation.ResolvedAddresses, "\n\t\t"), validation.UsedAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s\nError Detail:\n%s", c.RemoteError.Error(), errStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleHTTPError(resp *http.Response) error {
|
|
||||||
var errorDetail RemoteError
|
|
||||||
|
|
||||||
contenType := resp.Header.Get("Content-Type")
|
|
||||||
// try to decode the content as JSON
|
|
||||||
if contenType == "application/json" || contenType == "application/problem+json" {
|
|
||||||
decoder := json.NewDecoder(resp.Body)
|
|
||||||
err := decoder.Decode(&errorDetail)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
detailBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
errorDetail.Detail = string(detailBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
errorDetail.StatusCode = resp.StatusCode
|
|
||||||
|
|
||||||
// Check for errors we handle specifically
|
|
||||||
if errorDetail.StatusCode == http.StatusForbidden && errorDetail.Detail == tosAgreementError {
|
|
||||||
return TOSError{errorDetail}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorDetail
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleChallengeError(chlng challenge) error {
|
|
||||||
return challengeError{chlng.Error, chlng.ValidationRecords}
|
|
||||||
}
|
|
120
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
120
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
|
@ -1,120 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UserAgent (if non-empty) will be tacked onto the User-Agent string in requests.
|
|
||||||
var UserAgent string
|
|
||||||
|
|
||||||
// HTTPTimeout is used to override the default HTTP timeout of 10 seconds.
|
|
||||||
var HTTPTimeout = 10 * time.Second
|
|
||||||
|
|
||||||
// defaultClient is an HTTP client with a reasonable timeout value.
|
|
||||||
var defaultClient = http.Client{Timeout: HTTPTimeout}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// defaultGoUserAgent is the Go HTTP package user agent string. Too
|
|
||||||
// bad it isn't exported. If it changes, we should update it here, too.
|
|
||||||
defaultGoUserAgent = "Go-http-client/1.1"
|
|
||||||
|
|
||||||
// ourUserAgent is the User-Agent of this underlying library package.
|
|
||||||
ourUserAgent = "xenolf-acme"
|
|
||||||
)
|
|
||||||
|
|
||||||
// httpHead performs a HEAD request with a proper User-Agent string.
|
|
||||||
// The response body (resp.Body) is already closed when this function returns.
|
|
||||||
func httpHead(url string) (resp *http.Response, err error) {
|
|
||||||
req, err := http.NewRequest("HEAD", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("User-Agent", userAgent())
|
|
||||||
|
|
||||||
resp, err = defaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// httpPost performs a POST request with a proper User-Agent string.
|
|
||||||
// Callers should close resp.Body when done reading from it.
|
|
||||||
func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
|
|
||||||
req, err := http.NewRequest("POST", url, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", bodyType)
|
|
||||||
req.Header.Set("User-Agent", userAgent())
|
|
||||||
|
|
||||||
return defaultClient.Do(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// httpGet performs a GET request with a proper User-Agent string.
|
|
||||||
// Callers should close resp.Body when done reading from it.
|
|
||||||
func httpGet(url string) (resp *http.Response, err error) {
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("User-Agent", userAgent())
|
|
||||||
|
|
||||||
return defaultClient.Do(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getJSON performs an HTTP GET request and parses the response body
|
|
||||||
// as JSON, into the provided respBody object.
|
|
||||||
func getJSON(uri string, respBody interface{}) (http.Header, error) {
|
|
||||||
resp, err := httpGet(uri)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get %q: %v", uri, err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode >= http.StatusBadRequest {
|
|
||||||
return resp.Header, handleHTTPError(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.Header, json.NewDecoder(resp.Body).Decode(respBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
// postJSON performs an HTTP POST request and parses the response body
|
|
||||||
// as JSON, into the provided respBody object.
|
|
||||||
func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, error) {
|
|
||||||
jsonBytes, err := json.Marshal(reqBody)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("Failed to marshal network message...")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := j.post(uri, jsonBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to post JWS message. -> %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode >= http.StatusBadRequest {
|
|
||||||
return resp.Header, handleHTTPError(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if respBody == nil {
|
|
||||||
return resp.Header, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.Header, json.NewDecoder(resp.Body).Decode(respBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
// userAgent builds and returns the User-Agent string to use in requests.
|
|
||||||
func userAgent() string {
|
|
||||||
ua := fmt.Sprintf("%s (%s; %s) %s %s", defaultGoUserAgent, runtime.GOOS, runtime.GOARCH, ourUserAgent, UserAgent)
|
|
||||||
return strings.TrimSpace(ua)
|
|
||||||
}
|
|
41
vendor/github.com/xenolf/lego/acme/http_challenge.go
generated
vendored
41
vendor/github.com/xenolf/lego/acme/http_challenge.go
generated
vendored
|
@ -1,41 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type httpChallenge struct {
|
|
||||||
jws *jws
|
|
||||||
validate validateFunc
|
|
||||||
provider ChallengeProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP01ChallengePath returns the URL path for the `http-01` challenge
|
|
||||||
func HTTP01ChallengePath(token string) string {
|
|
||||||
return "/.well-known/acme-challenge/" + token
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
|
||||||
|
|
||||||
logf("[INFO][%s] acme: Trying to solve HTTP-01", domain)
|
|
||||||
|
|
||||||
// Generate the Key Authorization for the challenge
|
|
||||||
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.provider.Present(domain, chlng.Token, keyAuth)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("[%s] error presenting token: %v", domain, err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[%s] error cleaning up: %v", domain, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return s.validate(s.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
|
||||||
}
|
|
79
vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
79
vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
|
@ -1,79 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HTTPProviderServer implements ChallengeProvider for `http-01` challenge
|
|
||||||
// It may be instantiated without using the NewHTTPProviderServer function if
|
|
||||||
// you want only to use the default values.
|
|
||||||
type HTTPProviderServer struct {
|
|
||||||
iface string
|
|
||||||
port string
|
|
||||||
done chan bool
|
|
||||||
listener net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHTTPProviderServer creates a new HTTPProviderServer on the selected interface and port.
|
|
||||||
// Setting iface and / or port to an empty string will make the server fall back to
|
|
||||||
// the "any" interface and port 80 respectively.
|
|
||||||
func NewHTTPProviderServer(iface, port string) *HTTPProviderServer {
|
|
||||||
return &HTTPProviderServer{iface: iface, port: port}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Present starts a web server and makes the token available at `HTTP01ChallengePath(token)` for web requests.
|
|
||||||
func (s *HTTPProviderServer) Present(domain, token, keyAuth string) error {
|
|
||||||
if s.port == "" {
|
|
||||||
s.port = "80"
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
s.listener, err = net.Listen("tcp", net.JoinHostPort(s.iface, s.port))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Could not start HTTP server for challenge -> %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.done = make(chan bool)
|
|
||||||
go s.serve(domain, token, keyAuth)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanUp closes the HTTP server and removes the token from `HTTP01ChallengePath(token)`
|
|
||||||
func (s *HTTPProviderServer) CleanUp(domain, token, keyAuth string) error {
|
|
||||||
if s.listener == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s.listener.Close()
|
|
||||||
<-s.done
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *HTTPProviderServer) serve(domain, token, keyAuth string) {
|
|
||||||
path := HTTP01ChallengePath(token)
|
|
||||||
|
|
||||||
// The handler validates the HOST header and request type.
|
|
||||||
// For validation it then writes the token the server returned with the challenge
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if strings.HasPrefix(r.Host, domain) && r.Method == "GET" {
|
|
||||||
w.Header().Add("Content-Type", "text/plain")
|
|
||||||
w.Write([]byte(keyAuth))
|
|
||||||
logf("[INFO][%s] Served key authentication", domain)
|
|
||||||
} else {
|
|
||||||
logf("[INFO] Received request for domain %s with method %s", r.Host, r.Method)
|
|
||||||
w.Write([]byte("TEST"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
httpServer := &http.Server{
|
|
||||||
Handler: mux,
|
|
||||||
}
|
|
||||||
// Once httpServer is shut down we don't want any lingering
|
|
||||||
// connections, so disable KeepAlives.
|
|
||||||
httpServer.SetKeepAlivesEnabled(false)
|
|
||||||
httpServer.Serve(s.listener)
|
|
||||||
s.done <- true
|
|
||||||
}
|
|
109
vendor/github.com/xenolf/lego/acme/jws.go
generated
vendored
109
vendor/github.com/xenolf/lego/acme/jws.go
generated
vendored
|
@ -1,109 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rsa"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"gopkg.in/square/go-jose.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type jws struct {
|
|
||||||
directoryURL string
|
|
||||||
privKey crypto.PrivateKey
|
|
||||||
nonces []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func keyAsJWK(key interface{}) *jose.JsonWebKey {
|
|
||||||
switch k := key.(type) {
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
return &jose.JsonWebKey{Key: k, Algorithm: "EC"}
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
return &jose.JsonWebKey{Key: k, Algorithm: "RSA"}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Posts a JWS signed message to the specified URL
|
|
||||||
func (j *jws) post(url string, content []byte) (*http.Response, error) {
|
|
||||||
signedContent, err := j.signContent(content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := httpPost(url, "application/jose+json", bytes.NewBuffer([]byte(signedContent.FullSerialize())))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
j.getNonceFromResponse(resp)
|
|
||||||
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jws) signContent(content []byte) (*jose.JsonWebSignature, error) {
|
|
||||||
|
|
||||||
var alg jose.SignatureAlgorithm
|
|
||||||
switch k := j.privKey.(type) {
|
|
||||||
case *rsa.PrivateKey:
|
|
||||||
alg = jose.RS256
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
if k.Curve == elliptic.P256() {
|
|
||||||
alg = jose.ES256
|
|
||||||
} else if k.Curve == elliptic.P384() {
|
|
||||||
alg = jose.ES384
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, err := jose.NewSigner(alg, j.privKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
signer.SetNonceSource(j)
|
|
||||||
|
|
||||||
signed, err := signer.Sign(content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return signed, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jws) getNonceFromResponse(resp *http.Response) error {
|
|
||||||
nonce := resp.Header.Get("Replay-Nonce")
|
|
||||||
if nonce == "" {
|
|
||||||
return fmt.Errorf("Server did not respond with a proper nonce header.")
|
|
||||||
}
|
|
||||||
|
|
||||||
j.nonces = append(j.nonces, nonce)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jws) getNonce() error {
|
|
||||||
resp, err := httpHead(j.directoryURL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return j.getNonceFromResponse(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jws) Nonce() (string, error) {
|
|
||||||
nonce := ""
|
|
||||||
if len(j.nonces) == 0 {
|
|
||||||
err := j.getNonce()
|
|
||||||
if err != nil {
|
|
||||||
return nonce, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(j.nonces) == 0 {
|
|
||||||
return "", fmt.Errorf("Can't get nonce")
|
|
||||||
}
|
|
||||||
nonce, j.nonces = j.nonces[len(j.nonces)-1], j.nonces[:len(j.nonces)-1]
|
|
||||||
return nonce, nil
|
|
||||||
}
|
|
116
vendor/github.com/xenolf/lego/acme/messages.go
generated
vendored
116
vendor/github.com/xenolf/lego/acme/messages.go
generated
vendored
|
@ -1,116 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gopkg.in/square/go-jose.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type directory struct {
|
|
||||||
NewAuthzURL string `json:"new-authz"`
|
|
||||||
NewCertURL string `json:"new-cert"`
|
|
||||||
NewRegURL string `json:"new-reg"`
|
|
||||||
RevokeCertURL string `json:"revoke-cert"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type recoveryKeyMessage struct {
|
|
||||||
Length int `json:"length,omitempty"`
|
|
||||||
Client jose.JsonWebKey `json:"client,omitempty"`
|
|
||||||
Server jose.JsonWebKey `json:"client,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type registrationMessage struct {
|
|
||||||
Resource string `json:"resource"`
|
|
||||||
Contact []string `json:"contact"`
|
|
||||||
Delete bool `json:"delete,omitempty"`
|
|
||||||
// RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registration is returned by the ACME server after the registration
|
|
||||||
// The client implementation should save this registration somewhere.
|
|
||||||
type Registration struct {
|
|
||||||
Resource string `json:"resource,omitempty"`
|
|
||||||
ID int `json:"id"`
|
|
||||||
Key jose.JsonWebKey `json:"key"`
|
|
||||||
Contact []string `json:"contact"`
|
|
||||||
Agreement string `json:"agreement,omitempty"`
|
|
||||||
Authorizations string `json:"authorizations,omitempty"`
|
|
||||||
Certificates string `json:"certificates,omitempty"`
|
|
||||||
// RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegistrationResource represents all important informations about a registration
|
|
||||||
// of which the client needs to keep track itself.
|
|
||||||
type RegistrationResource struct {
|
|
||||||
Body Registration `json:"body,omitempty"`
|
|
||||||
URI string `json:"uri,omitempty"`
|
|
||||||
NewAuthzURL string `json:"new_authzr_uri,omitempty"`
|
|
||||||
TosURL string `json:"terms_of_service,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type authorizationResource struct {
|
|
||||||
Body authorization
|
|
||||||
Domain string
|
|
||||||
NewCertURL string
|
|
||||||
AuthURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
type authorization struct {
|
|
||||||
Resource string `json:"resource,omitempty"`
|
|
||||||
Identifier identifier `json:"identifier"`
|
|
||||||
Status string `json:"status,omitempty"`
|
|
||||||
Expires time.Time `json:"expires,omitempty"`
|
|
||||||
Challenges []challenge `json:"challenges,omitempty"`
|
|
||||||
Combinations [][]int `json:"combinations,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type identifier struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Value string `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type validationRecord struct {
|
|
||||||
URI string `json:"url,omitempty"`
|
|
||||||
Hostname string `json:"hostname,omitempty"`
|
|
||||||
Port string `json:"port,omitempty"`
|
|
||||||
ResolvedAddresses []string `json:"addressesResolved,omitempty"`
|
|
||||||
UsedAddress string `json:"addressUsed,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type challenge struct {
|
|
||||||
Resource string `json:"resource,omitempty"`
|
|
||||||
Type Challenge `json:"type,omitempty"`
|
|
||||||
Status string `json:"status,omitempty"`
|
|
||||||
URI string `json:"uri,omitempty"`
|
|
||||||
Token string `json:"token,omitempty"`
|
|
||||||
KeyAuthorization string `json:"keyAuthorization,omitempty"`
|
|
||||||
TLS bool `json:"tls,omitempty"`
|
|
||||||
Iterations int `json:"n,omitempty"`
|
|
||||||
Error RemoteError `json:"error,omitempty"`
|
|
||||||
ValidationRecords []validationRecord `json:"validationRecord,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type csrMessage struct {
|
|
||||||
Resource string `json:"resource,omitempty"`
|
|
||||||
Csr string `json:"csr"`
|
|
||||||
Authorizations []string `json:"authorizations"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type revokeCertMessage struct {
|
|
||||||
Resource string `json:"resource"`
|
|
||||||
Certificate string `json:"certificate"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CertificateResource represents a CA issued certificate.
|
|
||||||
// PrivateKey and Certificate are both already PEM encoded
|
|
||||||
// and can be directly written to disk. Certificate may
|
|
||||||
// be a certificate bundle, depending on the options supplied
|
|
||||||
// to create it.
|
|
||||||
type CertificateResource struct {
|
|
||||||
Domain string `json:"domain"`
|
|
||||||
CertURL string `json:"certUrl"`
|
|
||||||
CertStableURL string `json:"certStableUrl"`
|
|
||||||
AccountRef string `json:"accountRef,omitempty"`
|
|
||||||
PrivateKey []byte `json:"-"`
|
|
||||||
Certificate []byte `json:"-"`
|
|
||||||
}
|
|
1
vendor/github.com/xenolf/lego/acme/pop_challenge.go
generated
vendored
1
vendor/github.com/xenolf/lego/acme/pop_challenge.go
generated
vendored
|
@ -1 +0,0 @@
|
||||||
package acme
|
|
28
vendor/github.com/xenolf/lego/acme/provider.go
generated
vendored
28
vendor/github.com/xenolf/lego/acme/provider.go
generated
vendored
|
@ -1,28 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// ChallengeProvider enables implementing a custom challenge
|
|
||||||
// provider. Present presents the solution to a challenge available to
|
|
||||||
// be solved. CleanUp will be called by the challenge if Present ends
|
|
||||||
// in a non-error state.
|
|
||||||
type ChallengeProvider interface {
|
|
||||||
Present(domain, token, keyAuth string) error
|
|
||||||
CleanUp(domain, token, keyAuth string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChallengeProviderTimeout allows for implementing a
|
|
||||||
// ChallengeProvider where an unusually long timeout is required when
|
|
||||||
// waiting for an ACME challenge to be satisfied, such as when
|
|
||||||
// checking for DNS record progagation. If an implementor of a
|
|
||||||
// ChallengeProvider provides a Timeout method, then the return values
|
|
||||||
// of the Timeout method will be used when appropriate by the acme
|
|
||||||
// package. The interval value is the time between checks.
|
|
||||||
//
|
|
||||||
// The default values used for timeout and interval are 60 seconds and
|
|
||||||
// 2 seconds respectively. These are used when no Timeout method is
|
|
||||||
// defined for the ChallengeProvider.
|
|
||||||
type ChallengeProviderTimeout interface {
|
|
||||||
ChallengeProvider
|
|
||||||
Timeout() (timeout, interval time.Duration)
|
|
||||||
}
|
|
67
vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go
generated
vendored
67
vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go
generated
vendored
|
@ -1,67 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tlsSNIChallenge struct {
|
|
||||||
jws *jws
|
|
||||||
validate validateFunc
|
|
||||||
provider ChallengeProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tlsSNIChallenge) Solve(chlng challenge, domain string) error {
|
|
||||||
// FIXME: https://github.com/ietf-wg-acme/acme/pull/22
|
|
||||||
// Currently we implement this challenge to track boulder, not the current spec!
|
|
||||||
|
|
||||||
logf("[INFO][%s] acme: Trying to solve TLS-SNI-01", domain)
|
|
||||||
|
|
||||||
// Generate the Key Authorization for the challenge
|
|
||||||
keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = t.provider.Present(domain, chlng.Token, keyAuth)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("[%s] error presenting token: %v", domain, err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err := t.provider.CleanUp(domain, chlng.Token, keyAuth)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[%s] error cleaning up: %v", domain, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return t.validate(t.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
|
|
||||||
func TLSSNI01ChallengeCert(keyAuth string) (tls.Certificate, string, error) {
|
|
||||||
// generate a new RSA key for the certificates
|
|
||||||
tempPrivKey, err := generatePrivateKey(RSA2048)
|
|
||||||
if err != nil {
|
|
||||||
return tls.Certificate{}, "", err
|
|
||||||
}
|
|
||||||
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
|
|
||||||
rsaPrivPEM := pemEncode(rsaPrivKey)
|
|
||||||
|
|
||||||
zBytes := sha256.Sum256([]byte(keyAuth))
|
|
||||||
z := hex.EncodeToString(zBytes[:sha256.Size])
|
|
||||||
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
|
|
||||||
tempCertPEM, err := generatePemCert(rsaPrivKey, domain)
|
|
||||||
if err != nil {
|
|
||||||
return tls.Certificate{}, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
|
||||||
if err != nil {
|
|
||||||
return tls.Certificate{}, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return certificate, domain, nil
|
|
||||||
}
|
|
62
vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go
generated
vendored
62
vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go
generated
vendored
|
@ -1,62 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TLSProviderServer implements ChallengeProvider for `TLS-SNI-01` challenge
|
|
||||||
// It may be instantiated without using the NewTLSProviderServer function if
|
|
||||||
// you want only to use the default values.
|
|
||||||
type TLSProviderServer struct {
|
|
||||||
iface string
|
|
||||||
port string
|
|
||||||
done chan bool
|
|
||||||
listener net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTLSProviderServer creates a new TLSProviderServer on the selected interface and port.
|
|
||||||
// Setting iface and / or port to an empty string will make the server fall back to
|
|
||||||
// the "any" interface and port 443 respectively.
|
|
||||||
func NewTLSProviderServer(iface, port string) *TLSProviderServer {
|
|
||||||
return &TLSProviderServer{iface: iface, port: port}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Present makes the keyAuth available as a cert
|
|
||||||
func (s *TLSProviderServer) Present(domain, token, keyAuth string) error {
|
|
||||||
if s.port == "" {
|
|
||||||
s.port = "443"
|
|
||||||
}
|
|
||||||
|
|
||||||
cert, _, err := TLSSNI01ChallengeCert(keyAuth)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConf := new(tls.Config)
|
|
||||||
tlsConf.Certificates = []tls.Certificate{cert}
|
|
||||||
|
|
||||||
s.listener, err = tls.Listen("tcp", net.JoinHostPort(s.iface, s.port), tlsConf)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Could not start HTTPS server for challenge -> %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.done = make(chan bool)
|
|
||||||
go func() {
|
|
||||||
http.Serve(s.listener, nil)
|
|
||||||
s.done <- true
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanUp closes the HTTP server.
|
|
||||||
func (s *TLSProviderServer) CleanUp(domain, token, keyAuth string) error {
|
|
||||||
if s.listener == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s.listener.Close()
|
|
||||||
<-s.done
|
|
||||||
return nil
|
|
||||||
}
|
|
29
vendor/github.com/xenolf/lego/acme/utils.go
generated
vendored
29
vendor/github.com/xenolf/lego/acme/utils.go
generated
vendored
|
@ -1,29 +0,0 @@
|
||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WaitFor polls the given function 'f', once every 'interval', up to 'timeout'.
|
|
||||||
func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error {
|
|
||||||
var lastErr string
|
|
||||||
timeup := time.After(timeout)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-timeup:
|
|
||||||
return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
stop, err := f()
|
|
||||||
if stop {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
lastErr = err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(interval)
|
|
||||||
}
|
|
||||||
}
|
|
3
vendor/golang.org/x/crypto/README
generated
vendored
3
vendor/golang.org/x/crypto/README
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
This repository holds supplementary Go cryptography libraries.
|
|
||||||
|
|
||||||
To submit changes to this repository, see http://golang.org/doc/contribute.html.
|
|
21
vendor/golang.org/x/crypto/README.md
generated
vendored
Normal file
21
vendor/golang.org/x/crypto/README.md
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Go Cryptography
|
||||||
|
|
||||||
|
This repository holds supplementary Go cryptography libraries.
|
||||||
|
|
||||||
|
## Download/Install
|
||||||
|
|
||||||
|
The easiest way to install is to run `go get -u golang.org/x/crypto/...`. You
|
||||||
|
can also manually git clone the repository to `$GOPATH/src/golang.org/x/crypto`.
|
||||||
|
|
||||||
|
## Report Issues / Send Patches
|
||||||
|
|
||||||
|
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||||
|
this repository, see https://golang.org/doc/contribute.html.
|
||||||
|
|
||||||
|
The main issue tracker for the crypto repository is located at
|
||||||
|
https://github.com/golang/go/issues. Prefix your issue with "x/crypto:" in the
|
||||||
|
subject line, so it is easy to find.
|
||||||
|
|
||||||
|
Note that contributions to the cryptography package receive additional scrutiny
|
||||||
|
due to their sensitive nature. Patches may take longer than normal to receive
|
||||||
|
feedback.
|
922
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
Normal file
922
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
Normal file
|
@ -0,0 +1,922 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package acme provides an implementation of the
|
||||||
|
// Automatic Certificate Management Environment (ACME) spec.
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02 for details.
|
||||||
|
//
|
||||||
|
// Most common scenarios will want to use autocert subdirectory instead,
|
||||||
|
// which provides automatic access to certificates from Let's Encrypt
|
||||||
|
// and any other ACME-based CA.
|
||||||
|
//
|
||||||
|
// This package is a work in progress and makes no API stability promises.
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
||||||
|
LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
|
// ALPNProto is the ALPN protocol name used by a CA server when validating
|
||||||
|
// tls-alpn-01 challenges.
|
||||||
|
//
|
||||||
|
// Package users must ensure their servers can negotiate the ACME ALPN in
|
||||||
|
// order for tls-alpn-01 challenge verifications to succeed.
|
||||||
|
// See the crypto/tls package's Config.NextProtos field.
|
||||||
|
ALPNProto = "acme-tls/1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// idPeACMEIdentifierV1 is the OID for the ACME extension for the TLS-ALPN challenge.
|
||||||
|
var idPeACMEIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxChainLen = 5 // max depth and breadth of a certificate chain
|
||||||
|
maxCertSize = 1 << 20 // max size of a certificate, in bytes
|
||||||
|
|
||||||
|
// Max number of collected nonces kept in memory.
|
||||||
|
// Expect usual peak of 1 or 2.
|
||||||
|
maxNonces = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is an ACME client.
|
||||||
|
// The only required field is Key. An example of creating a client with a new key
|
||||||
|
// is as follows:
|
||||||
|
//
|
||||||
|
// key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// client := &Client{Key: key}
|
||||||
|
//
|
||||||
|
type Client struct {
|
||||||
|
// Key is the account key used to register with a CA and sign requests.
|
||||||
|
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
|
||||||
|
Key crypto.Signer
|
||||||
|
|
||||||
|
// HTTPClient optionally specifies an HTTP client to use
|
||||||
|
// instead of http.DefaultClient.
|
||||||
|
HTTPClient *http.Client
|
||||||
|
|
||||||
|
// DirectoryURL points to the CA directory endpoint.
|
||||||
|
// If empty, LetsEncryptURL is used.
|
||||||
|
// Mutating this value after a successful call of Client's Discover method
|
||||||
|
// will have no effect.
|
||||||
|
DirectoryURL string
|
||||||
|
|
||||||
|
// RetryBackoff computes the duration after which the nth retry of a failed request
|
||||||
|
// should occur. The value of n for the first call on failure is 1.
|
||||||
|
// The values of r and resp are the request and response of the last failed attempt.
|
||||||
|
// If the returned value is negative or zero, no more retries are done and an error
|
||||||
|
// is returned to the caller of the original method.
|
||||||
|
//
|
||||||
|
// Requests which result in a 4xx client error are not retried,
|
||||||
|
// except for 400 Bad Request due to "bad nonce" errors and 429 Too Many Requests.
|
||||||
|
//
|
||||||
|
// If RetryBackoff is nil, a truncated exponential backoff algorithm
|
||||||
|
// with the ceiling of 10 seconds is used, where each subsequent retry n
|
||||||
|
// is done after either ("Retry-After" + jitter) or (2^n seconds + jitter),
|
||||||
|
// preferring the former if "Retry-After" header is found in the resp.
|
||||||
|
// The jitter is a random value up to 1 second.
|
||||||
|
RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
|
||||||
|
|
||||||
|
dirMu sync.Mutex // guards writes to dir
|
||||||
|
dir *Directory // cached result of Client's Discover method
|
||||||
|
|
||||||
|
noncesMu sync.Mutex
|
||||||
|
nonces map[string]struct{} // nonces collected from previous responses
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover performs ACME server discovery using c.DirectoryURL.
|
||||||
|
//
|
||||||
|
// It caches successful result. So, subsequent calls will not result in
|
||||||
|
// a network round-trip. This also means mutating c.DirectoryURL after successful call
|
||||||
|
// of this method will have no effect.
|
||||||
|
func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
|
c.dirMu.Lock()
|
||||||
|
defer c.dirMu.Unlock()
|
||||||
|
if c.dir != nil {
|
||||||
|
return *c.dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dirURL := c.DirectoryURL
|
||||||
|
if dirURL == "" {
|
||||||
|
dirURL = LetsEncryptURL
|
||||||
|
}
|
||||||
|
res, err := c.get(ctx, dirURL, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return Directory{}, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
c.addNonce(res.Header)
|
||||||
|
|
||||||
|
var v struct {
|
||||||
|
Reg string `json:"new-reg"`
|
||||||
|
Authz string `json:"new-authz"`
|
||||||
|
Cert string `json:"new-cert"`
|
||||||
|
Revoke string `json:"revoke-cert"`
|
||||||
|
Meta struct {
|
||||||
|
Terms string `json:"terms-of-service"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
CAA []string `json:"caa-identities"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return Directory{}, err
|
||||||
|
}
|
||||||
|
c.dir = &Directory{
|
||||||
|
RegURL: v.Reg,
|
||||||
|
AuthzURL: v.Authz,
|
||||||
|
CertURL: v.Cert,
|
||||||
|
RevokeURL: v.Revoke,
|
||||||
|
Terms: v.Meta.Terms,
|
||||||
|
Website: v.Meta.Website,
|
||||||
|
CAA: v.Meta.CAA,
|
||||||
|
}
|
||||||
|
return *c.dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
|
||||||
|
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
|
||||||
|
// with a different duration.
|
||||||
|
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
|
||||||
|
//
|
||||||
|
// In the case where CA server does not provide the issued certificate in the response,
|
||||||
|
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
|
||||||
|
// In such a scenario, the caller can cancel the polling with ctx.
|
||||||
|
//
|
||||||
|
// CreateCert returns an error if the CA's response or chain was unreasonably large.
|
||||||
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||||
|
func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := struct {
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
CSR string `json:"csr"`
|
||||||
|
NotBefore string `json:"notBefore,omitempty"`
|
||||||
|
NotAfter string `json:"notAfter,omitempty"`
|
||||||
|
}{
|
||||||
|
Resource: "new-cert",
|
||||||
|
CSR: base64.RawURLEncoding.EncodeToString(csr),
|
||||||
|
}
|
||||||
|
now := timeNow()
|
||||||
|
req.NotBefore = now.Format(time.RFC3339)
|
||||||
|
if exp > 0 {
|
||||||
|
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.post(ctx, c.Key, c.dir.CertURL, req, wantStatus(http.StatusCreated))
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
curl := res.Header.Get("Location") // cert permanent URL
|
||||||
|
if res.ContentLength == 0 {
|
||||||
|
// no cert in the body; poll until we get it
|
||||||
|
cert, err := c.FetchCert(ctx, curl, bundle)
|
||||||
|
return cert, curl, err
|
||||||
|
}
|
||||||
|
// slurp issued cert and CA chain, if requested
|
||||||
|
cert, err := c.responseCert(ctx, res, bundle)
|
||||||
|
return cert, curl, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchCert retrieves already issued certificate from the given url, in DER format.
|
||||||
|
// It retries the request until the certificate is successfully retrieved,
|
||||||
|
// context is cancelled by the caller or an error response is received.
|
||||||
|
//
|
||||||
|
// The returned value will also contain the CA (issuer) certificate if the bundle argument is true.
|
||||||
|
//
|
||||||
|
// FetchCert returns an error if the CA's response or chain was unreasonably large.
|
||||||
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
||||||
|
// and has expected features.
|
||||||
|
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
||||||
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.responseCert(ctx, res, bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
|
||||||
|
//
|
||||||
|
// The key argument, used to sign the request, must be authorized
|
||||||
|
// to revoke the certificate. It's up to the CA to decide which keys are authorized.
|
||||||
|
// For instance, the key pair of the certificate may be authorized.
|
||||||
|
// If the key is nil, c.Key is used instead.
|
||||||
|
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body := &struct {
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
Cert string `json:"certificate"`
|
||||||
|
Reason int `json:"reason"`
|
||||||
|
}{
|
||||||
|
Resource: "revoke-cert",
|
||||||
|
Cert: base64.RawURLEncoding.EncodeToString(cert),
|
||||||
|
Reason: int(reason),
|
||||||
|
}
|
||||||
|
if key == nil {
|
||||||
|
key = c.Key
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, key, c.dir.RevokeURL, body, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
|
||||||
|
// during account registration. See Register method of Client for more details.
|
||||||
|
func AcceptTOS(tosURL string) bool { return true }
|
||||||
|
|
||||||
|
// Register creates a new account registration by following the "new-reg" flow.
|
||||||
|
// It returns the registered account. The account is not modified.
|
||||||
|
//
|
||||||
|
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
|
||||||
|
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
|
||||||
|
// Register calls prompt with a TOS URL provided by the CA. Prompt should report
|
||||||
|
// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
|
||||||
|
func (c *Client) Register(ctx context.Context, a *Account, prompt func(tosURL string) bool) (*Account, error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if a, err = c.doReg(ctx, c.dir.RegURL, "new-reg", a); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var accept bool
|
||||||
|
if a.CurrentTerms != "" && a.CurrentTerms != a.AgreedTerms {
|
||||||
|
accept = prompt(a.CurrentTerms)
|
||||||
|
}
|
||||||
|
if accept {
|
||||||
|
a.AgreedTerms = a.CurrentTerms
|
||||||
|
a, err = c.UpdateReg(ctx, a)
|
||||||
|
}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReg retrieves an existing registration.
|
||||||
|
// The url argument is an Account URI.
|
||||||
|
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
|
||||||
|
a, err := c.doReg(ctx, url, "reg", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a.URI = url
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateReg updates an existing registration.
|
||||||
|
// It returns an updated account copy. The provided account is not modified.
|
||||||
|
func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) {
|
||||||
|
uri := a.URI
|
||||||
|
a, err := c.doReg(ctx, uri, "reg", a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a.URI = uri
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorize performs the initial step in an authorization flow.
|
||||||
|
// The caller will then need to choose from and perform a set of returned
|
||||||
|
// challenges using c.Accept in order to successfully complete authorization.
|
||||||
|
//
|
||||||
|
// If an authorization has been previously granted, the CA may return
|
||||||
|
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
|
||||||
|
// need not fulfill any challenge and can proceed to requesting a certificate.
|
||||||
|
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type authzID struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
req := struct {
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
Identifier authzID `json:"identifier"`
|
||||||
|
}{
|
||||||
|
Resource: "new-authz",
|
||||||
|
Identifier: authzID{Type: "dns", Value: domain},
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, c.Key, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
var v wireAuthz
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
}
|
||||||
|
if v.Status != StatusPending && v.Status != StatusValid {
|
||||||
|
return nil, fmt.Errorf("acme: unexpected status: %s", v.Status)
|
||||||
|
}
|
||||||
|
return v.authorization(res.Header.Get("Location")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthorization retrieves an authorization identified by the given URL.
|
||||||
|
//
|
||||||
|
// If a caller needs to poll an authorization until its status is final,
|
||||||
|
// see the WaitAuthorization method.
|
||||||
|
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
var v wireAuthz
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
}
|
||||||
|
return v.authorization(url), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeAuthorization relinquishes an existing authorization identified
|
||||||
|
// by the given URL.
|
||||||
|
// The url argument is an Authorization.URI value.
|
||||||
|
//
|
||||||
|
// If successful, the caller will be required to obtain a new authorization
|
||||||
|
// using the Authorize method before being able to request a new certificate
|
||||||
|
// for the domain associated with the authorization.
|
||||||
|
//
|
||||||
|
// It does not revoke existing certificates.
|
||||||
|
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
|
req := struct {
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Delete bool `json:"delete"`
|
||||||
|
}{
|
||||||
|
Resource: "authz",
|
||||||
|
Status: "deactivated",
|
||||||
|
Delete: true,
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, c.Key, url, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitAuthorization polls an authorization at the given URL
|
||||||
|
// until it is in one of the final states, StatusValid or StatusInvalid,
|
||||||
|
// the ACME CA responded with a 4xx error code, or the context is done.
|
||||||
|
//
|
||||||
|
// It returns a non-nil Authorization only if its Status is StatusValid.
|
||||||
|
// In all other cases WaitAuthorization returns an error.
|
||||||
|
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
||||||
|
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
|
for {
|
||||||
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var raw wireAuthz
|
||||||
|
err = json.NewDecoder(res.Body).Decode(&raw)
|
||||||
|
res.Body.Close()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
// Skip and retry.
|
||||||
|
case raw.Status == StatusValid:
|
||||||
|
return raw.authorization(url), nil
|
||||||
|
case raw.Status == StatusInvalid:
|
||||||
|
return nil, raw.error(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exponential backoff is implemented in c.get above.
|
||||||
|
// This is just to prevent continuously hitting the CA
|
||||||
|
// while waiting for a final authorization status.
|
||||||
|
d := retryAfter(res.Header.Get("Retry-After"))
|
||||||
|
if d == 0 {
|
||||||
|
// Given that the fastest challenges TLS-SNI and HTTP-01
|
||||||
|
// require a CA to make at least 1 network round trip
|
||||||
|
// and most likely persist a challenge state,
|
||||||
|
// this default delay seems reasonable.
|
||||||
|
d = time.Second
|
||||||
|
}
|
||||||
|
t := time.NewTimer(d)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-t.C:
|
||||||
|
// Retry.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChallenge retrieves the current status of an challenge.
|
||||||
|
//
|
||||||
|
// A client typically polls a challenge status using this method.
|
||||||
|
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
||||||
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
v := wireChallenge{URI: url}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
}
|
||||||
|
return v.challenge(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept informs the server that the client accepts one of its challenges
|
||||||
|
// previously obtained with c.Authorize.
|
||||||
|
//
|
||||||
|
// The server will then perform the validation asynchronously.
|
||||||
|
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
|
||||||
|
auth, err := keyAuth(c.Key.Public(), chal.Token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := struct {
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Auth string `json:"keyAuthorization"`
|
||||||
|
}{
|
||||||
|
Resource: "challenge",
|
||||||
|
Type: chal.Type,
|
||||||
|
Auth: auth,
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, c.Key, chal.URI, req, wantStatus(
|
||||||
|
http.StatusOK, // according to the spec
|
||||||
|
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
var v wireChallenge
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
}
|
||||||
|
return v.challenge(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS01ChallengeRecord returns a DNS record value for a dns-01 challenge response.
|
||||||
|
// A TXT record containing the returned value must be provisioned under
|
||||||
|
// "_acme-challenge" name of the domain being validated.
|
||||||
|
//
|
||||||
|
// The token argument is a Challenge.Token value.
|
||||||
|
func (c *Client) DNS01ChallengeRecord(token string) (string, error) {
|
||||||
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b := sha256.Sum256([]byte(ka))
|
||||||
|
return base64.RawURLEncoding.EncodeToString(b[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP01ChallengeResponse returns the response for an http-01 challenge.
|
||||||
|
// Servers should respond with the value to HTTP requests at the URL path
|
||||||
|
// provided by HTTP01ChallengePath to validate the challenge and prove control
|
||||||
|
// over a domain name.
|
||||||
|
//
|
||||||
|
// The token argument is a Challenge.Token value.
|
||||||
|
func (c *Client) HTTP01ChallengeResponse(token string) (string, error) {
|
||||||
|
return keyAuth(c.Key.Public(), token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP01ChallengePath returns the URL path at which the response for an http-01 challenge
|
||||||
|
// should be provided by the servers.
|
||||||
|
// The response value can be obtained with HTTP01ChallengeResponse.
|
||||||
|
//
|
||||||
|
// The token argument is a Challenge.Token value.
|
||||||
|
func (c *Client) HTTP01ChallengePath(token string) string {
|
||||||
|
return "/.well-known/acme-challenge/" + token
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
|
||||||
|
// Servers can present the certificate to validate the challenge and prove control
|
||||||
|
// over a domain name.
|
||||||
|
//
|
||||||
|
// The implementation is incomplete in that the returned value is a single certificate,
|
||||||
|
// computed only for Z0 of the key authorization. ACME CAs are expected to update
|
||||||
|
// their implementations to use the newer version, TLS-SNI-02.
|
||||||
|
// For more details on TLS-SNI-01 see https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3.
|
||||||
|
//
|
||||||
|
// The token argument is a Challenge.Token value.
|
||||||
|
// If a WithKey option is provided, its private part signs the returned cert,
|
||||||
|
// and the public part is used to specify the signee.
|
||||||
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
|
//
|
||||||
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
|
// the server name of the TLS ClientHello matches exactly the returned name value.
|
||||||
|
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, "", err
|
||||||
|
}
|
||||||
|
b := sha256.Sum256([]byte(ka))
|
||||||
|
h := hex.EncodeToString(b[:])
|
||||||
|
name = fmt.Sprintf("%s.%s.acme.invalid", h[:32], h[32:])
|
||||||
|
cert, err = tlsChallengeCert([]string{name}, opt)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, "", err
|
||||||
|
}
|
||||||
|
return cert, name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
|
||||||
|
// Servers can present the certificate to validate the challenge and prove control
|
||||||
|
// over a domain name. For more details on TLS-SNI-02 see
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3.
|
||||||
|
//
|
||||||
|
// The token argument is a Challenge.Token value.
|
||||||
|
// If a WithKey option is provided, its private part signs the returned cert,
|
||||||
|
// and the public part is used to specify the signee.
|
||||||
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
|
//
|
||||||
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
|
// the server name in the TLS ClientHello matches exactly the returned name value.
|
||||||
|
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
|
b := sha256.Sum256([]byte(token))
|
||||||
|
h := hex.EncodeToString(b[:])
|
||||||
|
sanA := fmt.Sprintf("%s.%s.token.acme.invalid", h[:32], h[32:])
|
||||||
|
|
||||||
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, "", err
|
||||||
|
}
|
||||||
|
b = sha256.Sum256([]byte(ka))
|
||||||
|
h = hex.EncodeToString(b[:])
|
||||||
|
sanB := fmt.Sprintf("%s.%s.ka.acme.invalid", h[:32], h[32:])
|
||||||
|
|
||||||
|
cert, err = tlsChallengeCert([]string{sanA, sanB}, opt)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, "", err
|
||||||
|
}
|
||||||
|
return cert, sanA, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
|
||||||
|
// Servers can present the certificate to validate the challenge and prove control
|
||||||
|
// over a domain name. For more details on TLS-ALPN-01 see
|
||||||
|
// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
|
||||||
|
//
|
||||||
|
// The token argument is a Challenge.Token value.
|
||||||
|
// If a WithKey option is provided, its private part signs the returned cert,
|
||||||
|
// and the public part is used to specify the signee.
|
||||||
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
|
//
|
||||||
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
|
// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
|
||||||
|
// has been specified.
|
||||||
|
func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) (cert tls.Certificate, err error) {
|
||||||
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
shasum := sha256.Sum256([]byte(ka))
|
||||||
|
extValue, err := asn1.Marshal(shasum[:])
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
acmeExtension := pkix.Extension{
|
||||||
|
Id: idPeACMEIdentifierV1,
|
||||||
|
Critical: true,
|
||||||
|
Value: extValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := defaultTLSChallengeCertTemplate()
|
||||||
|
|
||||||
|
var newOpt []CertOption
|
||||||
|
for _, o := range opt {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *certOptTemplate:
|
||||||
|
t := *(*x509.Certificate)(o) // shallow copy is ok
|
||||||
|
tmpl = &t
|
||||||
|
default:
|
||||||
|
newOpt = append(newOpt, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension)
|
||||||
|
newOpt = append(newOpt, WithTemplate(tmpl))
|
||||||
|
return tlsChallengeCert([]string{domain}, newOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// doReg sends all types of registration requests.
|
||||||
|
// The type of request is identified by typ argument, which is a "resource"
|
||||||
|
// in the ACME spec terms.
|
||||||
|
//
|
||||||
|
// A non-nil acct argument indicates whether the intention is to mutate data
|
||||||
|
// of the Account. Only Contact and Agreement of its fields are used
|
||||||
|
// in such cases.
|
||||||
|
func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Account) (*Account, error) {
|
||||||
|
req := struct {
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
Contact []string `json:"contact,omitempty"`
|
||||||
|
Agreement string `json:"agreement,omitempty"`
|
||||||
|
}{
|
||||||
|
Resource: typ,
|
||||||
|
}
|
||||||
|
if acct != nil {
|
||||||
|
req.Contact = acct.Contact
|
||||||
|
req.Agreement = acct.AgreedTerms
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, c.Key, url, req, wantStatus(
|
||||||
|
http.StatusOK, // updates and deletes
|
||||||
|
http.StatusCreated, // new account creation
|
||||||
|
http.StatusAccepted, // Let's Encrypt divergent implementation
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
var v struct {
|
||||||
|
Contact []string
|
||||||
|
Agreement string
|
||||||
|
Authorizations string
|
||||||
|
Certificates string
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
}
|
||||||
|
var tos string
|
||||||
|
if v := linkHeader(res.Header, "terms-of-service"); len(v) > 0 {
|
||||||
|
tos = v[0]
|
||||||
|
}
|
||||||
|
var authz string
|
||||||
|
if v := linkHeader(res.Header, "next"); len(v) > 0 {
|
||||||
|
authz = v[0]
|
||||||
|
}
|
||||||
|
return &Account{
|
||||||
|
URI: res.Header.Get("Location"),
|
||||||
|
Contact: v.Contact,
|
||||||
|
AgreedTerms: v.Agreement,
|
||||||
|
CurrentTerms: tos,
|
||||||
|
Authz: authz,
|
||||||
|
Authorizations: v.Authorizations,
|
||||||
|
Certificates: v.Certificates,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// popNonce returns a nonce value previously stored with c.addNonce
|
||||||
|
// or fetches a fresh one from the given URL.
|
||||||
|
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
||||||
|
c.noncesMu.Lock()
|
||||||
|
defer c.noncesMu.Unlock()
|
||||||
|
if len(c.nonces) == 0 {
|
||||||
|
return c.fetchNonce(ctx, url)
|
||||||
|
}
|
||||||
|
var nonce string
|
||||||
|
for nonce = range c.nonces {
|
||||||
|
delete(c.nonces, nonce)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nonce, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearNonces clears any stored nonces
|
||||||
|
func (c *Client) clearNonces() {
|
||||||
|
c.noncesMu.Lock()
|
||||||
|
defer c.noncesMu.Unlock()
|
||||||
|
c.nonces = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// addNonce stores a nonce value found in h (if any) for future use.
|
||||||
|
func (c *Client) addNonce(h http.Header) {
|
||||||
|
v := nonceFromHeader(h)
|
||||||
|
if v == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.noncesMu.Lock()
|
||||||
|
defer c.noncesMu.Unlock()
|
||||||
|
if len(c.nonces) >= maxNonces {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.nonces == nil {
|
||||||
|
c.nonces = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
c.nonces[v] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
|
||||||
|
r, err := http.NewRequest("HEAD", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resp, err := c.doNoRetry(ctx, r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
nonce := nonceFromHeader(resp.Header)
|
||||||
|
if nonce == "" {
|
||||||
|
if resp.StatusCode > 299 {
|
||||||
|
return "", responseError(resp)
|
||||||
|
}
|
||||||
|
return "", errors.New("acme: nonce not found")
|
||||||
|
}
|
||||||
|
return nonce, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nonceFromHeader(h http.Header) string {
|
||||||
|
return h.Get("Replay-Nonce")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bool) ([][]byte, error) {
|
||||||
|
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: response stream: %v", err)
|
||||||
|
}
|
||||||
|
if len(b) > maxCertSize {
|
||||||
|
return nil, errors.New("acme: certificate is too big")
|
||||||
|
}
|
||||||
|
cert := [][]byte{b}
|
||||||
|
if !bundle {
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append CA chain cert(s).
|
||||||
|
// At least one is required according to the spec:
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.3.1
|
||||||
|
up := linkHeader(res.Header, "up")
|
||||||
|
if len(up) == 0 {
|
||||||
|
return nil, errors.New("acme: rel=up link not found")
|
||||||
|
}
|
||||||
|
if len(up) > maxChainLen {
|
||||||
|
return nil, errors.New("acme: rel=up link is too large")
|
||||||
|
}
|
||||||
|
for _, url := range up {
|
||||||
|
cc, err := c.chainCert(ctx, url, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cert = append(cert, cc...)
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// chainCert fetches CA certificate chain recursively by following "up" links.
|
||||||
|
// Each recursive call increments the depth by 1, resulting in an error
|
||||||
|
// if the recursion level reaches maxChainLen.
|
||||||
|
//
|
||||||
|
// First chainCert call starts with depth of 0.
|
||||||
|
func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte, error) {
|
||||||
|
if depth >= maxChainLen {
|
||||||
|
return nil, errors.New("acme: certificate chain is too deep")
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(b) > maxCertSize {
|
||||||
|
return nil, errors.New("acme: certificate is too big")
|
||||||
|
}
|
||||||
|
chain := [][]byte{b}
|
||||||
|
|
||||||
|
uplink := linkHeader(res.Header, "up")
|
||||||
|
if len(uplink) > maxChainLen {
|
||||||
|
return nil, errors.New("acme: certificate chain is too large")
|
||||||
|
}
|
||||||
|
for _, up := range uplink {
|
||||||
|
cc, err := c.chainCert(ctx, up, depth+1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
chain = append(chain, cc...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// linkHeader returns URI-Reference values of all Link headers
|
||||||
|
// with relation-type rel.
|
||||||
|
// See https://tools.ietf.org/html/rfc5988#section-5 for details.
|
||||||
|
func linkHeader(h http.Header, rel string) []string {
|
||||||
|
var links []string
|
||||||
|
for _, v := range h["Link"] {
|
||||||
|
parts := strings.Split(v, ";")
|
||||||
|
for _, p := range parts {
|
||||||
|
p = strings.TrimSpace(p)
|
||||||
|
if !strings.HasPrefix(p, "rel=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v := strings.Trim(p[4:], `"`); v == rel {
|
||||||
|
links = append(links, strings.Trim(parts[0], "<>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyAuth generates a key authorization string for a given token.
|
||||||
|
func keyAuth(pub crypto.PublicKey, token string) (string, error) {
|
||||||
|
th, err := JWKThumbprint(pub)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s.%s", token, th), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultTLSChallengeCertTemplate is a template used to create challenge certs for TLS challenges.
|
||||||
|
func defaultTLSChallengeCertTemplate() *x509.Certificate {
|
||||||
|
return &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(24 * time.Hour),
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
|
||||||
|
// with the given SANs and auto-generated public/private key pair.
|
||||||
|
// The Subject Common Name is set to the first SAN to aid debugging.
|
||||||
|
// To create a cert with a custom key pair, specify WithKey option.
|
||||||
|
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||||
|
var key crypto.Signer
|
||||||
|
tmpl := defaultTLSChallengeCertTemplate()
|
||||||
|
for _, o := range opt {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *certOptKey:
|
||||||
|
if key != nil {
|
||||||
|
return tls.Certificate{}, errors.New("acme: duplicate key option")
|
||||||
|
}
|
||||||
|
key = o.key
|
||||||
|
case *certOptTemplate:
|
||||||
|
t := *(*x509.Certificate)(o) // shallow copy is ok
|
||||||
|
tmpl = &t
|
||||||
|
default:
|
||||||
|
// package's fault, if we let this happen:
|
||||||
|
panic(fmt.Sprintf("unsupported option type %T", o))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if key == nil {
|
||||||
|
var err error
|
||||||
|
if key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpl.DNSNames = san
|
||||||
|
if len(san) > 0 {
|
||||||
|
tmpl.Subject.CommonName = san[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
return tls.Certificate{
|
||||||
|
Certificate: [][]byte{der},
|
||||||
|
PrivateKey: key,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodePEM returns b encoded as PEM with block of type typ.
|
||||||
|
func encodePEM(typ string, b []byte) []byte {
|
||||||
|
pb := &pem.Block{Type: typ, Bytes: b}
|
||||||
|
return pem.EncodeToMemory(pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeNow is useful for testing for fixed current time.
|
||||||
|
var timeNow = time.Now
|
1139
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
Normal file
1139
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
130
vendor/golang.org/x/crypto/acme/autocert/cache.go
generated
vendored
Normal file
130
vendor/golang.org/x/crypto/acme/autocert/cache.go
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrCacheMiss is returned when a certificate is not found in cache.
|
||||||
|
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
|
||||||
|
|
||||||
|
// Cache is used by Manager to store and retrieve previously obtained certificates
|
||||||
|
// and other account data as opaque blobs.
|
||||||
|
//
|
||||||
|
// Cache implementations should not rely on the key naming pattern. Keys can
|
||||||
|
// include any printable ASCII characters, except the following: \/:*?"<>|
|
||||||
|
type Cache interface {
|
||||||
|
// Get returns a certificate data for the specified key.
|
||||||
|
// If there's no such key, Get returns ErrCacheMiss.
|
||||||
|
Get(ctx context.Context, key string) ([]byte, error)
|
||||||
|
|
||||||
|
// Put stores the data in the cache under the specified key.
|
||||||
|
// Underlying implementations may use any data storage format,
|
||||||
|
// as long as the reverse operation, Get, results in the original data.
|
||||||
|
Put(ctx context.Context, key string, data []byte) error
|
||||||
|
|
||||||
|
// Delete removes a certificate data from the cache under the specified key.
|
||||||
|
// If there's no such key in the cache, Delete returns nil.
|
||||||
|
Delete(ctx context.Context, key string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirCache implements Cache using a directory on the local filesystem.
|
||||||
|
// If the directory does not exist, it will be created with 0700 permissions.
|
||||||
|
type DirCache string
|
||||||
|
|
||||||
|
// Get reads a certificate data from the specified file name.
|
||||||
|
func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
|
||||||
|
name = filepath.Join(string(d), name)
|
||||||
|
var (
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
done = make(chan struct{})
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
data, err = ioutil.ReadFile(name)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, ErrCacheMiss
|
||||||
|
}
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put writes the certificate data to the specified file name.
|
||||||
|
// The file will be created with 0600 permissions.
|
||||||
|
func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
|
||||||
|
if err := os.MkdirAll(string(d), 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
var err error
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
var tmp string
|
||||||
|
if tmp, err = d.writeTempFile(name, data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Don't overwrite the file if the context was canceled.
|
||||||
|
default:
|
||||||
|
newName := filepath.Join(string(d), name)
|
||||||
|
err = os.Rename(tmp, newName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes the specified file name.
|
||||||
|
func (d DirCache) Delete(ctx context.Context, name string) error {
|
||||||
|
name = filepath.Join(string(d), name)
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
done = make(chan struct{})
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
err = os.Remove(name)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeTempFile writes b to a temporary file, closes the file and returns its path.
|
||||||
|
func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) {
|
||||||
|
// TempFile uses 0600 permissions
|
||||||
|
f, err := ioutil.TempFile(string(d), prefix)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if _, err := f.Write(b); err != nil {
|
||||||
|
f.Close()
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return f.Name(), f.Close()
|
||||||
|
}
|
157
vendor/golang.org/x/crypto/acme/autocert/listener.go
generated
vendored
Normal file
157
vendor/golang.org/x/crypto/acme/autocert/listener.go
generated
vendored
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewListener returns a net.Listener that listens on the standard TLS
|
||||||
|
// port (443) on all interfaces and returns *tls.Conn connections with
|
||||||
|
// LetsEncrypt certificates for the provided domain or domains.
|
||||||
|
//
|
||||||
|
// It enables one-line HTTPS servers:
|
||||||
|
//
|
||||||
|
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
|
||||||
|
//
|
||||||
|
// NewListener is a convenience function for a common configuration.
|
||||||
|
// More complex or custom configurations can use the autocert.Manager
|
||||||
|
// type instead.
|
||||||
|
//
|
||||||
|
// Use of this function implies acceptance of the LetsEncrypt Terms of
|
||||||
|
// Service. If domains is not empty, the provided domains are passed
|
||||||
|
// to HostWhitelist. If domains is empty, the listener will do
|
||||||
|
// LetsEncrypt challenges for any requested domain, which is not
|
||||||
|
// recommended.
|
||||||
|
//
|
||||||
|
// Certificates are cached in a "golang-autocert" directory under an
|
||||||
|
// operating system-specific cache or temp directory. This may not
|
||||||
|
// be suitable for servers spanning multiple machines.
|
||||||
|
//
|
||||||
|
// The returned listener uses a *tls.Config that enables HTTP/2, and
|
||||||
|
// should only be used with servers that support HTTP/2.
|
||||||
|
//
|
||||||
|
// The returned Listener also enables TCP keep-alives on the accepted
|
||||||
|
// connections. The returned *tls.Conn are returned before their TLS
|
||||||
|
// handshake has completed.
|
||||||
|
func NewListener(domains ...string) net.Listener {
|
||||||
|
m := &Manager{
|
||||||
|
Prompt: AcceptTOS,
|
||||||
|
}
|
||||||
|
if len(domains) > 0 {
|
||||||
|
m.HostPolicy = HostWhitelist(domains...)
|
||||||
|
}
|
||||||
|
dir := cacheDir()
|
||||||
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||||
|
log.Printf("warning: autocert.NewListener not using a cache: %v", err)
|
||||||
|
} else {
|
||||||
|
m.Cache = DirCache(dir)
|
||||||
|
}
|
||||||
|
return m.Listener()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listener listens on the standard TLS port (443) on all interfaces
|
||||||
|
// and returns a net.Listener returning *tls.Conn connections.
|
||||||
|
//
|
||||||
|
// The returned listener uses a *tls.Config that enables HTTP/2, and
|
||||||
|
// should only be used with servers that support HTTP/2.
|
||||||
|
//
|
||||||
|
// The returned Listener also enables TCP keep-alives on the accepted
|
||||||
|
// connections. The returned *tls.Conn are returned before their TLS
|
||||||
|
// handshake has completed.
|
||||||
|
//
|
||||||
|
// Unlike NewListener, it is the caller's responsibility to initialize
|
||||||
|
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
|
||||||
|
func (m *Manager) Listener() net.Listener {
|
||||||
|
ln := &listener{
|
||||||
|
m: m,
|
||||||
|
conf: m.TLSConfig(),
|
||||||
|
}
|
||||||
|
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
||||||
|
return ln
|
||||||
|
}
|
||||||
|
|
||||||
|
type listener struct {
|
||||||
|
m *Manager
|
||||||
|
conf *tls.Config
|
||||||
|
|
||||||
|
tcpListener net.Listener
|
||||||
|
tcpListenErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *listener) Accept() (net.Conn, error) {
|
||||||
|
if ln.tcpListenErr != nil {
|
||||||
|
return nil, ln.tcpListenErr
|
||||||
|
}
|
||||||
|
conn, err := ln.tcpListener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tcpConn := conn.(*net.TCPConn)
|
||||||
|
|
||||||
|
// Because Listener is a convenience function, help out with
|
||||||
|
// this too. This is not possible for the caller to set once
|
||||||
|
// we return a *tcp.Conn wrapping an inaccessible net.Conn.
|
||||||
|
// If callers don't want this, they can do things the manual
|
||||||
|
// way and tweak as needed. But this is what net/http does
|
||||||
|
// itself, so copy that. If net/http changes, we can change
|
||||||
|
// here too.
|
||||||
|
tcpConn.SetKeepAlive(true)
|
||||||
|
tcpConn.SetKeepAlivePeriod(3 * time.Minute)
|
||||||
|
|
||||||
|
return tls.Server(tcpConn, ln.conf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *listener) Addr() net.Addr {
|
||||||
|
if ln.tcpListener != nil {
|
||||||
|
return ln.tcpListener.Addr()
|
||||||
|
}
|
||||||
|
// net.Listen failed. Return something non-nil in case callers
|
||||||
|
// call Addr before Accept:
|
||||||
|
return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *listener) Close() error {
|
||||||
|
if ln.tcpListenErr != nil {
|
||||||
|
return ln.tcpListenErr
|
||||||
|
}
|
||||||
|
return ln.tcpListener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func homeDir() string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
||||||
|
}
|
||||||
|
if h := os.Getenv("HOME"); h != "" {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheDir() string {
|
||||||
|
const base = "golang-autocert"
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
return filepath.Join(homeDir(), "Library", "Caches", base)
|
||||||
|
case "windows":
|
||||||
|
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
|
||||||
|
if v := os.Getenv(ev); v != "" {
|
||||||
|
return filepath.Join(v, base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Worst case:
|
||||||
|
return filepath.Join(homeDir(), base)
|
||||||
|
}
|
||||||
|
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
|
||||||
|
return filepath.Join(xdg, base)
|
||||||
|
}
|
||||||
|
return filepath.Join(homeDir(), ".cache", base)
|
||||||
|
}
|
141
vendor/golang.org/x/crypto/acme/autocert/renewal.go
generated
vendored
Normal file
141
vendor/golang.org/x/crypto/acme/autocert/renewal.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// renewJitter is the maximum deviation from Manager.RenewBefore.
|
||||||
|
const renewJitter = time.Hour
|
||||||
|
|
||||||
|
// domainRenewal tracks the state used by the periodic timers
|
||||||
|
// renewing a single domain's cert.
|
||||||
|
type domainRenewal struct {
|
||||||
|
m *Manager
|
||||||
|
ck certKey
|
||||||
|
key crypto.Signer
|
||||||
|
|
||||||
|
timerMu sync.Mutex
|
||||||
|
timer *time.Timer
|
||||||
|
}
|
||||||
|
|
||||||
|
// start starts a cert renewal timer at the time
|
||||||
|
// defined by the certificate expiration time exp.
|
||||||
|
//
|
||||||
|
// If the timer is already started, calling start is a noop.
|
||||||
|
func (dr *domainRenewal) start(exp time.Time) {
|
||||||
|
dr.timerMu.Lock()
|
||||||
|
defer dr.timerMu.Unlock()
|
||||||
|
if dr.timer != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop stops the cert renewal timer.
|
||||||
|
// If the timer is already stopped, calling stop is a noop.
|
||||||
|
func (dr *domainRenewal) stop() {
|
||||||
|
dr.timerMu.Lock()
|
||||||
|
defer dr.timerMu.Unlock()
|
||||||
|
if dr.timer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dr.timer.Stop()
|
||||||
|
dr.timer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// renew is called periodically by a timer.
|
||||||
|
// The first renew call is kicked off by dr.start.
|
||||||
|
func (dr *domainRenewal) renew() {
|
||||||
|
dr.timerMu.Lock()
|
||||||
|
defer dr.timerMu.Unlock()
|
||||||
|
if dr.timer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
// TODO: rotate dr.key at some point?
|
||||||
|
next, err := dr.do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
next = renewJitter / 2
|
||||||
|
next += time.Duration(pseudoRand.int63n(int64(next)))
|
||||||
|
}
|
||||||
|
dr.timer = time.AfterFunc(next, dr.renew)
|
||||||
|
testDidRenewLoop(next, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateState locks and replaces the relevant Manager.state item with the given
|
||||||
|
// state. It additionally updates dr.key with the given state's key.
|
||||||
|
func (dr *domainRenewal) updateState(state *certState) {
|
||||||
|
dr.m.stateMu.Lock()
|
||||||
|
defer dr.m.stateMu.Unlock()
|
||||||
|
dr.key = state.key
|
||||||
|
dr.m.state[dr.ck] = state
|
||||||
|
}
|
||||||
|
|
||||||
|
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
|
||||||
|
// Instead, it requests a new certificate independently and, upon success,
|
||||||
|
// replaces dr.m.state item with a new one and updates cache for the given domain.
|
||||||
|
//
|
||||||
|
// It may lock and update the Manager.state if the expiration date of the currently
|
||||||
|
// cached cert is far enough in the future.
|
||||||
|
//
|
||||||
|
// The returned value is a time interval after which the renewal should occur again.
|
||||||
|
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
||||||
|
// a race is likely unavoidable in a distributed environment
|
||||||
|
// but we try nonetheless
|
||||||
|
if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
|
||||||
|
next := dr.next(tlscert.Leaf.NotAfter)
|
||||||
|
if next > dr.m.renewBefore()+renewJitter {
|
||||||
|
signer, ok := tlscert.PrivateKey.(crypto.Signer)
|
||||||
|
if ok {
|
||||||
|
state := &certState{
|
||||||
|
key: signer,
|
||||||
|
cert: tlscert.Certificate,
|
||||||
|
leaf: tlscert.Leaf,
|
||||||
|
}
|
||||||
|
dr.updateState(state)
|
||||||
|
return next, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.ck)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
state := &certState{
|
||||||
|
key: dr.key,
|
||||||
|
cert: der,
|
||||||
|
leaf: leaf,
|
||||||
|
}
|
||||||
|
tlscert, err := state.tlscert()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := dr.m.cachePut(ctx, dr.ck, tlscert); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
dr.updateState(state)
|
||||||
|
return dr.next(leaf.NotAfter), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
|
||||||
|
d := expiry.Sub(dr.m.now()) - dr.m.renewBefore()
|
||||||
|
// add a bit of randomness to renew deadline
|
||||||
|
n := pseudoRand.int63n(int64(renewJitter))
|
||||||
|
d -= time.Duration(n)
|
||||||
|
if d < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
var testDidRenewLoop = func(next time.Duration, err error) {}
|
281
vendor/golang.org/x/crypto/acme/http.go
generated
vendored
Normal file
281
vendor/golang.org/x/crypto/acme/http.go
generated
vendored
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// retryTimer encapsulates common logic for retrying unsuccessful requests.
|
||||||
|
// It is not safe for concurrent use.
|
||||||
|
type retryTimer struct {
|
||||||
|
// backoffFn provides backoff delay sequence for retries.
|
||||||
|
// See Client.RetryBackoff doc comment.
|
||||||
|
backoffFn func(n int, r *http.Request, res *http.Response) time.Duration
|
||||||
|
// n is the current retry attempt.
|
||||||
|
n int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *retryTimer) inc() {
|
||||||
|
t.n++
|
||||||
|
}
|
||||||
|
|
||||||
|
// backoff pauses the current goroutine as described in Client.RetryBackoff.
|
||||||
|
func (t *retryTimer) backoff(ctx context.Context, r *http.Request, res *http.Response) error {
|
||||||
|
d := t.backoffFn(t.n, r, res)
|
||||||
|
if d <= 0 {
|
||||||
|
return fmt.Errorf("acme: no more retries for %s; tried %d time(s)", r.URL, t.n)
|
||||||
|
}
|
||||||
|
wakeup := time.NewTimer(d)
|
||||||
|
defer wakeup.Stop()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-wakeup.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) retryTimer() *retryTimer {
|
||||||
|
f := c.RetryBackoff
|
||||||
|
if f == nil {
|
||||||
|
f = defaultBackoff
|
||||||
|
}
|
||||||
|
return &retryTimer{backoffFn: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultBackoff provides default Client.RetryBackoff implementation
|
||||||
|
// using a truncated exponential backoff algorithm,
|
||||||
|
// as described in Client.RetryBackoff.
|
||||||
|
//
|
||||||
|
// The n argument is always bounded between 1 and 30.
|
||||||
|
// The returned value is always greater than 0.
|
||||||
|
func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
|
||||||
|
const max = 10 * time.Second
|
||||||
|
var jitter time.Duration
|
||||||
|
if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
|
||||||
|
// Set the minimum to 1ms to avoid a case where
|
||||||
|
// an invalid Retry-After value is parsed into 0 below,
|
||||||
|
// resulting in the 0 returned value which would unintentionally
|
||||||
|
// stop the retries.
|
||||||
|
jitter = (1 + time.Duration(x.Int64())) * time.Millisecond
|
||||||
|
}
|
||||||
|
if v, ok := res.Header["Retry-After"]; ok {
|
||||||
|
return retryAfter(v[0]) + jitter
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < 1 {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
if n > 30 {
|
||||||
|
n = 30
|
||||||
|
}
|
||||||
|
d := time.Duration(1<<uint(n-1))*time.Second + jitter
|
||||||
|
if d > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryAfter parses a Retry-After HTTP header value,
|
||||||
|
// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
|
||||||
|
// It returns zero value if v cannot be parsed.
|
||||||
|
func retryAfter(v string) time.Duration {
|
||||||
|
if i, err := strconv.Atoi(v); err == nil {
|
||||||
|
return time.Duration(i) * time.Second
|
||||||
|
}
|
||||||
|
t, err := http.ParseTime(v)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return t.Sub(timeNow())
|
||||||
|
}
|
||||||
|
|
||||||
|
// resOkay is a function that reports whether the provided response is okay.
|
||||||
|
// It is expected to keep the response body unread.
|
||||||
|
type resOkay func(*http.Response) bool
|
||||||
|
|
||||||
|
// wantStatus returns a function which reports whether the code
|
||||||
|
// matches the status code of a response.
|
||||||
|
func wantStatus(codes ...int) resOkay {
|
||||||
|
return func(res *http.Response) bool {
|
||||||
|
for _, code := range codes {
|
||||||
|
if code == res.StatusCode {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get issues an unsigned GET request to the specified URL.
|
||||||
|
// It returns a non-error value only when ok reports true.
|
||||||
|
//
|
||||||
|
// get retries unsuccessful attempts according to c.RetryBackoff
|
||||||
|
// until the context is done or a non-retriable error is received.
|
||||||
|
func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
|
||||||
|
retry := c.retryTimer()
|
||||||
|
for {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res, err := c.doNoRetry(ctx, req)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return nil, err
|
||||||
|
case ok(res):
|
||||||
|
return res, nil
|
||||||
|
case isRetriable(res.StatusCode):
|
||||||
|
retry.inc()
|
||||||
|
resErr := responseError(res)
|
||||||
|
res.Body.Close()
|
||||||
|
// Ignore the error value from retry.backoff
|
||||||
|
// and return the one from last retry, as received from the CA.
|
||||||
|
if retry.backoff(ctx, req, res) != nil {
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
defer res.Body.Close()
|
||||||
|
return nil, responseError(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// post issues a signed POST request in JWS format using the provided key
|
||||||
|
// to the specified URL.
|
||||||
|
// It returns a non-error value only when ok reports true.
|
||||||
|
//
|
||||||
|
// post retries unsuccessful attempts according to c.RetryBackoff
|
||||||
|
// until the context is done or a non-retriable error is received.
|
||||||
|
// It uses postNoRetry to make individual requests.
|
||||||
|
func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body interface{}, ok resOkay) (*http.Response, error) {
|
||||||
|
retry := c.retryTimer()
|
||||||
|
for {
|
||||||
|
res, req, err := c.postNoRetry(ctx, key, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ok(res) {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
resErr := responseError(res)
|
||||||
|
res.Body.Close()
|
||||||
|
switch {
|
||||||
|
// Check for bad nonce before isRetriable because it may have been returned
|
||||||
|
// with an unretriable response code such as 400 Bad Request.
|
||||||
|
case isBadNonce(resErr):
|
||||||
|
// Consider any previously stored nonce values to be invalid.
|
||||||
|
c.clearNonces()
|
||||||
|
case !isRetriable(res.StatusCode):
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
retry.inc()
|
||||||
|
// Ignore the error value from retry.backoff
|
||||||
|
// and return the one from last retry, as received from the CA.
|
||||||
|
if err := retry.backoff(ctx, req, res); err != nil {
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// postNoRetry signs the body with the given key and POSTs it to the provided url.
|
||||||
|
// The body argument must be JSON-serializable.
|
||||||
|
// It is used by c.post to retry unsuccessful attempts.
|
||||||
|
func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
|
||||||
|
nonce, err := c.popNonce(ctx, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
b, err := jwsEncodeJSON(body, key, nonce)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/jose+json")
|
||||||
|
res, err := c.doNoRetry(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
c.addNonce(res.Header)
|
||||||
|
return res, req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// doNoRetry issues a request req, replacing its context (if any) with ctx.
|
||||||
|
func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
|
||||||
|
res, err := c.httpClient().Do(req.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Prefer the unadorned context error.
|
||||||
|
// (The acme package had tests assuming this, previously from ctxhttp's
|
||||||
|
// behavior, predating net/http supporting contexts natively)
|
||||||
|
// TODO(bradfitz): reconsider this in the future. But for now this
|
||||||
|
// requires no test updates.
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) httpClient() *http.Client {
|
||||||
|
if c.HTTPClient != nil {
|
||||||
|
return c.HTTPClient
|
||||||
|
}
|
||||||
|
return http.DefaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBadNonce reports whether err is an ACME "badnonce" error.
|
||||||
|
func isBadNonce(err error) bool {
|
||||||
|
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
|
||||||
|
// However, ACME servers in the wild return their versions of the error.
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
|
||||||
|
// and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
|
||||||
|
ae, ok := err.(*Error)
|
||||||
|
return ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRetriable reports whether a request can be retried
|
||||||
|
// based on the response status code.
|
||||||
|
//
|
||||||
|
// Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
|
||||||
|
// Callers should parse the response and check with isBadNonce.
|
||||||
|
func isRetriable(code int) bool {
|
||||||
|
return code <= 399 || code >= 500 || code == http.StatusTooManyRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseError creates an error of Error type from resp.
|
||||||
|
func responseError(resp *http.Response) error {
|
||||||
|
// don't care if ReadAll returns an error:
|
||||||
|
// json.Unmarshal will fail in that case anyway
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
e := &wireError{Status: resp.StatusCode}
|
||||||
|
if err := json.Unmarshal(b, e); err != nil {
|
||||||
|
// this is not a regular error response:
|
||||||
|
// populate detail with anything we received,
|
||||||
|
// e.Status will already contain HTTP response code value
|
||||||
|
e.Detail = string(b)
|
||||||
|
if e.Detail == "" {
|
||||||
|
e.Detail = resp.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.error(resp.Header)
|
||||||
|
}
|
153
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
Normal file
153
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
_ "crypto/sha512" // need for EC keys
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// jwsEncodeJSON signs claimset using provided key and a nonce.
|
||||||
|
// The result is serialized in JSON format.
|
||||||
|
// See https://tools.ietf.org/html/rfc7515#section-7.
|
||||||
|
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) {
|
||||||
|
jwk, err := jwkEncode(key.Public())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
alg, sha := jwsHasher(key)
|
||||||
|
if alg == "" || !sha.Available() {
|
||||||
|
return nil, ErrUnsupportedKey
|
||||||
|
}
|
||||||
|
phead := fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q}`, alg, jwk, nonce)
|
||||||
|
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
|
||||||
|
cs, err := json.Marshal(claimset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload := base64.RawURLEncoding.EncodeToString(cs)
|
||||||
|
hash := sha.New()
|
||||||
|
hash.Write([]byte(phead + "." + payload))
|
||||||
|
sig, err := jwsSign(key, sha, hash.Sum(nil))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := struct {
|
||||||
|
Protected string `json:"protected"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
Sig string `json:"signature"`
|
||||||
|
}{
|
||||||
|
Protected: phead,
|
||||||
|
Payload: payload,
|
||||||
|
Sig: base64.RawURLEncoding.EncodeToString(sig),
|
||||||
|
}
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
|
||||||
|
// The result is also suitable for creating a JWK thumbprint.
|
||||||
|
// https://tools.ietf.org/html/rfc7517
|
||||||
|
func jwkEncode(pub crypto.PublicKey) (string, error) {
|
||||||
|
switch pub := pub.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
// https://tools.ietf.org/html/rfc7518#section-6.3.1
|
||||||
|
n := pub.N
|
||||||
|
e := big.NewInt(int64(pub.E))
|
||||||
|
// Field order is important.
|
||||||
|
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
|
||||||
|
return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`,
|
||||||
|
base64.RawURLEncoding.EncodeToString(e.Bytes()),
|
||||||
|
base64.RawURLEncoding.EncodeToString(n.Bytes()),
|
||||||
|
), nil
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
// https://tools.ietf.org/html/rfc7518#section-6.2.1
|
||||||
|
p := pub.Curve.Params()
|
||||||
|
n := p.BitSize / 8
|
||||||
|
if p.BitSize%8 != 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
x := pub.X.Bytes()
|
||||||
|
if n > len(x) {
|
||||||
|
x = append(make([]byte, n-len(x)), x...)
|
||||||
|
}
|
||||||
|
y := pub.Y.Bytes()
|
||||||
|
if n > len(y) {
|
||||||
|
y = append(make([]byte, n-len(y)), y...)
|
||||||
|
}
|
||||||
|
// Field order is important.
|
||||||
|
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
|
||||||
|
return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`,
|
||||||
|
p.Name,
|
||||||
|
base64.RawURLEncoding.EncodeToString(x),
|
||||||
|
base64.RawURLEncoding.EncodeToString(y),
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
return "", ErrUnsupportedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// jwsSign signs the digest using the given key.
|
||||||
|
// It returns ErrUnsupportedKey if the key type is unknown.
|
||||||
|
// The hash is used only for RSA keys.
|
||||||
|
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
||||||
|
switch key := key.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return key.Sign(rand.Reader, digest, hash)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
r, s, err := ecdsa.Sign(rand.Reader, key, digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rb, sb := r.Bytes(), s.Bytes()
|
||||||
|
size := key.Params().BitSize / 8
|
||||||
|
if size%8 > 0 {
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
sig := make([]byte, size*2)
|
||||||
|
copy(sig[size-len(rb):], rb)
|
||||||
|
copy(sig[size*2-len(sb):], sb)
|
||||||
|
return sig, nil
|
||||||
|
}
|
||||||
|
return nil, ErrUnsupportedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
||||||
|
// to use for signing a digest with the provided key.
|
||||||
|
// It returns ("", 0) if the key is not supported.
|
||||||
|
func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
|
||||||
|
switch key := key.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return "RS256", crypto.SHA256
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
switch key.Params().Name {
|
||||||
|
case "P-256":
|
||||||
|
return "ES256", crypto.SHA256
|
||||||
|
case "P-384":
|
||||||
|
return "ES384", crypto.SHA384
|
||||||
|
case "P-521":
|
||||||
|
return "ES512", crypto.SHA512
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// JWKThumbprint creates a JWK thumbprint out of pub
|
||||||
|
// as specified in https://tools.ietf.org/html/rfc7638.
|
||||||
|
func JWKThumbprint(pub crypto.PublicKey) (string, error) {
|
||||||
|
jwk, err := jwkEncode(pub)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b := sha256.Sum256([]byte(jwk))
|
||||||
|
return base64.RawURLEncoding.EncodeToString(b[:]), nil
|
||||||
|
}
|
329
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
Normal file
329
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ACME server response statuses used to describe Authorization and Challenge states.
|
||||||
|
const (
|
||||||
|
StatusUnknown = "unknown"
|
||||||
|
StatusPending = "pending"
|
||||||
|
StatusProcessing = "processing"
|
||||||
|
StatusValid = "valid"
|
||||||
|
StatusInvalid = "invalid"
|
||||||
|
StatusRevoked = "revoked"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CRLReasonCode identifies the reason for a certificate revocation.
|
||||||
|
type CRLReasonCode int
|
||||||
|
|
||||||
|
// CRL reason codes as defined in RFC 5280.
|
||||||
|
const (
|
||||||
|
CRLReasonUnspecified CRLReasonCode = 0
|
||||||
|
CRLReasonKeyCompromise CRLReasonCode = 1
|
||||||
|
CRLReasonCACompromise CRLReasonCode = 2
|
||||||
|
CRLReasonAffiliationChanged CRLReasonCode = 3
|
||||||
|
CRLReasonSuperseded CRLReasonCode = 4
|
||||||
|
CRLReasonCessationOfOperation CRLReasonCode = 5
|
||||||
|
CRLReasonCertificateHold CRLReasonCode = 6
|
||||||
|
CRLReasonRemoveFromCRL CRLReasonCode = 8
|
||||||
|
CRLReasonPrivilegeWithdrawn CRLReasonCode = 9
|
||||||
|
CRLReasonAACompromise CRLReasonCode = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
|
||||||
|
var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")
|
||||||
|
|
||||||
|
// Error is an ACME error, defined in Problem Details for HTTP APIs doc
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
|
||||||
|
type Error struct {
|
||||||
|
// StatusCode is The HTTP status code generated by the origin server.
|
||||||
|
StatusCode int
|
||||||
|
// ProblemType is a URI reference that identifies the problem type,
|
||||||
|
// typically in a "urn:acme:error:xxx" form.
|
||||||
|
ProblemType string
|
||||||
|
// Detail is a human-readable explanation specific to this occurrence of the problem.
|
||||||
|
Detail string
|
||||||
|
// Header is the original server error response headers.
|
||||||
|
// It may be nil.
|
||||||
|
Header http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizationError indicates that an authorization for an identifier
|
||||||
|
// did not succeed.
|
||||||
|
// It contains all errors from Challenge items of the failed Authorization.
|
||||||
|
type AuthorizationError struct {
|
||||||
|
// URI uniquely identifies the failed Authorization.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Identifier is an AuthzID.Value of the failed Authorization.
|
||||||
|
Identifier string
|
||||||
|
|
||||||
|
// Errors is a collection of non-nil error values of Challenge items
|
||||||
|
// of the failed Authorization.
|
||||||
|
Errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthorizationError) Error() string {
|
||||||
|
e := make([]string, len(a.Errors))
|
||||||
|
for i, err := range a.Errors {
|
||||||
|
e[i] = err.Error()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimit reports whether err represents a rate limit error and
|
||||||
|
// any Retry-After duration returned by the server.
|
||||||
|
//
|
||||||
|
// See the following for more details on rate limiting:
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-5.6
|
||||||
|
func RateLimit(err error) (time.Duration, bool) {
|
||||||
|
e, ok := err.(*Error)
|
||||||
|
if !ok {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
// Some CA implementations may return incorrect values.
|
||||||
|
// Use case-insensitive comparison.
|
||||||
|
if !strings.HasSuffix(strings.ToLower(e.ProblemType), ":ratelimited") {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if e.Header == nil {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
return retryAfter(e.Header.Get("Retry-After")), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account is a user account. It is associated with a private key.
|
||||||
|
type Account struct {
|
||||||
|
// URI is the account unique ID, which is also a URL used to retrieve
|
||||||
|
// account data from the CA.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Contact is a slice of contact info used during registration.
|
||||||
|
Contact []string
|
||||||
|
|
||||||
|
// The terms user has agreed to.
|
||||||
|
// A value not matching CurrentTerms indicates that the user hasn't agreed
|
||||||
|
// to the actual Terms of Service of the CA.
|
||||||
|
AgreedTerms string
|
||||||
|
|
||||||
|
// Actual terms of a CA.
|
||||||
|
CurrentTerms string
|
||||||
|
|
||||||
|
// Authz is the authorization URL used to initiate a new authz flow.
|
||||||
|
Authz string
|
||||||
|
|
||||||
|
// Authorizations is a URI from which a list of authorizations
|
||||||
|
// granted to this account can be fetched via a GET request.
|
||||||
|
Authorizations string
|
||||||
|
|
||||||
|
// Certificates is a URI from which a list of certificates
|
||||||
|
// issued for this account can be fetched via a GET request.
|
||||||
|
Certificates string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directory is ACME server discovery data.
|
||||||
|
type Directory struct {
|
||||||
|
// RegURL is an account endpoint URL, allowing for creating new
|
||||||
|
// and modifying existing accounts.
|
||||||
|
RegURL string
|
||||||
|
|
||||||
|
// AuthzURL is used to initiate Identifier Authorization flow.
|
||||||
|
AuthzURL string
|
||||||
|
|
||||||
|
// CertURL is a new certificate issuance endpoint URL.
|
||||||
|
CertURL string
|
||||||
|
|
||||||
|
// RevokeURL is used to initiate a certificate revocation flow.
|
||||||
|
RevokeURL string
|
||||||
|
|
||||||
|
// Term is a URI identifying the current terms of service.
|
||||||
|
Terms string
|
||||||
|
|
||||||
|
// Website is an HTTP or HTTPS URL locating a website
|
||||||
|
// providing more information about the ACME server.
|
||||||
|
Website string
|
||||||
|
|
||||||
|
// CAA consists of lowercase hostname elements, which the ACME server
|
||||||
|
// recognises as referring to itself for the purposes of CAA record validation
|
||||||
|
// as defined in RFC6844.
|
||||||
|
CAA []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Challenge encodes a returned CA challenge.
|
||||||
|
// Its Error field may be non-nil if the challenge is part of an Authorization
|
||||||
|
// with StatusInvalid.
|
||||||
|
type Challenge struct {
|
||||||
|
// Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01".
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// URI is where a challenge response can be posted to.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Token is a random value that uniquely identifies the challenge.
|
||||||
|
Token string
|
||||||
|
|
||||||
|
// Status identifies the status of this challenge.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// Error indicates the reason for an authorization failure
|
||||||
|
// when this challenge was used.
|
||||||
|
// The type of a non-nil value is *Error.
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization encodes an authorization response.
|
||||||
|
type Authorization struct {
|
||||||
|
// URI uniquely identifies a authorization.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Status identifies the status of an authorization.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// Identifier is what the account is authorized to represent.
|
||||||
|
Identifier AuthzID
|
||||||
|
|
||||||
|
// Challenges that the client needs to fulfill in order to prove possession
|
||||||
|
// of the identifier (for pending authorizations).
|
||||||
|
// For final authorizations, the challenges that were used.
|
||||||
|
Challenges []*Challenge
|
||||||
|
|
||||||
|
// A collection of sets of challenges, each of which would be sufficient
|
||||||
|
// to prove possession of the identifier.
|
||||||
|
// Clients must complete a set of challenges that covers at least one set.
|
||||||
|
// Challenges are identified by their indices in the challenges array.
|
||||||
|
// If this field is empty, the client needs to complete all challenges.
|
||||||
|
Combinations [][]int
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthzID is an identifier that an account is authorized to represent.
|
||||||
|
type AuthzID struct {
|
||||||
|
Type string // The type of identifier, e.g. "dns".
|
||||||
|
Value string // The identifier itself, e.g. "example.org".
|
||||||
|
}
|
||||||
|
|
||||||
|
// wireAuthz is ACME JSON representation of Authorization objects.
|
||||||
|
type wireAuthz struct {
|
||||||
|
Status string
|
||||||
|
Challenges []wireChallenge
|
||||||
|
Combinations [][]int
|
||||||
|
Identifier struct {
|
||||||
|
Type string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *wireAuthz) authorization(uri string) *Authorization {
|
||||||
|
a := &Authorization{
|
||||||
|
URI: uri,
|
||||||
|
Status: z.Status,
|
||||||
|
Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value},
|
||||||
|
Combinations: z.Combinations, // shallow copy
|
||||||
|
Challenges: make([]*Challenge, len(z.Challenges)),
|
||||||
|
}
|
||||||
|
for i, v := range z.Challenges {
|
||||||
|
a.Challenges[i] = v.challenge()
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *wireAuthz) error(uri string) *AuthorizationError {
|
||||||
|
err := &AuthorizationError{
|
||||||
|
URI: uri,
|
||||||
|
Identifier: z.Identifier.Value,
|
||||||
|
}
|
||||||
|
for _, raw := range z.Challenges {
|
||||||
|
if raw.Error != nil {
|
||||||
|
err.Errors = append(err.Errors, raw.Error.error(nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wireChallenge is ACME JSON challenge representation.
|
||||||
|
type wireChallenge struct {
|
||||||
|
URI string `json:"uri"`
|
||||||
|
Type string
|
||||||
|
Token string
|
||||||
|
Status string
|
||||||
|
Error *wireError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireChallenge) challenge() *Challenge {
|
||||||
|
v := &Challenge{
|
||||||
|
URI: c.URI,
|
||||||
|
Type: c.Type,
|
||||||
|
Token: c.Token,
|
||||||
|
Status: c.Status,
|
||||||
|
}
|
||||||
|
if v.Status == "" {
|
||||||
|
v.Status = StatusPending
|
||||||
|
}
|
||||||
|
if c.Error != nil {
|
||||||
|
v.Error = c.Error.error(nil)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// wireError is a subset of fields of the Problem Details object
|
||||||
|
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
|
||||||
|
type wireError struct {
|
||||||
|
Status int
|
||||||
|
Type string
|
||||||
|
Detail string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *wireError) error(h http.Header) *Error {
|
||||||
|
return &Error{
|
||||||
|
StatusCode: e.Status,
|
||||||
|
ProblemType: e.Type,
|
||||||
|
Detail: e.Detail,
|
||||||
|
Header: h,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertOption is an optional argument type for the TLS ChallengeCert methods for
|
||||||
|
// customizing a temporary certificate for TLS-based challenges.
|
||||||
|
type CertOption interface {
|
||||||
|
privateCertOpt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithKey creates an option holding a private/public key pair.
|
||||||
|
// The private part signs a certificate, and the public part represents the signee.
|
||||||
|
func WithKey(key crypto.Signer) CertOption {
|
||||||
|
return &certOptKey{key}
|
||||||
|
}
|
||||||
|
|
||||||
|
type certOptKey struct {
|
||||||
|
key crypto.Signer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*certOptKey) privateCertOpt() {}
|
||||||
|
|
||||||
|
// WithTemplate creates an option for specifying a certificate template.
|
||||||
|
// See x509.CreateCertificate for template usage details.
|
||||||
|
//
|
||||||
|
// In TLS ChallengeCert methods, the template is also used as parent,
|
||||||
|
// resulting in a self-signed certificate.
|
||||||
|
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
|
||||||
|
func WithTemplate(t *x509.Certificate) CertOption {
|
||||||
|
return (*certOptTemplate)(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
type certOptTemplate x509.Certificate
|
||||||
|
|
||||||
|
func (*certOptTemplate) privateCertOpt() {}
|
11
vendor/golang.org/x/crypto/bcrypt/bcrypt.go
generated
vendored
11
vendor/golang.org/x/crypto/bcrypt/bcrypt.go
generated
vendored
|
@ -12,9 +12,10 @@ import (
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/blowfish"
|
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/blowfish"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -205,7 +206,6 @@ func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
|
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
|
||||||
|
|
||||||
csalt, err := base64Decode(salt)
|
csalt, err := base64Decode(salt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -213,7 +213,8 @@ func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cip
|
||||||
|
|
||||||
// Bug compatibility with C bcrypt implementations. They use the trailing
|
// Bug compatibility with C bcrypt implementations. They use the trailing
|
||||||
// NULL in the key string during expansion.
|
// NULL in the key string during expansion.
|
||||||
ckey := append(key, 0)
|
// We copy the key to prevent changing the underlying array.
|
||||||
|
ckey := append(key[:len(key):len(key)], 0)
|
||||||
|
|
||||||
c, err := blowfish.NewSaltedCipher(ckey, csalt)
|
c, err := blowfish.NewSaltedCipher(ckey, csalt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -240,11 +241,11 @@ func (p *hashed) Hash() []byte {
|
||||||
n = 3
|
n = 3
|
||||||
}
|
}
|
||||||
arr[n] = '$'
|
arr[n] = '$'
|
||||||
n += 1
|
n++
|
||||||
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
|
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
|
||||||
n += 2
|
n += 2
|
||||||
arr[n] = '$'
|
arr[n] = '$'
|
||||||
n += 1
|
n++
|
||||||
copy(arr[n:], p.salt)
|
copy(arr[n:], p.salt)
|
||||||
n += encodedSaltSize
|
n += encodedSaltSize
|
||||||
copy(arr[n:], p.hash)
|
copy(arr[n:], p.hash)
|
||||||
|
|
4
vendor/golang.org/x/crypto/blowfish/cipher.go
generated
vendored
4
vendor/golang.org/x/crypto/blowfish/cipher.go
generated
vendored
|
@ -6,7 +6,7 @@
|
||||||
package blowfish // import "golang.org/x/crypto/blowfish"
|
package blowfish // import "golang.org/x/crypto/blowfish"
|
||||||
|
|
||||||
// The code is a port of Bruce Schneier's C implementation.
|
// The code is a port of Bruce Schneier's C implementation.
|
||||||
// See http://www.schneier.com/blowfish.html.
|
// See https://www.schneier.com/blowfish.html.
|
||||||
|
|
||||||
import "strconv"
|
import "strconv"
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ func NewCipher(key []byte) (*Cipher, error) {
|
||||||
|
|
||||||
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
||||||
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
||||||
// sufficient and desirable. For bcrypt compatiblity, the key can be over 56
|
// sufficient and desirable. For bcrypt compatibility, the key can be over 56
|
||||||
// bytes.
|
// bytes.
|
||||||
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
|
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
|
||||||
if len(salt) == 0 {
|
if len(salt) == 0 {
|
||||||
|
|
2
vendor/golang.org/x/crypto/blowfish/const.go
generated
vendored
2
vendor/golang.org/x/crypto/blowfish/const.go
generated
vendored
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
// The startup permutation array and substitution boxes.
|
// The startup permutation array and substitution boxes.
|
||||||
// They are the hexadecimal digits of PI; see:
|
// They are the hexadecimal digits of PI; see:
|
||||||
// http://www.schneier.com/code/constants.txt.
|
// https://www.schneier.com/code/constants.txt.
|
||||||
|
|
||||||
package blowfish
|
package blowfish
|
||||||
|
|
||||||
|
|
8
vendor/golang.org/x/crypto/curve25519/const_amd64.h
generated
vendored
Normal file
8
vendor/golang.org/x/crypto/curve25519/const_amd64.h
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This code was translated into a form compatible with 6a from the public
|
||||||
|
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||||
|
|
||||||
|
#define REDMASK51 0x0007FFFFFFFFFFFF
|
20
vendor/golang.org/x/crypto/curve25519/const_amd64.s
generated
vendored
Normal file
20
vendor/golang.org/x/crypto/curve25519/const_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This code was translated into a form compatible with 6a from the public
|
||||||
|
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
// These constants cannot be encoded in non-MOVQ immediates.
|
||||||
|
// We access them directly from memory instead.
|
||||||
|
|
||||||
|
DATA ·_121666_213(SB)/8, $996687872
|
||||||
|
GLOBL ·_121666_213(SB), 8, $8
|
||||||
|
|
||||||
|
DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA
|
||||||
|
GLOBL ·_2P0(SB), 8, $8
|
||||||
|
|
||||||
|
DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE
|
||||||
|
GLOBL ·_2P1234(SB), 8, $8
|
65
vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
generated
vendored
Normal file
65
vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
// func cswap(inout *[4][5]uint64, v uint64)
|
||||||
|
TEXT ·cswap(SB),7,$0
|
||||||
|
MOVQ inout+0(FP),DI
|
||||||
|
MOVQ v+8(FP),SI
|
||||||
|
|
||||||
|
SUBQ $1, SI
|
||||||
|
NOTQ SI
|
||||||
|
MOVQ SI, X15
|
||||||
|
PSHUFD $0x44, X15, X15
|
||||||
|
|
||||||
|
MOVOU 0(DI), X0
|
||||||
|
MOVOU 16(DI), X2
|
||||||
|
MOVOU 32(DI), X4
|
||||||
|
MOVOU 48(DI), X6
|
||||||
|
MOVOU 64(DI), X8
|
||||||
|
MOVOU 80(DI), X1
|
||||||
|
MOVOU 96(DI), X3
|
||||||
|
MOVOU 112(DI), X5
|
||||||
|
MOVOU 128(DI), X7
|
||||||
|
MOVOU 144(DI), X9
|
||||||
|
|
||||||
|
MOVO X1, X10
|
||||||
|
MOVO X3, X11
|
||||||
|
MOVO X5, X12
|
||||||
|
MOVO X7, X13
|
||||||
|
MOVO X9, X14
|
||||||
|
|
||||||
|
PXOR X0, X10
|
||||||
|
PXOR X2, X11
|
||||||
|
PXOR X4, X12
|
||||||
|
PXOR X6, X13
|
||||||
|
PXOR X8, X14
|
||||||
|
PAND X15, X10
|
||||||
|
PAND X15, X11
|
||||||
|
PAND X15, X12
|
||||||
|
PAND X15, X13
|
||||||
|
PAND X15, X14
|
||||||
|
PXOR X10, X0
|
||||||
|
PXOR X10, X1
|
||||||
|
PXOR X11, X2
|
||||||
|
PXOR X11, X3
|
||||||
|
PXOR X12, X4
|
||||||
|
PXOR X12, X5
|
||||||
|
PXOR X13, X6
|
||||||
|
PXOR X13, X7
|
||||||
|
PXOR X14, X8
|
||||||
|
PXOR X14, X9
|
||||||
|
|
||||||
|
MOVOU X0, 0(DI)
|
||||||
|
MOVOU X2, 16(DI)
|
||||||
|
MOVOU X4, 32(DI)
|
||||||
|
MOVOU X6, 48(DI)
|
||||||
|
MOVOU X8, 64(DI)
|
||||||
|
MOVOU X1, 80(DI)
|
||||||
|
MOVOU X3, 96(DI)
|
||||||
|
MOVOU X5, 112(DI)
|
||||||
|
MOVOU X7, 128(DI)
|
||||||
|
MOVOU X9, 144(DI)
|
||||||
|
RET
|
834
vendor/golang.org/x/crypto/curve25519/curve25519.go
generated
vendored
Normal file
834
vendor/golang.org/x/crypto/curve25519/curve25519.go
generated
vendored
Normal file
|
@ -0,0 +1,834 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// We have an implementation in amd64 assembly so this code is only run on
|
||||||
|
// non-amd64 platforms. The amd64 assembly does not support gccgo.
|
||||||
|
// +build !amd64 gccgo appengine
|
||||||
|
|
||||||
|
package curve25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This code is a port of the public domain, "ref10" implementation of
|
||||||
|
// curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
|
||||||
|
|
||||||
|
// fieldElement represents an element of the field GF(2^255 - 19). An element
|
||||||
|
// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
|
||||||
|
// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
|
||||||
|
// context.
|
||||||
|
type fieldElement [10]int32
|
||||||
|
|
||||||
|
func feZero(fe *fieldElement) {
|
||||||
|
for i := range fe {
|
||||||
|
fe[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func feOne(fe *fieldElement) {
|
||||||
|
feZero(fe)
|
||||||
|
fe[0] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func feAdd(dst, a, b *fieldElement) {
|
||||||
|
for i := range dst {
|
||||||
|
dst[i] = a[i] + b[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func feSub(dst, a, b *fieldElement) {
|
||||||
|
for i := range dst {
|
||||||
|
dst[i] = a[i] - b[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func feCopy(dst, src *fieldElement) {
|
||||||
|
for i := range dst {
|
||||||
|
dst[i] = src[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0.
|
||||||
|
//
|
||||||
|
// Preconditions: b in {0,1}.
|
||||||
|
func feCSwap(f, g *fieldElement, b int32) {
|
||||||
|
b = -b
|
||||||
|
for i := range f {
|
||||||
|
t := b & (f[i] ^ g[i])
|
||||||
|
f[i] ^= t
|
||||||
|
g[i] ^= t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load3 reads a 24-bit, little-endian value from in.
|
||||||
|
func load3(in []byte) int64 {
|
||||||
|
var r int64
|
||||||
|
r = int64(in[0])
|
||||||
|
r |= int64(in[1]) << 8
|
||||||
|
r |= int64(in[2]) << 16
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// load4 reads a 32-bit, little-endian value from in.
|
||||||
|
func load4(in []byte) int64 {
|
||||||
|
return int64(binary.LittleEndian.Uint32(in))
|
||||||
|
}
|
||||||
|
|
||||||
|
func feFromBytes(dst *fieldElement, src *[32]byte) {
|
||||||
|
h0 := load4(src[:])
|
||||||
|
h1 := load3(src[4:]) << 6
|
||||||
|
h2 := load3(src[7:]) << 5
|
||||||
|
h3 := load3(src[10:]) << 3
|
||||||
|
h4 := load3(src[13:]) << 2
|
||||||
|
h5 := load4(src[16:])
|
||||||
|
h6 := load3(src[20:]) << 7
|
||||||
|
h7 := load3(src[23:]) << 5
|
||||||
|
h8 := load3(src[26:]) << 4
|
||||||
|
h9 := load3(src[29:]) << 2
|
||||||
|
|
||||||
|
var carry [10]int64
|
||||||
|
carry[9] = (h9 + 1<<24) >> 25
|
||||||
|
h0 += carry[9] * 19
|
||||||
|
h9 -= carry[9] << 25
|
||||||
|
carry[1] = (h1 + 1<<24) >> 25
|
||||||
|
h2 += carry[1]
|
||||||
|
h1 -= carry[1] << 25
|
||||||
|
carry[3] = (h3 + 1<<24) >> 25
|
||||||
|
h4 += carry[3]
|
||||||
|
h3 -= carry[3] << 25
|
||||||
|
carry[5] = (h5 + 1<<24) >> 25
|
||||||
|
h6 += carry[5]
|
||||||
|
h5 -= carry[5] << 25
|
||||||
|
carry[7] = (h7 + 1<<24) >> 25
|
||||||
|
h8 += carry[7]
|
||||||
|
h7 -= carry[7] << 25
|
||||||
|
|
||||||
|
carry[0] = (h0 + 1<<25) >> 26
|
||||||
|
h1 += carry[0]
|
||||||
|
h0 -= carry[0] << 26
|
||||||
|
carry[2] = (h2 + 1<<25) >> 26
|
||||||
|
h3 += carry[2]
|
||||||
|
h2 -= carry[2] << 26
|
||||||
|
carry[4] = (h4 + 1<<25) >> 26
|
||||||
|
h5 += carry[4]
|
||||||
|
h4 -= carry[4] << 26
|
||||||
|
carry[6] = (h6 + 1<<25) >> 26
|
||||||
|
h7 += carry[6]
|
||||||
|
h6 -= carry[6] << 26
|
||||||
|
carry[8] = (h8 + 1<<25) >> 26
|
||||||
|
h9 += carry[8]
|
||||||
|
h8 -= carry[8] << 26
|
||||||
|
|
||||||
|
dst[0] = int32(h0)
|
||||||
|
dst[1] = int32(h1)
|
||||||
|
dst[2] = int32(h2)
|
||||||
|
dst[3] = int32(h3)
|
||||||
|
dst[4] = int32(h4)
|
||||||
|
dst[5] = int32(h5)
|
||||||
|
dst[6] = int32(h6)
|
||||||
|
dst[7] = int32(h7)
|
||||||
|
dst[8] = int32(h8)
|
||||||
|
dst[9] = int32(h9)
|
||||||
|
}
|
||||||
|
|
||||||
|
// feToBytes marshals h to s.
|
||||||
|
// Preconditions:
|
||||||
|
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||||
|
//
|
||||||
|
// Write p=2^255-19; q=floor(h/p).
|
||||||
|
// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
|
||||||
|
//
|
||||||
|
// Proof:
|
||||||
|
// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
|
||||||
|
// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
|
||||||
|
//
|
||||||
|
// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
|
||||||
|
// Then 0<y<1.
|
||||||
|
//
|
||||||
|
// Write r=h-pq.
|
||||||
|
// Have 0<=r<=p-1=2^255-20.
|
||||||
|
// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
|
||||||
|
//
|
||||||
|
// Write x=r+19(2^-255)r+y.
|
||||||
|
// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
|
||||||
|
//
|
||||||
|
// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
|
||||||
|
// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
|
||||||
|
func feToBytes(s *[32]byte, h *fieldElement) {
|
||||||
|
var carry [10]int32
|
||||||
|
|
||||||
|
q := (19*h[9] + (1 << 24)) >> 25
|
||||||
|
q = (h[0] + q) >> 26
|
||||||
|
q = (h[1] + q) >> 25
|
||||||
|
q = (h[2] + q) >> 26
|
||||||
|
q = (h[3] + q) >> 25
|
||||||
|
q = (h[4] + q) >> 26
|
||||||
|
q = (h[5] + q) >> 25
|
||||||
|
q = (h[6] + q) >> 26
|
||||||
|
q = (h[7] + q) >> 25
|
||||||
|
q = (h[8] + q) >> 26
|
||||||
|
q = (h[9] + q) >> 25
|
||||||
|
|
||||||
|
// Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
|
||||||
|
h[0] += 19 * q
|
||||||
|
// Goal: Output h-2^255 q, which is between 0 and 2^255-20.
|
||||||
|
|
||||||
|
carry[0] = h[0] >> 26
|
||||||
|
h[1] += carry[0]
|
||||||
|
h[0] -= carry[0] << 26
|
||||||
|
carry[1] = h[1] >> 25
|
||||||
|
h[2] += carry[1]
|
||||||
|
h[1] -= carry[1] << 25
|
||||||
|
carry[2] = h[2] >> 26
|
||||||
|
h[3] += carry[2]
|
||||||
|
h[2] -= carry[2] << 26
|
||||||
|
carry[3] = h[3] >> 25
|
||||||
|
h[4] += carry[3]
|
||||||
|
h[3] -= carry[3] << 25
|
||||||
|
carry[4] = h[4] >> 26
|
||||||
|
h[5] += carry[4]
|
||||||
|
h[4] -= carry[4] << 26
|
||||||
|
carry[5] = h[5] >> 25
|
||||||
|
h[6] += carry[5]
|
||||||
|
h[5] -= carry[5] << 25
|
||||||
|
carry[6] = h[6] >> 26
|
||||||
|
h[7] += carry[6]
|
||||||
|
h[6] -= carry[6] << 26
|
||||||
|
carry[7] = h[7] >> 25
|
||||||
|
h[8] += carry[7]
|
||||||
|
h[7] -= carry[7] << 25
|
||||||
|
carry[8] = h[8] >> 26
|
||||||
|
h[9] += carry[8]
|
||||||
|
h[8] -= carry[8] << 26
|
||||||
|
carry[9] = h[9] >> 25
|
||||||
|
h[9] -= carry[9] << 25
|
||||||
|
// h10 = carry9
|
||||||
|
|
||||||
|
// Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
|
||||||
|
// Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
|
||||||
|
// evidently 2^255 h10-2^255 q = 0.
|
||||||
|
// Goal: Output h[0]+...+2^230 h[9].
|
||||||
|
|
||||||
|
s[0] = byte(h[0] >> 0)
|
||||||
|
s[1] = byte(h[0] >> 8)
|
||||||
|
s[2] = byte(h[0] >> 16)
|
||||||
|
s[3] = byte((h[0] >> 24) | (h[1] << 2))
|
||||||
|
s[4] = byte(h[1] >> 6)
|
||||||
|
s[5] = byte(h[1] >> 14)
|
||||||
|
s[6] = byte((h[1] >> 22) | (h[2] << 3))
|
||||||
|
s[7] = byte(h[2] >> 5)
|
||||||
|
s[8] = byte(h[2] >> 13)
|
||||||
|
s[9] = byte((h[2] >> 21) | (h[3] << 5))
|
||||||
|
s[10] = byte(h[3] >> 3)
|
||||||
|
s[11] = byte(h[3] >> 11)
|
||||||
|
s[12] = byte((h[3] >> 19) | (h[4] << 6))
|
||||||
|
s[13] = byte(h[4] >> 2)
|
||||||
|
s[14] = byte(h[4] >> 10)
|
||||||
|
s[15] = byte(h[4] >> 18)
|
||||||
|
s[16] = byte(h[5] >> 0)
|
||||||
|
s[17] = byte(h[5] >> 8)
|
||||||
|
s[18] = byte(h[5] >> 16)
|
||||||
|
s[19] = byte((h[5] >> 24) | (h[6] << 1))
|
||||||
|
s[20] = byte(h[6] >> 7)
|
||||||
|
s[21] = byte(h[6] >> 15)
|
||||||
|
s[22] = byte((h[6] >> 23) | (h[7] << 3))
|
||||||
|
s[23] = byte(h[7] >> 5)
|
||||||
|
s[24] = byte(h[7] >> 13)
|
||||||
|
s[25] = byte((h[7] >> 21) | (h[8] << 4))
|
||||||
|
s[26] = byte(h[8] >> 4)
|
||||||
|
s[27] = byte(h[8] >> 12)
|
||||||
|
s[28] = byte((h[8] >> 20) | (h[9] << 6))
|
||||||
|
s[29] = byte(h[9] >> 2)
|
||||||
|
s[30] = byte(h[9] >> 10)
|
||||||
|
s[31] = byte(h[9] >> 18)
|
||||||
|
}
|
||||||
|
|
||||||
|
// feMul calculates h = f * g
|
||||||
|
// Can overlap h with f or g.
|
||||||
|
//
|
||||||
|
// Preconditions:
|
||||||
|
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||||
|
// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||||
|
//
|
||||||
|
// Postconditions:
|
||||||
|
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||||
|
//
|
||||||
|
// Notes on implementation strategy:
|
||||||
|
//
|
||||||
|
// Using schoolbook multiplication.
|
||||||
|
// Karatsuba would save a little in some cost models.
|
||||||
|
//
|
||||||
|
// Most multiplications by 2 and 19 are 32-bit precomputations;
|
||||||
|
// cheaper than 64-bit postcomputations.
|
||||||
|
//
|
||||||
|
// There is one remaining multiplication by 19 in the carry chain;
|
||||||
|
// one *19 precomputation can be merged into this,
|
||||||
|
// but the resulting data flow is considerably less clean.
|
||||||
|
//
|
||||||
|
// There are 12 carries below.
|
||||||
|
// 10 of them are 2-way parallelizable and vectorizable.
|
||||||
|
// Can get away with 11 carries, but then data flow is much deeper.
|
||||||
|
//
|
||||||
|
// With tighter constraints on inputs can squeeze carries into int32.
|
||||||
|
func feMul(h, f, g *fieldElement) {
|
||||||
|
f0 := f[0]
|
||||||
|
f1 := f[1]
|
||||||
|
f2 := f[2]
|
||||||
|
f3 := f[3]
|
||||||
|
f4 := f[4]
|
||||||
|
f5 := f[5]
|
||||||
|
f6 := f[6]
|
||||||
|
f7 := f[7]
|
||||||
|
f8 := f[8]
|
||||||
|
f9 := f[9]
|
||||||
|
g0 := g[0]
|
||||||
|
g1 := g[1]
|
||||||
|
g2 := g[2]
|
||||||
|
g3 := g[3]
|
||||||
|
g4 := g[4]
|
||||||
|
g5 := g[5]
|
||||||
|
g6 := g[6]
|
||||||
|
g7 := g[7]
|
||||||
|
g8 := g[8]
|
||||||
|
g9 := g[9]
|
||||||
|
g1_19 := 19 * g1 // 1.4*2^29
|
||||||
|
g2_19 := 19 * g2 // 1.4*2^30; still ok
|
||||||
|
g3_19 := 19 * g3
|
||||||
|
g4_19 := 19 * g4
|
||||||
|
g5_19 := 19 * g5
|
||||||
|
g6_19 := 19 * g6
|
||||||
|
g7_19 := 19 * g7
|
||||||
|
g8_19 := 19 * g8
|
||||||
|
g9_19 := 19 * g9
|
||||||
|
f1_2 := 2 * f1
|
||||||
|
f3_2 := 2 * f3
|
||||||
|
f5_2 := 2 * f5
|
||||||
|
f7_2 := 2 * f7
|
||||||
|
f9_2 := 2 * f9
|
||||||
|
f0g0 := int64(f0) * int64(g0)
|
||||||
|
f0g1 := int64(f0) * int64(g1)
|
||||||
|
f0g2 := int64(f0) * int64(g2)
|
||||||
|
f0g3 := int64(f0) * int64(g3)
|
||||||
|
f0g4 := int64(f0) * int64(g4)
|
||||||
|
f0g5 := int64(f0) * int64(g5)
|
||||||
|
f0g6 := int64(f0) * int64(g6)
|
||||||
|
f0g7 := int64(f0) * int64(g7)
|
||||||
|
f0g8 := int64(f0) * int64(g8)
|
||||||
|
f0g9 := int64(f0) * int64(g9)
|
||||||
|
f1g0 := int64(f1) * int64(g0)
|
||||||
|
f1g1_2 := int64(f1_2) * int64(g1)
|
||||||
|
f1g2 := int64(f1) * int64(g2)
|
||||||
|
f1g3_2 := int64(f1_2) * int64(g3)
|
||||||
|
f1g4 := int64(f1) * int64(g4)
|
||||||
|
f1g5_2 := int64(f1_2) * int64(g5)
|
||||||
|
f1g6 := int64(f1) * int64(g6)
|
||||||
|
f1g7_2 := int64(f1_2) * int64(g7)
|
||||||
|
f1g8 := int64(f1) * int64(g8)
|
||||||
|
f1g9_38 := int64(f1_2) * int64(g9_19)
|
||||||
|
f2g0 := int64(f2) * int64(g0)
|
||||||
|
f2g1 := int64(f2) * int64(g1)
|
||||||
|
f2g2 := int64(f2) * int64(g2)
|
||||||
|
f2g3 := int64(f2) * int64(g3)
|
||||||
|
f2g4 := int64(f2) * int64(g4)
|
||||||
|
f2g5 := int64(f2) * int64(g5)
|
||||||
|
f2g6 := int64(f2) * int64(g6)
|
||||||
|
f2g7 := int64(f2) * int64(g7)
|
||||||
|
f2g8_19 := int64(f2) * int64(g8_19)
|
||||||
|
f2g9_19 := int64(f2) * int64(g9_19)
|
||||||
|
f3g0 := int64(f3) * int64(g0)
|
||||||
|
f3g1_2 := int64(f3_2) * int64(g1)
|
||||||
|
f3g2 := int64(f3) * int64(g2)
|
||||||
|
f3g3_2 := int64(f3_2) * int64(g3)
|
||||||
|
f3g4 := int64(f3) * int64(g4)
|
||||||
|
f3g5_2 := int64(f3_2) * int64(g5)
|
||||||
|
f3g6 := int64(f3) * int64(g6)
|
||||||
|
f3g7_38 := int64(f3_2) * int64(g7_19)
|
||||||
|
f3g8_19 := int64(f3) * int64(g8_19)
|
||||||
|
f3g9_38 := int64(f3_2) * int64(g9_19)
|
||||||
|
f4g0 := int64(f4) * int64(g0)
|
||||||
|
f4g1 := int64(f4) * int64(g1)
|
||||||
|
f4g2 := int64(f4) * int64(g2)
|
||||||
|
f4g3 := int64(f4) * int64(g3)
|
||||||
|
f4g4 := int64(f4) * int64(g4)
|
||||||
|
f4g5 := int64(f4) * int64(g5)
|
||||||
|
f4g6_19 := int64(f4) * int64(g6_19)
|
||||||
|
f4g7_19 := int64(f4) * int64(g7_19)
|
||||||
|
f4g8_19 := int64(f4) * int64(g8_19)
|
||||||
|
f4g9_19 := int64(f4) * int64(g9_19)
|
||||||
|
f5g0 := int64(f5) * int64(g0)
|
||||||
|
f5g1_2 := int64(f5_2) * int64(g1)
|
||||||
|
f5g2 := int64(f5) * int64(g2)
|
||||||
|
f5g3_2 := int64(f5_2) * int64(g3)
|
||||||
|
f5g4 := int64(f5) * int64(g4)
|
||||||
|
f5g5_38 := int64(f5_2) * int64(g5_19)
|
||||||
|
f5g6_19 := int64(f5) * int64(g6_19)
|
||||||
|
f5g7_38 := int64(f5_2) * int64(g7_19)
|
||||||
|
f5g8_19 := int64(f5) * int64(g8_19)
|
||||||
|
f5g9_38 := int64(f5_2) * int64(g9_19)
|
||||||
|
f6g0 := int64(f6) * int64(g0)
|
||||||
|
f6g1 := int64(f6) * int64(g1)
|
||||||
|
f6g2 := int64(f6) * int64(g2)
|
||||||
|
f6g3 := int64(f6) * int64(g3)
|
||||||
|
f6g4_19 := int64(f6) * int64(g4_19)
|
||||||
|
f6g5_19 := int64(f6) * int64(g5_19)
|
||||||
|
f6g6_19 := int64(f6) * int64(g6_19)
|
||||||
|
f6g7_19 := int64(f6) * int64(g7_19)
|
||||||
|
f6g8_19 := int64(f6) * int64(g8_19)
|
||||||
|
f6g9_19 := int64(f6) * int64(g9_19)
|
||||||
|
f7g0 := int64(f7) * int64(g0)
|
||||||
|
f7g1_2 := int64(f7_2) * int64(g1)
|
||||||
|
f7g2 := int64(f7) * int64(g2)
|
||||||
|
f7g3_38 := int64(f7_2) * int64(g3_19)
|
||||||
|
f7g4_19 := int64(f7) * int64(g4_19)
|
||||||
|
f7g5_38 := int64(f7_2) * int64(g5_19)
|
||||||
|
f7g6_19 := int64(f7) * int64(g6_19)
|
||||||
|
f7g7_38 := int64(f7_2) * int64(g7_19)
|
||||||
|
f7g8_19 := int64(f7) * int64(g8_19)
|
||||||
|
f7g9_38 := int64(f7_2) * int64(g9_19)
|
||||||
|
f8g0 := int64(f8) * int64(g0)
|
||||||
|
f8g1 := int64(f8) * int64(g1)
|
||||||
|
f8g2_19 := int64(f8) * int64(g2_19)
|
||||||
|
f8g3_19 := int64(f8) * int64(g3_19)
|
||||||
|
f8g4_19 := int64(f8) * int64(g4_19)
|
||||||
|
f8g5_19 := int64(f8) * int64(g5_19)
|
||||||
|
f8g6_19 := int64(f8) * int64(g6_19)
|
||||||
|
f8g7_19 := int64(f8) * int64(g7_19)
|
||||||
|
f8g8_19 := int64(f8) * int64(g8_19)
|
||||||
|
f8g9_19 := int64(f8) * int64(g9_19)
|
||||||
|
f9g0 := int64(f9) * int64(g0)
|
||||||
|
f9g1_38 := int64(f9_2) * int64(g1_19)
|
||||||
|
f9g2_19 := int64(f9) * int64(g2_19)
|
||||||
|
f9g3_38 := int64(f9_2) * int64(g3_19)
|
||||||
|
f9g4_19 := int64(f9) * int64(g4_19)
|
||||||
|
f9g5_38 := int64(f9_2) * int64(g5_19)
|
||||||
|
f9g6_19 := int64(f9) * int64(g6_19)
|
||||||
|
f9g7_38 := int64(f9_2) * int64(g7_19)
|
||||||
|
f9g8_19 := int64(f9) * int64(g8_19)
|
||||||
|
f9g9_38 := int64(f9_2) * int64(g9_19)
|
||||||
|
h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38
|
||||||
|
h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19
|
||||||
|
h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38
|
||||||
|
h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19
|
||||||
|
h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38
|
||||||
|
h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19
|
||||||
|
h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38
|
||||||
|
h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19
|
||||||
|
h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38
|
||||||
|
h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0
|
||||||
|
var carry [10]int64
|
||||||
|
|
||||||
|
// |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
|
||||||
|
// i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
|
||||||
|
// |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
|
||||||
|
// i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
|
||||||
|
|
||||||
|
carry[0] = (h0 + (1 << 25)) >> 26
|
||||||
|
h1 += carry[0]
|
||||||
|
h0 -= carry[0] << 26
|
||||||
|
carry[4] = (h4 + (1 << 25)) >> 26
|
||||||
|
h5 += carry[4]
|
||||||
|
h4 -= carry[4] << 26
|
||||||
|
// |h0| <= 2^25
|
||||||
|
// |h4| <= 2^25
|
||||||
|
// |h1| <= 1.51*2^58
|
||||||
|
// |h5| <= 1.51*2^58
|
||||||
|
|
||||||
|
carry[1] = (h1 + (1 << 24)) >> 25
|
||||||
|
h2 += carry[1]
|
||||||
|
h1 -= carry[1] << 25
|
||||||
|
carry[5] = (h5 + (1 << 24)) >> 25
|
||||||
|
h6 += carry[5]
|
||||||
|
h5 -= carry[5] << 25
|
||||||
|
// |h1| <= 2^24; from now on fits into int32
|
||||||
|
// |h5| <= 2^24; from now on fits into int32
|
||||||
|
// |h2| <= 1.21*2^59
|
||||||
|
// |h6| <= 1.21*2^59
|
||||||
|
|
||||||
|
carry[2] = (h2 + (1 << 25)) >> 26
|
||||||
|
h3 += carry[2]
|
||||||
|
h2 -= carry[2] << 26
|
||||||
|
carry[6] = (h6 + (1 << 25)) >> 26
|
||||||
|
h7 += carry[6]
|
||||||
|
h6 -= carry[6] << 26
|
||||||
|
// |h2| <= 2^25; from now on fits into int32 unchanged
|
||||||
|
// |h6| <= 2^25; from now on fits into int32 unchanged
|
||||||
|
// |h3| <= 1.51*2^58
|
||||||
|
// |h7| <= 1.51*2^58
|
||||||
|
|
||||||
|
carry[3] = (h3 + (1 << 24)) >> 25
|
||||||
|
h4 += carry[3]
|
||||||
|
h3 -= carry[3] << 25
|
||||||
|
carry[7] = (h7 + (1 << 24)) >> 25
|
||||||
|
h8 += carry[7]
|
||||||
|
h7 -= carry[7] << 25
|
||||||
|
// |h3| <= 2^24; from now on fits into int32 unchanged
|
||||||
|
// |h7| <= 2^24; from now on fits into int32 unchanged
|
||||||
|
// |h4| <= 1.52*2^33
|
||||||
|
// |h8| <= 1.52*2^33
|
||||||
|
|
||||||
|
carry[4] = (h4 + (1 << 25)) >> 26
|
||||||
|
h5 += carry[4]
|
||||||
|
h4 -= carry[4] << 26
|
||||||
|
carry[8] = (h8 + (1 << 25)) >> 26
|
||||||
|
h9 += carry[8]
|
||||||
|
h8 -= carry[8] << 26
|
||||||
|
// |h4| <= 2^25; from now on fits into int32 unchanged
|
||||||
|
// |h8| <= 2^25; from now on fits into int32 unchanged
|
||||||
|
// |h5| <= 1.01*2^24
|
||||||
|
// |h9| <= 1.51*2^58
|
||||||
|
|
||||||
|
carry[9] = (h9 + (1 << 24)) >> 25
|
||||||
|
h0 += carry[9] * 19
|
||||||
|
h9 -= carry[9] << 25
|
||||||
|
// |h9| <= 2^24; from now on fits into int32 unchanged
|
||||||
|
// |h0| <= 1.8*2^37
|
||||||
|
|
||||||
|
carry[0] = (h0 + (1 << 25)) >> 26
|
||||||
|
h1 += carry[0]
|
||||||
|
h0 -= carry[0] << 26
|
||||||
|
// |h0| <= 2^25; from now on fits into int32 unchanged
|
||||||
|
// |h1| <= 1.01*2^24
|
||||||
|
|
||||||
|
h[0] = int32(h0)
|
||||||
|
h[1] = int32(h1)
|
||||||
|
h[2] = int32(h2)
|
||||||
|
h[3] = int32(h3)
|
||||||
|
h[4] = int32(h4)
|
||||||
|
h[5] = int32(h5)
|
||||||
|
h[6] = int32(h6)
|
||||||
|
h[7] = int32(h7)
|
||||||
|
h[8] = int32(h8)
|
||||||
|
h[9] = int32(h9)
|
||||||
|
}
|
||||||
|
|
||||||
|
// feSquare calculates h = f*f. Can overlap h with f.
|
||||||
|
//
|
||||||
|
// Preconditions:
|
||||||
|
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||||
|
//
|
||||||
|
// Postconditions:
|
||||||
|
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||||
|
func feSquare(h, f *fieldElement) {
|
||||||
|
f0 := f[0]
|
||||||
|
f1 := f[1]
|
||||||
|
f2 := f[2]
|
||||||
|
f3 := f[3]
|
||||||
|
f4 := f[4]
|
||||||
|
f5 := f[5]
|
||||||
|
f6 := f[6]
|
||||||
|
f7 := f[7]
|
||||||
|
f8 := f[8]
|
||||||
|
f9 := f[9]
|
||||||
|
f0_2 := 2 * f0
|
||||||
|
f1_2 := 2 * f1
|
||||||
|
f2_2 := 2 * f2
|
||||||
|
f3_2 := 2 * f3
|
||||||
|
f4_2 := 2 * f4
|
||||||
|
f5_2 := 2 * f5
|
||||||
|
f6_2 := 2 * f6
|
||||||
|
f7_2 := 2 * f7
|
||||||
|
f5_38 := 38 * f5 // 1.31*2^30
|
||||||
|
f6_19 := 19 * f6 // 1.31*2^30
|
||||||
|
f7_38 := 38 * f7 // 1.31*2^30
|
||||||
|
f8_19 := 19 * f8 // 1.31*2^30
|
||||||
|
f9_38 := 38 * f9 // 1.31*2^30
|
||||||
|
f0f0 := int64(f0) * int64(f0)
|
||||||
|
f0f1_2 := int64(f0_2) * int64(f1)
|
||||||
|
f0f2_2 := int64(f0_2) * int64(f2)
|
||||||
|
f0f3_2 := int64(f0_2) * int64(f3)
|
||||||
|
f0f4_2 := int64(f0_2) * int64(f4)
|
||||||
|
f0f5_2 := int64(f0_2) * int64(f5)
|
||||||
|
f0f6_2 := int64(f0_2) * int64(f6)
|
||||||
|
f0f7_2 := int64(f0_2) * int64(f7)
|
||||||
|
f0f8_2 := int64(f0_2) * int64(f8)
|
||||||
|
f0f9_2 := int64(f0_2) * int64(f9)
|
||||||
|
f1f1_2 := int64(f1_2) * int64(f1)
|
||||||
|
f1f2_2 := int64(f1_2) * int64(f2)
|
||||||
|
f1f3_4 := int64(f1_2) * int64(f3_2)
|
||||||
|
f1f4_2 := int64(f1_2) * int64(f4)
|
||||||
|
f1f5_4 := int64(f1_2) * int64(f5_2)
|
||||||
|
f1f6_2 := int64(f1_2) * int64(f6)
|
||||||
|
f1f7_4 := int64(f1_2) * int64(f7_2)
|
||||||
|
f1f8_2 := int64(f1_2) * int64(f8)
|
||||||
|
f1f9_76 := int64(f1_2) * int64(f9_38)
|
||||||
|
f2f2 := int64(f2) * int64(f2)
|
||||||
|
f2f3_2 := int64(f2_2) * int64(f3)
|
||||||
|
f2f4_2 := int64(f2_2) * int64(f4)
|
||||||
|
f2f5_2 := int64(f2_2) * int64(f5)
|
||||||
|
f2f6_2 := int64(f2_2) * int64(f6)
|
||||||
|
f2f7_2 := int64(f2_2) * int64(f7)
|
||||||
|
f2f8_38 := int64(f2_2) * int64(f8_19)
|
||||||
|
f2f9_38 := int64(f2) * int64(f9_38)
|
||||||
|
f3f3_2 := int64(f3_2) * int64(f3)
|
||||||
|
f3f4_2 := int64(f3_2) * int64(f4)
|
||||||
|
f3f5_4 := int64(f3_2) * int64(f5_2)
|
||||||
|
f3f6_2 := int64(f3_2) * int64(f6)
|
||||||
|
f3f7_76 := int64(f3_2) * int64(f7_38)
|
||||||
|
f3f8_38 := int64(f3_2) * int64(f8_19)
|
||||||
|
f3f9_76 := int64(f3_2) * int64(f9_38)
|
||||||
|
f4f4 := int64(f4) * int64(f4)
|
||||||
|
f4f5_2 := int64(f4_2) * int64(f5)
|
||||||
|
f4f6_38 := int64(f4_2) * int64(f6_19)
|
||||||
|
f4f7_38 := int64(f4) * int64(f7_38)
|
||||||
|
f4f8_38 := int64(f4_2) * int64(f8_19)
|
||||||
|
f4f9_38 := int64(f4) * int64(f9_38)
|
||||||
|
f5f5_38 := int64(f5) * int64(f5_38)
|
||||||
|
f5f6_38 := int64(f5_2) * int64(f6_19)
|
||||||
|
f5f7_76 := int64(f5_2) * int64(f7_38)
|
||||||
|
f5f8_38 := int64(f5_2) * int64(f8_19)
|
||||||
|
f5f9_76 := int64(f5_2) * int64(f9_38)
|
||||||
|
f6f6_19 := int64(f6) * int64(f6_19)
|
||||||
|
f6f7_38 := int64(f6) * int64(f7_38)
|
||||||
|
f6f8_38 := int64(f6_2) * int64(f8_19)
|
||||||
|
f6f9_38 := int64(f6) * int64(f9_38)
|
||||||
|
f7f7_38 := int64(f7) * int64(f7_38)
|
||||||
|
f7f8_38 := int64(f7_2) * int64(f8_19)
|
||||||
|
f7f9_76 := int64(f7_2) * int64(f9_38)
|
||||||
|
f8f8_19 := int64(f8) * int64(f8_19)
|
||||||
|
f8f9_38 := int64(f8) * int64(f9_38)
|
||||||
|
f9f9_38 := int64(f9) * int64(f9_38)
|
||||||
|
h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38
|
||||||
|
h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38
|
||||||
|
h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19
|
||||||
|
h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38
|
||||||
|
h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38
|
||||||
|
h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38
|
||||||
|
h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19
|
||||||
|
h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38
|
||||||
|
h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38
|
||||||
|
h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2
|
||||||
|
var carry [10]int64
|
||||||
|
|
||||||
|
carry[0] = (h0 + (1 << 25)) >> 26
|
||||||
|
h1 += carry[0]
|
||||||
|
h0 -= carry[0] << 26
|
||||||
|
carry[4] = (h4 + (1 << 25)) >> 26
|
||||||
|
h5 += carry[4]
|
||||||
|
h4 -= carry[4] << 26
|
||||||
|
|
||||||
|
carry[1] = (h1 + (1 << 24)) >> 25
|
||||||
|
h2 += carry[1]
|
||||||
|
h1 -= carry[1] << 25
|
||||||
|
carry[5] = (h5 + (1 << 24)) >> 25
|
||||||
|
h6 += carry[5]
|
||||||
|
h5 -= carry[5] << 25
|
||||||
|
|
||||||
|
carry[2] = (h2 + (1 << 25)) >> 26
|
||||||
|
h3 += carry[2]
|
||||||
|
h2 -= carry[2] << 26
|
||||||
|
carry[6] = (h6 + (1 << 25)) >> 26
|
||||||
|
h7 += carry[6]
|
||||||
|
h6 -= carry[6] << 26
|
||||||
|
|
||||||
|
carry[3] = (h3 + (1 << 24)) >> 25
|
||||||
|
h4 += carry[3]
|
||||||
|
h3 -= carry[3] << 25
|
||||||
|
carry[7] = (h7 + (1 << 24)) >> 25
|
||||||
|
h8 += carry[7]
|
||||||
|
h7 -= carry[7] << 25
|
||||||
|
|
||||||
|
carry[4] = (h4 + (1 << 25)) >> 26
|
||||||
|
h5 += carry[4]
|
||||||
|
h4 -= carry[4] << 26
|
||||||
|
carry[8] = (h8 + (1 << 25)) >> 26
|
||||||
|
h9 += carry[8]
|
||||||
|
h8 -= carry[8] << 26
|
||||||
|
|
||||||
|
carry[9] = (h9 + (1 << 24)) >> 25
|
||||||
|
h0 += carry[9] * 19
|
||||||
|
h9 -= carry[9] << 25
|
||||||
|
|
||||||
|
carry[0] = (h0 + (1 << 25)) >> 26
|
||||||
|
h1 += carry[0]
|
||||||
|
h0 -= carry[0] << 26
|
||||||
|
|
||||||
|
h[0] = int32(h0)
|
||||||
|
h[1] = int32(h1)
|
||||||
|
h[2] = int32(h2)
|
||||||
|
h[3] = int32(h3)
|
||||||
|
h[4] = int32(h4)
|
||||||
|
h[5] = int32(h5)
|
||||||
|
h[6] = int32(h6)
|
||||||
|
h[7] = int32(h7)
|
||||||
|
h[8] = int32(h8)
|
||||||
|
h[9] = int32(h9)
|
||||||
|
}
|
||||||
|
|
||||||
|
// feMul121666 calculates h = f * 121666. Can overlap h with f.
|
||||||
|
//
|
||||||
|
// Preconditions:
|
||||||
|
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||||
|
//
|
||||||
|
// Postconditions:
|
||||||
|
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||||
|
func feMul121666(h, f *fieldElement) {
|
||||||
|
h0 := int64(f[0]) * 121666
|
||||||
|
h1 := int64(f[1]) * 121666
|
||||||
|
h2 := int64(f[2]) * 121666
|
||||||
|
h3 := int64(f[3]) * 121666
|
||||||
|
h4 := int64(f[4]) * 121666
|
||||||
|
h5 := int64(f[5]) * 121666
|
||||||
|
h6 := int64(f[6]) * 121666
|
||||||
|
h7 := int64(f[7]) * 121666
|
||||||
|
h8 := int64(f[8]) * 121666
|
||||||
|
h9 := int64(f[9]) * 121666
|
||||||
|
var carry [10]int64
|
||||||
|
|
||||||
|
carry[9] = (h9 + (1 << 24)) >> 25
|
||||||
|
h0 += carry[9] * 19
|
||||||
|
h9 -= carry[9] << 25
|
||||||
|
carry[1] = (h1 + (1 << 24)) >> 25
|
||||||
|
h2 += carry[1]
|
||||||
|
h1 -= carry[1] << 25
|
||||||
|
carry[3] = (h3 + (1 << 24)) >> 25
|
||||||
|
h4 += carry[3]
|
||||||
|
h3 -= carry[3] << 25
|
||||||
|
carry[5] = (h5 + (1 << 24)) >> 25
|
||||||
|
h6 += carry[5]
|
||||||
|
h5 -= carry[5] << 25
|
||||||
|
carry[7] = (h7 + (1 << 24)) >> 25
|
||||||
|
h8 += carry[7]
|
||||||
|
h7 -= carry[7] << 25
|
||||||
|
|
||||||
|
carry[0] = (h0 + (1 << 25)) >> 26
|
||||||
|
h1 += carry[0]
|
||||||
|
h0 -= carry[0] << 26
|
||||||
|
carry[2] = (h2 + (1 << 25)) >> 26
|
||||||
|
h3 += carry[2]
|
||||||
|
h2 -= carry[2] << 26
|
||||||
|
carry[4] = (h4 + (1 << 25)) >> 26
|
||||||
|
h5 += carry[4]
|
||||||
|
h4 -= carry[4] << 26
|
||||||
|
carry[6] = (h6 + (1 << 25)) >> 26
|
||||||
|
h7 += carry[6]
|
||||||
|
h6 -= carry[6] << 26
|
||||||
|
carry[8] = (h8 + (1 << 25)) >> 26
|
||||||
|
h9 += carry[8]
|
||||||
|
h8 -= carry[8] << 26
|
||||||
|
|
||||||
|
h[0] = int32(h0)
|
||||||
|
h[1] = int32(h1)
|
||||||
|
h[2] = int32(h2)
|
||||||
|
h[3] = int32(h3)
|
||||||
|
h[4] = int32(h4)
|
||||||
|
h[5] = int32(h5)
|
||||||
|
h[6] = int32(h6)
|
||||||
|
h[7] = int32(h7)
|
||||||
|
h[8] = int32(h8)
|
||||||
|
h[9] = int32(h9)
|
||||||
|
}
|
||||||
|
|
||||||
|
// feInvert sets out = z^-1.
|
||||||
|
func feInvert(out, z *fieldElement) {
|
||||||
|
var t0, t1, t2, t3 fieldElement
|
||||||
|
var i int
|
||||||
|
|
||||||
|
feSquare(&t0, z)
|
||||||
|
for i = 1; i < 1; i++ {
|
||||||
|
feSquare(&t0, &t0)
|
||||||
|
}
|
||||||
|
feSquare(&t1, &t0)
|
||||||
|
for i = 1; i < 2; i++ {
|
||||||
|
feSquare(&t1, &t1)
|
||||||
|
}
|
||||||
|
feMul(&t1, z, &t1)
|
||||||
|
feMul(&t0, &t0, &t1)
|
||||||
|
feSquare(&t2, &t0)
|
||||||
|
for i = 1; i < 1; i++ {
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
}
|
||||||
|
feMul(&t1, &t1, &t2)
|
||||||
|
feSquare(&t2, &t1)
|
||||||
|
for i = 1; i < 5; i++ {
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
}
|
||||||
|
feMul(&t1, &t2, &t1)
|
||||||
|
feSquare(&t2, &t1)
|
||||||
|
for i = 1; i < 10; i++ {
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
}
|
||||||
|
feMul(&t2, &t2, &t1)
|
||||||
|
feSquare(&t3, &t2)
|
||||||
|
for i = 1; i < 20; i++ {
|
||||||
|
feSquare(&t3, &t3)
|
||||||
|
}
|
||||||
|
feMul(&t2, &t3, &t2)
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
for i = 1; i < 10; i++ {
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
}
|
||||||
|
feMul(&t1, &t2, &t1)
|
||||||
|
feSquare(&t2, &t1)
|
||||||
|
for i = 1; i < 50; i++ {
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
}
|
||||||
|
feMul(&t2, &t2, &t1)
|
||||||
|
feSquare(&t3, &t2)
|
||||||
|
for i = 1; i < 100; i++ {
|
||||||
|
feSquare(&t3, &t3)
|
||||||
|
}
|
||||||
|
feMul(&t2, &t3, &t2)
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
for i = 1; i < 50; i++ {
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
}
|
||||||
|
feMul(&t1, &t2, &t1)
|
||||||
|
feSquare(&t1, &t1)
|
||||||
|
for i = 1; i < 5; i++ {
|
||||||
|
feSquare(&t1, &t1)
|
||||||
|
}
|
||||||
|
feMul(out, &t1, &t0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func scalarMult(out, in, base *[32]byte) {
|
||||||
|
var e [32]byte
|
||||||
|
|
||||||
|
copy(e[:], in[:])
|
||||||
|
e[0] &= 248
|
||||||
|
e[31] &= 127
|
||||||
|
e[31] |= 64
|
||||||
|
|
||||||
|
var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement
|
||||||
|
feFromBytes(&x1, base)
|
||||||
|
feOne(&x2)
|
||||||
|
feCopy(&x3, &x1)
|
||||||
|
feOne(&z3)
|
||||||
|
|
||||||
|
swap := int32(0)
|
||||||
|
for pos := 254; pos >= 0; pos-- {
|
||||||
|
b := e[pos/8] >> uint(pos&7)
|
||||||
|
b &= 1
|
||||||
|
swap ^= int32(b)
|
||||||
|
feCSwap(&x2, &x3, swap)
|
||||||
|
feCSwap(&z2, &z3, swap)
|
||||||
|
swap = int32(b)
|
||||||
|
|
||||||
|
feSub(&tmp0, &x3, &z3)
|
||||||
|
feSub(&tmp1, &x2, &z2)
|
||||||
|
feAdd(&x2, &x2, &z2)
|
||||||
|
feAdd(&z2, &x3, &z3)
|
||||||
|
feMul(&z3, &tmp0, &x2)
|
||||||
|
feMul(&z2, &z2, &tmp1)
|
||||||
|
feSquare(&tmp0, &tmp1)
|
||||||
|
feSquare(&tmp1, &x2)
|
||||||
|
feAdd(&x3, &z3, &z2)
|
||||||
|
feSub(&z2, &z3, &z2)
|
||||||
|
feMul(&x2, &tmp1, &tmp0)
|
||||||
|
feSub(&tmp1, &tmp1, &tmp0)
|
||||||
|
feSquare(&z2, &z2)
|
||||||
|
feMul121666(&z3, &tmp1)
|
||||||
|
feSquare(&x3, &x3)
|
||||||
|
feAdd(&tmp0, &tmp0, &z3)
|
||||||
|
feMul(&z3, &x1, &z2)
|
||||||
|
feMul(&z2, &tmp1, &tmp0)
|
||||||
|
}
|
||||||
|
|
||||||
|
feCSwap(&x2, &x3, swap)
|
||||||
|
feCSwap(&z2, &z3, swap)
|
||||||
|
|
||||||
|
feInvert(&z2, &z2)
|
||||||
|
feMul(&x2, &x2, &z2)
|
||||||
|
feToBytes(out, &x2)
|
||||||
|
}
|
23
vendor/golang.org/x/crypto/curve25519/doc.go
generated
vendored
Normal file
23
vendor/golang.org/x/crypto/curve25519/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package curve25519 provides an implementation of scalar multiplication on
|
||||||
|
// the elliptic curve known as curve25519. See https://cr.yp.to/ecdh.html
|
||||||
|
package curve25519 // import "golang.org/x/crypto/curve25519"
|
||||||
|
|
||||||
|
// basePoint is the x coordinate of the generator of the curve.
|
||||||
|
var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
|
||||||
|
// ScalarMult sets dst to the product in*base where dst and base are the x
|
||||||
|
// coordinates of group points and all values are in little-endian form.
|
||||||
|
func ScalarMult(dst, in, base *[32]byte) {
|
||||||
|
scalarMult(dst, in, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarBaseMult sets dst to the product in*base where dst and base are the x
|
||||||
|
// coordinates of group points, base is the standard generator and all values
|
||||||
|
// are in little-endian form.
|
||||||
|
func ScalarBaseMult(dst, in *[32]byte) {
|
||||||
|
ScalarMult(dst, in, &basePoint)
|
||||||
|
}
|
73
vendor/golang.org/x/crypto/curve25519/freeze_amd64.s
generated
vendored
Normal file
73
vendor/golang.org/x/crypto/curve25519/freeze_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This code was translated into a form compatible with 6a from the public
|
||||||
|
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
#include "const_amd64.h"
|
||||||
|
|
||||||
|
// func freeze(inout *[5]uint64)
|
||||||
|
TEXT ·freeze(SB),7,$0-8
|
||||||
|
MOVQ inout+0(FP), DI
|
||||||
|
|
||||||
|
MOVQ 0(DI),SI
|
||||||
|
MOVQ 8(DI),DX
|
||||||
|
MOVQ 16(DI),CX
|
||||||
|
MOVQ 24(DI),R8
|
||||||
|
MOVQ 32(DI),R9
|
||||||
|
MOVQ $REDMASK51,AX
|
||||||
|
MOVQ AX,R10
|
||||||
|
SUBQ $18,R10
|
||||||
|
MOVQ $3,R11
|
||||||
|
REDUCELOOP:
|
||||||
|
MOVQ SI,R12
|
||||||
|
SHRQ $51,R12
|
||||||
|
ANDQ AX,SI
|
||||||
|
ADDQ R12,DX
|
||||||
|
MOVQ DX,R12
|
||||||
|
SHRQ $51,R12
|
||||||
|
ANDQ AX,DX
|
||||||
|
ADDQ R12,CX
|
||||||
|
MOVQ CX,R12
|
||||||
|
SHRQ $51,R12
|
||||||
|
ANDQ AX,CX
|
||||||
|
ADDQ R12,R8
|
||||||
|
MOVQ R8,R12
|
||||||
|
SHRQ $51,R12
|
||||||
|
ANDQ AX,R8
|
||||||
|
ADDQ R12,R9
|
||||||
|
MOVQ R9,R12
|
||||||
|
SHRQ $51,R12
|
||||||
|
ANDQ AX,R9
|
||||||
|
IMUL3Q $19,R12,R12
|
||||||
|
ADDQ R12,SI
|
||||||
|
SUBQ $1,R11
|
||||||
|
JA REDUCELOOP
|
||||||
|
MOVQ $1,R12
|
||||||
|
CMPQ R10,SI
|
||||||
|
CMOVQLT R11,R12
|
||||||
|
CMPQ AX,DX
|
||||||
|
CMOVQNE R11,R12
|
||||||
|
CMPQ AX,CX
|
||||||
|
CMOVQNE R11,R12
|
||||||
|
CMPQ AX,R8
|
||||||
|
CMOVQNE R11,R12
|
||||||
|
CMPQ AX,R9
|
||||||
|
CMOVQNE R11,R12
|
||||||
|
NEGQ R12
|
||||||
|
ANDQ R12,AX
|
||||||
|
ANDQ R12,R10
|
||||||
|
SUBQ R10,SI
|
||||||
|
SUBQ AX,DX
|
||||||
|
SUBQ AX,CX
|
||||||
|
SUBQ AX,R8
|
||||||
|
SUBQ AX,R9
|
||||||
|
MOVQ SI,0(DI)
|
||||||
|
MOVQ DX,8(DI)
|
||||||
|
MOVQ CX,16(DI)
|
||||||
|
MOVQ R8,24(DI)
|
||||||
|
MOVQ R9,32(DI)
|
||||||
|
RET
|
1377
vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s
generated
vendored
Normal file
1377
vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
240
vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go
generated
vendored
Normal file
240
vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
package curve25519
|
||||||
|
|
||||||
|
// These functions are implemented in the .s files. The names of the functions
|
||||||
|
// in the rest of the file are also taken from the SUPERCOP sources to help
|
||||||
|
// people following along.
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
|
||||||
|
func cswap(inout *[5]uint64, v uint64)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
|
||||||
|
func ladderstep(inout *[5][5]uint64)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
|
||||||
|
func freeze(inout *[5]uint64)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
|
||||||
|
func mul(dest, a, b *[5]uint64)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
|
||||||
|
func square(out, in *[5]uint64)
|
||||||
|
|
||||||
|
// mladder uses a Montgomery ladder to calculate (xr/zr) *= s.
|
||||||
|
func mladder(xr, zr *[5]uint64, s *[32]byte) {
|
||||||
|
var work [5][5]uint64
|
||||||
|
|
||||||
|
work[0] = *xr
|
||||||
|
setint(&work[1], 1)
|
||||||
|
setint(&work[2], 0)
|
||||||
|
work[3] = *xr
|
||||||
|
setint(&work[4], 1)
|
||||||
|
|
||||||
|
j := uint(6)
|
||||||
|
var prevbit byte
|
||||||
|
|
||||||
|
for i := 31; i >= 0; i-- {
|
||||||
|
for j < 8 {
|
||||||
|
bit := ((*s)[i] >> j) & 1
|
||||||
|
swap := bit ^ prevbit
|
||||||
|
prevbit = bit
|
||||||
|
cswap(&work[1], uint64(swap))
|
||||||
|
ladderstep(&work)
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
j = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
*xr = work[1]
|
||||||
|
*zr = work[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func scalarMult(out, in, base *[32]byte) {
|
||||||
|
var e [32]byte
|
||||||
|
copy(e[:], (*in)[:])
|
||||||
|
e[0] &= 248
|
||||||
|
e[31] &= 127
|
||||||
|
e[31] |= 64
|
||||||
|
|
||||||
|
var t, z [5]uint64
|
||||||
|
unpack(&t, base)
|
||||||
|
mladder(&t, &z, &e)
|
||||||
|
invert(&z, &z)
|
||||||
|
mul(&t, &t, &z)
|
||||||
|
pack(out, &t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setint(r *[5]uint64, v uint64) {
|
||||||
|
r[0] = v
|
||||||
|
r[1] = 0
|
||||||
|
r[2] = 0
|
||||||
|
r[3] = 0
|
||||||
|
r[4] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian
|
||||||
|
// order.
|
||||||
|
func unpack(r *[5]uint64, x *[32]byte) {
|
||||||
|
r[0] = uint64(x[0]) |
|
||||||
|
uint64(x[1])<<8 |
|
||||||
|
uint64(x[2])<<16 |
|
||||||
|
uint64(x[3])<<24 |
|
||||||
|
uint64(x[4])<<32 |
|
||||||
|
uint64(x[5])<<40 |
|
||||||
|
uint64(x[6]&7)<<48
|
||||||
|
|
||||||
|
r[1] = uint64(x[6])>>3 |
|
||||||
|
uint64(x[7])<<5 |
|
||||||
|
uint64(x[8])<<13 |
|
||||||
|
uint64(x[9])<<21 |
|
||||||
|
uint64(x[10])<<29 |
|
||||||
|
uint64(x[11])<<37 |
|
||||||
|
uint64(x[12]&63)<<45
|
||||||
|
|
||||||
|
r[2] = uint64(x[12])>>6 |
|
||||||
|
uint64(x[13])<<2 |
|
||||||
|
uint64(x[14])<<10 |
|
||||||
|
uint64(x[15])<<18 |
|
||||||
|
uint64(x[16])<<26 |
|
||||||
|
uint64(x[17])<<34 |
|
||||||
|
uint64(x[18])<<42 |
|
||||||
|
uint64(x[19]&1)<<50
|
||||||
|
|
||||||
|
r[3] = uint64(x[19])>>1 |
|
||||||
|
uint64(x[20])<<7 |
|
||||||
|
uint64(x[21])<<15 |
|
||||||
|
uint64(x[22])<<23 |
|
||||||
|
uint64(x[23])<<31 |
|
||||||
|
uint64(x[24])<<39 |
|
||||||
|
uint64(x[25]&15)<<47
|
||||||
|
|
||||||
|
r[4] = uint64(x[25])>>4 |
|
||||||
|
uint64(x[26])<<4 |
|
||||||
|
uint64(x[27])<<12 |
|
||||||
|
uint64(x[28])<<20 |
|
||||||
|
uint64(x[29])<<28 |
|
||||||
|
uint64(x[30])<<36 |
|
||||||
|
uint64(x[31]&127)<<44
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack sets out = x where out is the usual, little-endian form of the 5,
|
||||||
|
// 51-bit limbs in x.
|
||||||
|
func pack(out *[32]byte, x *[5]uint64) {
|
||||||
|
t := *x
|
||||||
|
freeze(&t)
|
||||||
|
|
||||||
|
out[0] = byte(t[0])
|
||||||
|
out[1] = byte(t[0] >> 8)
|
||||||
|
out[2] = byte(t[0] >> 16)
|
||||||
|
out[3] = byte(t[0] >> 24)
|
||||||
|
out[4] = byte(t[0] >> 32)
|
||||||
|
out[5] = byte(t[0] >> 40)
|
||||||
|
out[6] = byte(t[0] >> 48)
|
||||||
|
|
||||||
|
out[6] ^= byte(t[1]<<3) & 0xf8
|
||||||
|
out[7] = byte(t[1] >> 5)
|
||||||
|
out[8] = byte(t[1] >> 13)
|
||||||
|
out[9] = byte(t[1] >> 21)
|
||||||
|
out[10] = byte(t[1] >> 29)
|
||||||
|
out[11] = byte(t[1] >> 37)
|
||||||
|
out[12] = byte(t[1] >> 45)
|
||||||
|
|
||||||
|
out[12] ^= byte(t[2]<<6) & 0xc0
|
||||||
|
out[13] = byte(t[2] >> 2)
|
||||||
|
out[14] = byte(t[2] >> 10)
|
||||||
|
out[15] = byte(t[2] >> 18)
|
||||||
|
out[16] = byte(t[2] >> 26)
|
||||||
|
out[17] = byte(t[2] >> 34)
|
||||||
|
out[18] = byte(t[2] >> 42)
|
||||||
|
out[19] = byte(t[2] >> 50)
|
||||||
|
|
||||||
|
out[19] ^= byte(t[3]<<1) & 0xfe
|
||||||
|
out[20] = byte(t[3] >> 7)
|
||||||
|
out[21] = byte(t[3] >> 15)
|
||||||
|
out[22] = byte(t[3] >> 23)
|
||||||
|
out[23] = byte(t[3] >> 31)
|
||||||
|
out[24] = byte(t[3] >> 39)
|
||||||
|
out[25] = byte(t[3] >> 47)
|
||||||
|
|
||||||
|
out[25] ^= byte(t[4]<<4) & 0xf0
|
||||||
|
out[26] = byte(t[4] >> 4)
|
||||||
|
out[27] = byte(t[4] >> 12)
|
||||||
|
out[28] = byte(t[4] >> 20)
|
||||||
|
out[29] = byte(t[4] >> 28)
|
||||||
|
out[30] = byte(t[4] >> 36)
|
||||||
|
out[31] = byte(t[4] >> 44)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invert calculates r = x^-1 mod p using Fermat's little theorem.
|
||||||
|
func invert(r *[5]uint64, x *[5]uint64) {
|
||||||
|
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64
|
||||||
|
|
||||||
|
square(&z2, x) /* 2 */
|
||||||
|
square(&t, &z2) /* 4 */
|
||||||
|
square(&t, &t) /* 8 */
|
||||||
|
mul(&z9, &t, x) /* 9 */
|
||||||
|
mul(&z11, &z9, &z2) /* 11 */
|
||||||
|
square(&t, &z11) /* 22 */
|
||||||
|
mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */
|
||||||
|
|
||||||
|
square(&t, &z2_5_0) /* 2^6 - 2^1 */
|
||||||
|
for i := 1; i < 5; i++ { /* 2^20 - 2^10 */
|
||||||
|
square(&t, &t)
|
||||||
|
}
|
||||||
|
mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */
|
||||||
|
|
||||||
|
square(&t, &z2_10_0) /* 2^11 - 2^1 */
|
||||||
|
for i := 1; i < 10; i++ { /* 2^20 - 2^10 */
|
||||||
|
square(&t, &t)
|
||||||
|
}
|
||||||
|
mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */
|
||||||
|
|
||||||
|
square(&t, &z2_20_0) /* 2^21 - 2^1 */
|
||||||
|
for i := 1; i < 20; i++ { /* 2^40 - 2^20 */
|
||||||
|
square(&t, &t)
|
||||||
|
}
|
||||||
|
mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */
|
||||||
|
|
||||||
|
square(&t, &t) /* 2^41 - 2^1 */
|
||||||
|
for i := 1; i < 10; i++ { /* 2^50 - 2^10 */
|
||||||
|
square(&t, &t)
|
||||||
|
}
|
||||||
|
mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */
|
||||||
|
|
||||||
|
square(&t, &z2_50_0) /* 2^51 - 2^1 */
|
||||||
|
for i := 1; i < 50; i++ { /* 2^100 - 2^50 */
|
||||||
|
square(&t, &t)
|
||||||
|
}
|
||||||
|
mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */
|
||||||
|
|
||||||
|
square(&t, &z2_100_0) /* 2^101 - 2^1 */
|
||||||
|
for i := 1; i < 100; i++ { /* 2^200 - 2^100 */
|
||||||
|
square(&t, &t)
|
||||||
|
}
|
||||||
|
mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */
|
||||||
|
|
||||||
|
square(&t, &t) /* 2^201 - 2^1 */
|
||||||
|
for i := 1; i < 50; i++ { /* 2^250 - 2^50 */
|
||||||
|
square(&t, &t)
|
||||||
|
}
|
||||||
|
mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */
|
||||||
|
|
||||||
|
square(&t, &t) /* 2^251 - 2^1 */
|
||||||
|
square(&t, &t) /* 2^252 - 2^2 */
|
||||||
|
square(&t, &t) /* 2^253 - 2^3 */
|
||||||
|
|
||||||
|
square(&t, &t) /* 2^254 - 2^4 */
|
||||||
|
|
||||||
|
square(&t, &t) /* 2^255 - 2^5 */
|
||||||
|
mul(r, &t, &z11) /* 2^255 - 21 */
|
||||||
|
}
|
169
vendor/golang.org/x/crypto/curve25519/mul_amd64.s
generated
vendored
Normal file
169
vendor/golang.org/x/crypto/curve25519/mul_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This code was translated into a form compatible with 6a from the public
|
||||||
|
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
#include "const_amd64.h"
|
||||||
|
|
||||||
|
// func mul(dest, a, b *[5]uint64)
|
||||||
|
TEXT ·mul(SB),0,$16-24
|
||||||
|
MOVQ dest+0(FP), DI
|
||||||
|
MOVQ a+8(FP), SI
|
||||||
|
MOVQ b+16(FP), DX
|
||||||
|
|
||||||
|
MOVQ DX,CX
|
||||||
|
MOVQ 24(SI),DX
|
||||||
|
IMUL3Q $19,DX,AX
|
||||||
|
MOVQ AX,0(SP)
|
||||||
|
MULQ 16(CX)
|
||||||
|
MOVQ AX,R8
|
||||||
|
MOVQ DX,R9
|
||||||
|
MOVQ 32(SI),DX
|
||||||
|
IMUL3Q $19,DX,AX
|
||||||
|
MOVQ AX,8(SP)
|
||||||
|
MULQ 8(CX)
|
||||||
|
ADDQ AX,R8
|
||||||
|
ADCQ DX,R9
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
MULQ 0(CX)
|
||||||
|
ADDQ AX,R8
|
||||||
|
ADCQ DX,R9
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
MULQ 8(CX)
|
||||||
|
MOVQ AX,R10
|
||||||
|
MOVQ DX,R11
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
MOVQ AX,R12
|
||||||
|
MOVQ DX,R13
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
MOVQ AX,R14
|
||||||
|
MOVQ DX,R15
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
MOVQ AX,BX
|
||||||
|
MOVQ DX,BP
|
||||||
|
MOVQ 8(SI),AX
|
||||||
|
MULQ 0(CX)
|
||||||
|
ADDQ AX,R10
|
||||||
|
ADCQ DX,R11
|
||||||
|
MOVQ 8(SI),AX
|
||||||
|
MULQ 8(CX)
|
||||||
|
ADDQ AX,R12
|
||||||
|
ADCQ DX,R13
|
||||||
|
MOVQ 8(SI),AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
ADDQ AX,R14
|
||||||
|
ADCQ DX,R15
|
||||||
|
MOVQ 8(SI),AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX,BX
|
||||||
|
ADCQ DX,BP
|
||||||
|
MOVQ 8(SI),DX
|
||||||
|
IMUL3Q $19,DX,AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX,R8
|
||||||
|
ADCQ DX,R9
|
||||||
|
MOVQ 16(SI),AX
|
||||||
|
MULQ 0(CX)
|
||||||
|
ADDQ AX,R12
|
||||||
|
ADCQ DX,R13
|
||||||
|
MOVQ 16(SI),AX
|
||||||
|
MULQ 8(CX)
|
||||||
|
ADDQ AX,R14
|
||||||
|
ADCQ DX,R15
|
||||||
|
MOVQ 16(SI),AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
ADDQ AX,BX
|
||||||
|
ADCQ DX,BP
|
||||||
|
MOVQ 16(SI),DX
|
||||||
|
IMUL3Q $19,DX,AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX,R8
|
||||||
|
ADCQ DX,R9
|
||||||
|
MOVQ 16(SI),DX
|
||||||
|
IMUL3Q $19,DX,AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX,R10
|
||||||
|
ADCQ DX,R11
|
||||||
|
MOVQ 24(SI),AX
|
||||||
|
MULQ 0(CX)
|
||||||
|
ADDQ AX,R14
|
||||||
|
ADCQ DX,R15
|
||||||
|
MOVQ 24(SI),AX
|
||||||
|
MULQ 8(CX)
|
||||||
|
ADDQ AX,BX
|
||||||
|
ADCQ DX,BP
|
||||||
|
MOVQ 0(SP),AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX,R10
|
||||||
|
ADCQ DX,R11
|
||||||
|
MOVQ 0(SP),AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX,R12
|
||||||
|
ADCQ DX,R13
|
||||||
|
MOVQ 32(SI),AX
|
||||||
|
MULQ 0(CX)
|
||||||
|
ADDQ AX,BX
|
||||||
|
ADCQ DX,BP
|
||||||
|
MOVQ 8(SP),AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
ADDQ AX,R10
|
||||||
|
ADCQ DX,R11
|
||||||
|
MOVQ 8(SP),AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX,R12
|
||||||
|
ADCQ DX,R13
|
||||||
|
MOVQ 8(SP),AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX,R14
|
||||||
|
ADCQ DX,R15
|
||||||
|
MOVQ $REDMASK51,SI
|
||||||
|
SHLQ $13,R9:R8
|
||||||
|
ANDQ SI,R8
|
||||||
|
SHLQ $13,R11:R10
|
||||||
|
ANDQ SI,R10
|
||||||
|
ADDQ R9,R10
|
||||||
|
SHLQ $13,R13:R12
|
||||||
|
ANDQ SI,R12
|
||||||
|
ADDQ R11,R12
|
||||||
|
SHLQ $13,R15:R14
|
||||||
|
ANDQ SI,R14
|
||||||
|
ADDQ R13,R14
|
||||||
|
SHLQ $13,BP:BX
|
||||||
|
ANDQ SI,BX
|
||||||
|
ADDQ R15,BX
|
||||||
|
IMUL3Q $19,BP,DX
|
||||||
|
ADDQ DX,R8
|
||||||
|
MOVQ R8,DX
|
||||||
|
SHRQ $51,DX
|
||||||
|
ADDQ R10,DX
|
||||||
|
MOVQ DX,CX
|
||||||
|
SHRQ $51,DX
|
||||||
|
ANDQ SI,R8
|
||||||
|
ADDQ R12,DX
|
||||||
|
MOVQ DX,R9
|
||||||
|
SHRQ $51,DX
|
||||||
|
ANDQ SI,CX
|
||||||
|
ADDQ R14,DX
|
||||||
|
MOVQ DX,AX
|
||||||
|
SHRQ $51,DX
|
||||||
|
ANDQ SI,R9
|
||||||
|
ADDQ BX,DX
|
||||||
|
MOVQ DX,R10
|
||||||
|
SHRQ $51,DX
|
||||||
|
ANDQ SI,AX
|
||||||
|
IMUL3Q $19,DX,DX
|
||||||
|
ADDQ DX,R8
|
||||||
|
ANDQ SI,R10
|
||||||
|
MOVQ R8,0(DI)
|
||||||
|
MOVQ CX,8(DI)
|
||||||
|
MOVQ R9,16(DI)
|
||||||
|
MOVQ AX,24(DI)
|
||||||
|
MOVQ R10,32(DI)
|
||||||
|
RET
|
132
vendor/golang.org/x/crypto/curve25519/square_amd64.s
generated
vendored
Normal file
132
vendor/golang.org/x/crypto/curve25519/square_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This code was translated into a form compatible with 6a from the public
|
||||||
|
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
#include "const_amd64.h"
|
||||||
|
|
||||||
|
// func square(out, in *[5]uint64)
|
||||||
|
TEXT ·square(SB),7,$0-16
|
||||||
|
MOVQ out+0(FP), DI
|
||||||
|
MOVQ in+8(FP), SI
|
||||||
|
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
MULQ 0(SI)
|
||||||
|
MOVQ AX,CX
|
||||||
|
MOVQ DX,R8
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
SHLQ $1,AX
|
||||||
|
MULQ 8(SI)
|
||||||
|
MOVQ AX,R9
|
||||||
|
MOVQ DX,R10
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
SHLQ $1,AX
|
||||||
|
MULQ 16(SI)
|
||||||
|
MOVQ AX,R11
|
||||||
|
MOVQ DX,R12
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
SHLQ $1,AX
|
||||||
|
MULQ 24(SI)
|
||||||
|
MOVQ AX,R13
|
||||||
|
MOVQ DX,R14
|
||||||
|
MOVQ 0(SI),AX
|
||||||
|
SHLQ $1,AX
|
||||||
|
MULQ 32(SI)
|
||||||
|
MOVQ AX,R15
|
||||||
|
MOVQ DX,BX
|
||||||
|
MOVQ 8(SI),AX
|
||||||
|
MULQ 8(SI)
|
||||||
|
ADDQ AX,R11
|
||||||
|
ADCQ DX,R12
|
||||||
|
MOVQ 8(SI),AX
|
||||||
|
SHLQ $1,AX
|
||||||
|
MULQ 16(SI)
|
||||||
|
ADDQ AX,R13
|
||||||
|
ADCQ DX,R14
|
||||||
|
MOVQ 8(SI),AX
|
||||||
|
SHLQ $1,AX
|
||||||
|
MULQ 24(SI)
|
||||||
|
ADDQ AX,R15
|
||||||
|
ADCQ DX,BX
|
||||||
|
MOVQ 8(SI),DX
|
||||||
|
IMUL3Q $38,DX,AX
|
||||||
|
MULQ 32(SI)
|
||||||
|
ADDQ AX,CX
|
||||||
|
ADCQ DX,R8
|
||||||
|
MOVQ 16(SI),AX
|
||||||
|
MULQ 16(SI)
|
||||||
|
ADDQ AX,R15
|
||||||
|
ADCQ DX,BX
|
||||||
|
MOVQ 16(SI),DX
|
||||||
|
IMUL3Q $38,DX,AX
|
||||||
|
MULQ 24(SI)
|
||||||
|
ADDQ AX,CX
|
||||||
|
ADCQ DX,R8
|
||||||
|
MOVQ 16(SI),DX
|
||||||
|
IMUL3Q $38,DX,AX
|
||||||
|
MULQ 32(SI)
|
||||||
|
ADDQ AX,R9
|
||||||
|
ADCQ DX,R10
|
||||||
|
MOVQ 24(SI),DX
|
||||||
|
IMUL3Q $19,DX,AX
|
||||||
|
MULQ 24(SI)
|
||||||
|
ADDQ AX,R9
|
||||||
|
ADCQ DX,R10
|
||||||
|
MOVQ 24(SI),DX
|
||||||
|
IMUL3Q $38,DX,AX
|
||||||
|
MULQ 32(SI)
|
||||||
|
ADDQ AX,R11
|
||||||
|
ADCQ DX,R12
|
||||||
|
MOVQ 32(SI),DX
|
||||||
|
IMUL3Q $19,DX,AX
|
||||||
|
MULQ 32(SI)
|
||||||
|
ADDQ AX,R13
|
||||||
|
ADCQ DX,R14
|
||||||
|
MOVQ $REDMASK51,SI
|
||||||
|
SHLQ $13,R8:CX
|
||||||
|
ANDQ SI,CX
|
||||||
|
SHLQ $13,R10:R9
|
||||||
|
ANDQ SI,R9
|
||||||
|
ADDQ R8,R9
|
||||||
|
SHLQ $13,R12:R11
|
||||||
|
ANDQ SI,R11
|
||||||
|
ADDQ R10,R11
|
||||||
|
SHLQ $13,R14:R13
|
||||||
|
ANDQ SI,R13
|
||||||
|
ADDQ R12,R13
|
||||||
|
SHLQ $13,BX:R15
|
||||||
|
ANDQ SI,R15
|
||||||
|
ADDQ R14,R15
|
||||||
|
IMUL3Q $19,BX,DX
|
||||||
|
ADDQ DX,CX
|
||||||
|
MOVQ CX,DX
|
||||||
|
SHRQ $51,DX
|
||||||
|
ADDQ R9,DX
|
||||||
|
ANDQ SI,CX
|
||||||
|
MOVQ DX,R8
|
||||||
|
SHRQ $51,DX
|
||||||
|
ADDQ R11,DX
|
||||||
|
ANDQ SI,R8
|
||||||
|
MOVQ DX,R9
|
||||||
|
SHRQ $51,DX
|
||||||
|
ADDQ R13,DX
|
||||||
|
ANDQ SI,R9
|
||||||
|
MOVQ DX,AX
|
||||||
|
SHRQ $51,DX
|
||||||
|
ADDQ R15,DX
|
||||||
|
ANDQ SI,AX
|
||||||
|
MOVQ DX,R10
|
||||||
|
SHRQ $51,DX
|
||||||
|
IMUL3Q $19,DX,DX
|
||||||
|
ADDQ DX,CX
|
||||||
|
ANDQ SI,R10
|
||||||
|
MOVQ CX,0(DI)
|
||||||
|
MOVQ R8,8(DI)
|
||||||
|
MOVQ R9,16(DI)
|
||||||
|
MOVQ AX,24(DI)
|
||||||
|
MOVQ R10,32(DI)
|
||||||
|
RET
|
592
vendor/golang.org/x/crypto/ocsp/ocsp.go
generated
vendored
592
vendor/golang.org/x/crypto/ocsp/ocsp.go
generated
vendored
|
@ -1,592 +0,0 @@
|
||||||
// Copyright 2013 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package ocsp parses OCSP responses as specified in RFC 2560. OCSP responses
|
|
||||||
// are signed messages attesting to the validity of a certificate for a small
|
|
||||||
// period of time. This is used to manage revocation for X.509 certificates.
|
|
||||||
package ocsp // import "golang.org/x/crypto/ocsp"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/asn1"
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
|
|
||||||
|
|
||||||
// These are internal structures that reflect the ASN.1 structure of an OCSP
|
|
||||||
// response. See RFC 2560, section 4.2.
|
|
||||||
|
|
||||||
const (
|
|
||||||
ocspSuccess = 0
|
|
||||||
ocspMalformed = 1
|
|
||||||
ocspInternalError = 2
|
|
||||||
ocspTryLater = 3
|
|
||||||
ocspSigRequired = 4
|
|
||||||
ocspUnauthorized = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
type certID struct {
|
|
||||||
HashAlgorithm pkix.AlgorithmIdentifier
|
|
||||||
NameHash []byte
|
|
||||||
IssuerKeyHash []byte
|
|
||||||
SerialNumber *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc2560#section-4.1.1
|
|
||||||
type ocspRequest struct {
|
|
||||||
TBSRequest tbsRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
type tbsRequest struct {
|
|
||||||
Version int `asn1:"explicit,tag:0,default:0,optional"`
|
|
||||||
RequestorName pkix.RDNSequence `asn1:"explicit,tag:1,optional"`
|
|
||||||
RequestList []request
|
|
||||||
}
|
|
||||||
|
|
||||||
type request struct {
|
|
||||||
Cert certID
|
|
||||||
}
|
|
||||||
|
|
||||||
type responseASN1 struct {
|
|
||||||
Status asn1.Enumerated
|
|
||||||
Response responseBytes `asn1:"explicit,tag:0"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type responseBytes struct {
|
|
||||||
ResponseType asn1.ObjectIdentifier
|
|
||||||
Response []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type basicResponse struct {
|
|
||||||
TBSResponseData responseData
|
|
||||||
SignatureAlgorithm pkix.AlgorithmIdentifier
|
|
||||||
Signature asn1.BitString
|
|
||||||
Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type responseData struct {
|
|
||||||
Raw asn1.RawContent
|
|
||||||
Version int `asn1:"optional,default:1,explicit,tag:0"`
|
|
||||||
RawResponderName asn1.RawValue `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"`
|
|
||||||
Unknown asn1.Flag `asn1:"tag:2,optional"`
|
|
||||||
ThisUpdate time.Time `asn1:"generalized"`
|
|
||||||
NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type revokedInfo struct {
|
|
||||||
RevocationTime time.Time `asn1:"generalized"`
|
|
||||||
Reason int `asn1:"explicit,tag:0,optional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
|
|
||||||
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
|
|
||||||
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
|
||||||
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
|
||||||
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
|
||||||
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
|
|
||||||
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
|
|
||||||
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
|
|
||||||
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
|
|
||||||
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
|
|
||||||
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
|
|
||||||
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
|
|
||||||
)
|
|
||||||
|
|
||||||
var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{
|
|
||||||
crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}),
|
|
||||||
crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}),
|
|
||||||
crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}),
|
|
||||||
crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}),
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
|
|
||||||
var signatureAlgorithmDetails = []struct {
|
|
||||||
algo x509.SignatureAlgorithm
|
|
||||||
oid asn1.ObjectIdentifier
|
|
||||||
pubKeyAlgo x509.PublicKeyAlgorithm
|
|
||||||
hash crypto.Hash
|
|
||||||
}{
|
|
||||||
{x509.MD2WithRSA, oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
|
|
||||||
{x509.MD5WithRSA, oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
|
|
||||||
{x509.SHA1WithRSA, oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
|
|
||||||
{x509.SHA256WithRSA, oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
|
|
||||||
{x509.SHA384WithRSA, oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
|
|
||||||
{x509.SHA512WithRSA, oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
|
|
||||||
{x509.DSAWithSHA1, oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
|
|
||||||
{x509.DSAWithSHA256, oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
|
|
||||||
{x509.ECDSAWithSHA1, oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
|
|
||||||
{x509.ECDSAWithSHA256, oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
|
|
||||||
{x509.ECDSAWithSHA384, oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
|
|
||||||
{x509.ECDSAWithSHA512, oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
|
|
||||||
func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
|
|
||||||
var pubType x509.PublicKeyAlgorithm
|
|
||||||
|
|
||||||
switch pub := pub.(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
pubType = x509.RSA
|
|
||||||
hashFunc = crypto.SHA256
|
|
||||||
sigAlgo.Algorithm = oidSignatureSHA256WithRSA
|
|
||||||
sigAlgo.Parameters = asn1.RawValue{
|
|
||||||
Tag: 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
pubType = x509.ECDSA
|
|
||||||
|
|
||||||
switch pub.Curve {
|
|
||||||
case elliptic.P224(), elliptic.P256():
|
|
||||||
hashFunc = crypto.SHA256
|
|
||||||
sigAlgo.Algorithm = oidSignatureECDSAWithSHA256
|
|
||||||
case elliptic.P384():
|
|
||||||
hashFunc = crypto.SHA384
|
|
||||||
sigAlgo.Algorithm = oidSignatureECDSAWithSHA384
|
|
||||||
case elliptic.P521():
|
|
||||||
hashFunc = crypto.SHA512
|
|
||||||
sigAlgo.Algorithm = oidSignatureECDSAWithSHA512
|
|
||||||
default:
|
|
||||||
err = errors.New("x509: unknown elliptic curve")
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = errors.New("x509: only RSA and ECDSA keys supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if requestedSigAlgo == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
found := false
|
|
||||||
for _, details := range signatureAlgorithmDetails {
|
|
||||||
if details.algo == requestedSigAlgo {
|
|
||||||
if details.pubKeyAlgo != pubType {
|
|
||||||
err = errors.New("x509: requested SignatureAlgorithm does not match private key type")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
|
|
||||||
if hashFunc == 0 {
|
|
||||||
err = errors.New("x509: cannot sign with hash function requested")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
err = errors.New("x509: unknown SignatureAlgorithm")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(agl): this is taken from crypto/x509 and so should probably be exported
|
|
||||||
// from crypto/x509 or crypto/x509/pkix.
|
|
||||||
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm {
|
|
||||||
for _, details := range signatureAlgorithmDetails {
|
|
||||||
if oid.Equal(details.oid) {
|
|
||||||
return details.algo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return x509.UnknownSignatureAlgorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(rlb): This is not taken from crypto/x509, but it's of the same general form.
|
|
||||||
func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash {
|
|
||||||
for hash, oid := range hashOIDs {
|
|
||||||
if oid.Equal(target) {
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crypto.Hash(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the exposed reflection of the internal OCSP structures.
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Good means that the certificate is valid.
|
|
||||||
Good = iota
|
|
||||||
// Revoked means that the certificate has been deliberately revoked.
|
|
||||||
Revoked = iota
|
|
||||||
// Unknown means that the OCSP responder doesn't know about the certificate.
|
|
||||||
Unknown = iota
|
|
||||||
// ServerFailed means that the OCSP responder failed to process the request.
|
|
||||||
ServerFailed = iota
|
|
||||||
)
|
|
||||||
|
|
||||||
// Request represents an OCSP request. See RFC 2560.
|
|
||||||
type Request struct {
|
|
||||||
HashAlgorithm crypto.Hash
|
|
||||||
IssuerNameHash []byte
|
|
||||||
IssuerKeyHash []byte
|
|
||||||
SerialNumber *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Response represents an OCSP response. See RFC 2560.
|
|
||||||
type Response struct {
|
|
||||||
// Status is one of {Good, Revoked, Unknown, ServerFailed}
|
|
||||||
Status int
|
|
||||||
SerialNumber *big.Int
|
|
||||||
ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time
|
|
||||||
RevocationReason int
|
|
||||||
Certificate *x509.Certificate
|
|
||||||
// TBSResponseData contains the raw bytes of the signed response. If
|
|
||||||
// Certificate is nil then this can be used to verify Signature.
|
|
||||||
TBSResponseData []byte
|
|
||||||
Signature []byte
|
|
||||||
SignatureAlgorithm x509.SignatureAlgorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are pre-serialized error responses for the various non-success codes
|
|
||||||
// defined by OCSP. The Unauthorized code in particular can be used by an OCSP
|
|
||||||
// responder that supports only pre-signed responses as a response to requests
|
|
||||||
// for certificates with unknown status. See RFC 5019.
|
|
||||||
var (
|
|
||||||
MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
|
|
||||||
InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
|
|
||||||
TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
|
|
||||||
SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
|
|
||||||
UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
|
|
||||||
)
|
|
||||||
|
|
||||||
// CheckSignatureFrom checks that the signature in resp is a valid signature
|
|
||||||
// from issuer. This should only be used if resp.Certificate is nil. Otherwise,
|
|
||||||
// the OCSP response contained an intermediate certificate that created the
|
|
||||||
// signature. That signature is checked by ParseResponse and only
|
|
||||||
// resp.Certificate remains to be validated.
|
|
||||||
func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error {
|
|
||||||
return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseError results from an invalid OCSP response.
|
|
||||||
type ParseError string
|
|
||||||
|
|
||||||
func (p ParseError) Error() string {
|
|
||||||
return string(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseRequest parses an OCSP request in DER form. It only supports
|
|
||||||
// requests for a single certificate. Signed requests are not supported.
|
|
||||||
// If a request includes a signature, it will result in a ParseError.
|
|
||||||
func ParseRequest(bytes []byte) (*Request, error) {
|
|
||||||
var req ocspRequest
|
|
||||||
rest, err := asn1.Unmarshal(bytes, &req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(rest) > 0 {
|
|
||||||
return nil, ParseError("trailing data in OCSP request")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(req.TBSRequest.RequestList) == 0 {
|
|
||||||
return nil, ParseError("OCSP request contains no request body")
|
|
||||||
}
|
|
||||||
innerRequest := req.TBSRequest.RequestList[0]
|
|
||||||
|
|
||||||
hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm)
|
|
||||||
if hashFunc == crypto.Hash(0) {
|
|
||||||
return nil, ParseError("OCSP request uses unknown hash function")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Request{
|
|
||||||
HashAlgorithm: hashFunc,
|
|
||||||
IssuerNameHash: innerRequest.Cert.NameHash,
|
|
||||||
IssuerKeyHash: innerRequest.Cert.IssuerKeyHash,
|
|
||||||
SerialNumber: innerRequest.Cert.SerialNumber,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseResponse parses an OCSP response in DER form. It only supports
|
|
||||||
// responses for a single certificate. If the response contains a certificate
|
|
||||||
// then the signature over the response is checked. If issuer is not nil then
|
|
||||||
// it will be used to validate the signature or embedded certificate. Invalid
|
|
||||||
// signatures or parse failures will result in a ParseError.
|
|
||||||
func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
|
|
||||||
var resp responseASN1
|
|
||||||
rest, err := asn1.Unmarshal(bytes, &resp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(rest) > 0 {
|
|
||||||
return nil, ParseError("trailing data in OCSP response")
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := new(Response)
|
|
||||||
if resp.Status != ocspSuccess {
|
|
||||||
ret.Status = ServerFailed
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) {
|
|
||||||
return nil, ParseError("bad OCSP response type")
|
|
||||||
}
|
|
||||||
|
|
||||||
var basicResp basicResponse
|
|
||||||
rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(basicResp.Certificates) > 1 {
|
|
||||||
return nil, ParseError("OCSP response contains bad number of certificates")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(basicResp.TBSResponseData.Responses) != 1 {
|
|
||||||
return nil, ParseError("OCSP response contains bad number of responses")
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.TBSResponseData = basicResp.TBSResponseData.Raw
|
|
||||||
ret.Signature = basicResp.Signature.RightAlign()
|
|
||||||
ret.SignatureAlgorithm = getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm)
|
|
||||||
|
|
||||||
if len(basicResp.Certificates) > 0 {
|
|
||||||
ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
|
|
||||||
return nil, ParseError("bad OCSP signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
if issuer != nil {
|
|
||||||
if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil {
|
|
||||||
return nil, ParseError("bad signature on embedded certificate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if issuer != nil {
|
|
||||||
if err := ret.CheckSignatureFrom(issuer); err != nil {
|
|
||||||
return nil, ParseError("bad OCSP signature")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r := basicResp.TBSResponseData.Responses[0]
|
|
||||||
|
|
||||||
ret.SerialNumber = r.CertID.SerialNumber
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case bool(r.Good):
|
|
||||||
ret.Status = Good
|
|
||||||
case bool(r.Unknown):
|
|
||||||
ret.Status = Unknown
|
|
||||||
default:
|
|
||||||
ret.Status = Revoked
|
|
||||||
ret.RevokedAt = r.Revoked.RevocationTime
|
|
||||||
ret.RevocationReason = r.Revoked.Reason
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.ProducedAt = basicResp.TBSResponseData.ProducedAt
|
|
||||||
ret.ThisUpdate = r.ThisUpdate
|
|
||||||
ret.NextUpdate = r.NextUpdate
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequestOptions contains options for constructing OCSP requests.
|
|
||||||
type RequestOptions struct {
|
|
||||||
// Hash contains the hash function that should be used when
|
|
||||||
// constructing the OCSP request. If zero, SHA-1 will be used.
|
|
||||||
Hash crypto.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts *RequestOptions) hash() crypto.Hash {
|
|
||||||
if opts == nil || opts.Hash == 0 {
|
|
||||||
// SHA-1 is nearly universally used in OCSP.
|
|
||||||
return crypto.SHA1
|
|
||||||
}
|
|
||||||
return opts.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRequest returns a DER-encoded, OCSP request for the status of cert. If
|
|
||||||
// opts is nil then sensible defaults are used.
|
|
||||||
func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) {
|
|
||||||
hashFunc := opts.hash()
|
|
||||||
|
|
||||||
// OCSP seems to be the only place where these raw hash identifiers are
|
|
||||||
// used. I took the following from
|
|
||||||
// http://msdn.microsoft.com/en-us/library/ff635603.aspx
|
|
||||||
var hashOID asn1.ObjectIdentifier
|
|
||||||
hashOID, ok := hashOIDs[hashFunc]
|
|
||||||
if !ok {
|
|
||||||
return nil, x509.ErrUnsupportedAlgorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hashFunc.Available() {
|
|
||||||
return nil, x509.ErrUnsupportedAlgorithm
|
|
||||||
}
|
|
||||||
h := opts.hash().New()
|
|
||||||
|
|
||||||
var publicKeyInfo struct {
|
|
||||||
Algorithm pkix.AlgorithmIdentifier
|
|
||||||
PublicKey asn1.BitString
|
|
||||||
}
|
|
||||||
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h.Write(publicKeyInfo.PublicKey.RightAlign())
|
|
||||||
issuerKeyHash := h.Sum(nil)
|
|
||||||
|
|
||||||
h.Reset()
|
|
||||||
h.Write(issuer.RawSubject)
|
|
||||||
issuerNameHash := h.Sum(nil)
|
|
||||||
|
|
||||||
return asn1.Marshal(ocspRequest{
|
|
||||||
tbsRequest{
|
|
||||||
Version: 0,
|
|
||||||
RequestList: []request{
|
|
||||||
{
|
|
||||||
Cert: certID{
|
|
||||||
pkix.AlgorithmIdentifier{
|
|
||||||
Algorithm: hashOID,
|
|
||||||
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
|
|
||||||
},
|
|
||||||
issuerNameHash,
|
|
||||||
issuerKeyHash,
|
|
||||||
cert.SerialNumber,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResponse returns a DER-encoded OCSP response with the specified contents.
|
|
||||||
// The fields in the response are populated as follows:
|
|
||||||
//
|
|
||||||
// The responder cert is used to populate the ResponderName field, and the certificate
|
|
||||||
// itself is provided alongside the OCSP response signature.
|
|
||||||
//
|
|
||||||
// The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields.
|
|
||||||
// (SHA-1 is used for the hash function; this is not configurable.)
|
|
||||||
//
|
|
||||||
// The template is used to populate the SerialNumber, RevocationStatus, RevokedAt,
|
|
||||||
// RevocationReason, ThisUpdate, and NextUpdate fields.
|
|
||||||
//
|
|
||||||
// The ProducedAt date is automatically set to the current date, to the nearest minute.
|
|
||||||
func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) {
|
|
||||||
var publicKeyInfo struct {
|
|
||||||
Algorithm pkix.AlgorithmIdentifier
|
|
||||||
PublicKey asn1.BitString
|
|
||||||
}
|
|
||||||
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h := sha1.New()
|
|
||||||
h.Write(publicKeyInfo.PublicKey.RightAlign())
|
|
||||||
issuerKeyHash := h.Sum(nil)
|
|
||||||
|
|
||||||
h.Reset()
|
|
||||||
h.Write(issuer.RawSubject)
|
|
||||||
issuerNameHash := h.Sum(nil)
|
|
||||||
|
|
||||||
innerResponse := singleResponse{
|
|
||||||
CertID: certID{
|
|
||||||
HashAlgorithm: pkix.AlgorithmIdentifier{
|
|
||||||
Algorithm: hashOIDs[crypto.SHA1],
|
|
||||||
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
|
|
||||||
},
|
|
||||||
NameHash: issuerNameHash,
|
|
||||||
IssuerKeyHash: issuerKeyHash,
|
|
||||||
SerialNumber: template.SerialNumber,
|
|
||||||
},
|
|
||||||
ThisUpdate: template.ThisUpdate.UTC(),
|
|
||||||
NextUpdate: template.NextUpdate.UTC(),
|
|
||||||
}
|
|
||||||
|
|
||||||
switch template.Status {
|
|
||||||
case Good:
|
|
||||||
innerResponse.Good = true
|
|
||||||
case Unknown:
|
|
||||||
innerResponse.Unknown = true
|
|
||||||
case Revoked:
|
|
||||||
innerResponse.Revoked = revokedInfo{
|
|
||||||
RevocationTime: template.RevokedAt.UTC(),
|
|
||||||
Reason: template.RevocationReason,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
responderName := asn1.RawValue{
|
|
||||||
Class: 2, // context-specific
|
|
||||||
Tag: 1, // explicit tag
|
|
||||||
IsCompound: true,
|
|
||||||
Bytes: responderCert.RawSubject,
|
|
||||||
}
|
|
||||||
tbsResponseData := responseData{
|
|
||||||
Version: 0,
|
|
||||||
RawResponderName: responderName,
|
|
||||||
ProducedAt: time.Now().Truncate(time.Minute).UTC(),
|
|
||||||
Responses: []singleResponse{innerResponse},
|
|
||||||
}
|
|
||||||
|
|
||||||
tbsResponseDataDER, err := asn1.Marshal(tbsResponseData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
responseHash := hashFunc.New()
|
|
||||||
responseHash.Write(tbsResponseDataDER)
|
|
||||||
signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
response := basicResponse{
|
|
||||||
TBSResponseData: tbsResponseData,
|
|
||||||
SignatureAlgorithm: signatureAlgorithm,
|
|
||||||
Signature: asn1.BitString{
|
|
||||||
Bytes: signature,
|
|
||||||
BitLength: 8 * len(signature),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if template.Certificate != nil {
|
|
||||||
response.Certificates = []asn1.RawValue{
|
|
||||||
asn1.RawValue{FullBytes: template.Certificate.Raw},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
responseDER, err := asn1.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return asn1.Marshal(responseASN1{
|
|
||||||
Status: ocspSuccess,
|
|
||||||
Response: responseBytes{
|
|
||||||
ResponseType: idPKIXOCSPBasic,
|
|
||||||
Response: responseDER,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
126
vendor/golang.org/x/crypto/otr/libotr_test_helper.c
generated
vendored
126
vendor/golang.org/x/crypto/otr/libotr_test_helper.c
generated
vendored
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <proto.h>
|
#include <proto.h>
|
||||||
#include <message.h>
|
#include <message.h>
|
||||||
|
#include <privkey.h>
|
||||||
|
|
||||||
static int g_session_established = 0;
|
static int g_session_established = 0;
|
||||||
|
|
||||||
|
@ -20,65 +21,61 @@ OtrlPolicy policy(void *opdata, ConnContext *context) {
|
||||||
return OTRL_POLICY_ALWAYS;
|
return OTRL_POLICY_ALWAYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) {
|
int is_logged_in(void *opdata, const char *accountname, const char *protocol,
|
||||||
|
const char *recipient) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) {
|
void inject_message(void *opdata, const char *accountname, const char *protocol,
|
||||||
|
const char *recipient, const char *message) {
|
||||||
printf("%s\n", message);
|
printf("%s\n", message);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fprintf(stderr, "libotr helper sent: %s\n", message);
|
fprintf(stderr, "libotr helper sent: %s\n", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify(void *opdata, OtrlNotifyLevel level, const char *accountname, const char *protocol, const char *username, const char *title, const char *primary, const char *secondary) {
|
void update_context_list(void *opdata) {}
|
||||||
fprintf(stderr, "NOTIFY: %s %s %s %s\n", username, title, primary, secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
int display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg) {
|
void new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
|
||||||
fprintf(stderr, "MESSAGE: %s %s\n", username, msg);
|
const char *protocol, const char *username,
|
||||||
return 1;
|
unsigned char fingerprint[20]) {
|
||||||
}
|
|
||||||
|
|
||||||
void update_context_list(void *opdata) {
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *protocol_name(void *opdata, const char *protocol) {
|
|
||||||
return "PROTOCOL";
|
|
||||||
}
|
|
||||||
|
|
||||||
void protocol_name_free(void *opdata, const char *protocol_name) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]) {
|
|
||||||
fprintf(stderr, "NEW FINGERPRINT\n");
|
fprintf(stderr, "NEW FINGERPRINT\n");
|
||||||
g_session_established = 1;
|
g_session_established = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_fingerprints(void *opdata) {
|
void write_fingerprints(void *opdata) {}
|
||||||
}
|
|
||||||
|
|
||||||
void gone_secure(void *opdata, ConnContext *context) {
|
void gone_secure(void *opdata, ConnContext *context) {}
|
||||||
}
|
|
||||||
|
|
||||||
void gone_insecure(void *opdata, ConnContext *context) {
|
void gone_insecure(void *opdata, ConnContext *context) {}
|
||||||
}
|
|
||||||
|
|
||||||
void still_secure(void *opdata, ConnContext *context, int is_reply) {
|
void still_secure(void *opdata, ConnContext *context, int is_reply) {}
|
||||||
}
|
|
||||||
|
|
||||||
void log_message(void *opdata, const char *message) {
|
int max_message_size(void *opdata, ConnContext *context) { return 99999; }
|
||||||
fprintf(stderr, "MESSAGE: %s\n", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
int max_message_size(void *opdata, ConnContext *context) {
|
const char *account_name(void *opdata, const char *account,
|
||||||
return 99999;
|
const char *protocol) {
|
||||||
}
|
|
||||||
|
|
||||||
const char *account_name(void *opdata, const char *account, const char *protocol) {
|
|
||||||
return "ACCOUNT";
|
return "ACCOUNT";
|
||||||
}
|
}
|
||||||
|
|
||||||
void account_name_free(void *opdata, const char *account_name) {
|
void account_name_free(void *opdata, const char *account_name) {}
|
||||||
|
|
||||||
|
const char *error_message(void *opdata, ConnContext *context,
|
||||||
|
OtrlErrorCode err_code) {
|
||||||
|
return "ERR";
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_message_free(void *opdata, const char *msg) {}
|
||||||
|
|
||||||
|
void resent_msg_prefix_free(void *opdata, const char *prefix) {}
|
||||||
|
|
||||||
|
void handle_smp_event(void *opdata, OtrlSMPEvent smp_event,
|
||||||
|
ConnContext *context, unsigned short progress_event,
|
||||||
|
char *question) {}
|
||||||
|
|
||||||
|
void handle_msg_event(void *opdata, OtrlMessageEvent msg_event,
|
||||||
|
ConnContext *context, const char *message,
|
||||||
|
gcry_error_t err) {
|
||||||
|
fprintf(stderr, "msg event: %d %s\n", msg_event, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
OtrlMessageAppOps uiops = {
|
OtrlMessageAppOps uiops = {
|
||||||
|
@ -86,26 +83,47 @@ OtrlMessageAppOps uiops = {
|
||||||
NULL,
|
NULL,
|
||||||
is_logged_in,
|
is_logged_in,
|
||||||
inject_message,
|
inject_message,
|
||||||
notify,
|
|
||||||
display_otr_message,
|
|
||||||
update_context_list,
|
update_context_list,
|
||||||
protocol_name,
|
|
||||||
protocol_name_free,
|
|
||||||
new_fingerprint,
|
new_fingerprint,
|
||||||
write_fingerprints,
|
write_fingerprints,
|
||||||
gone_secure,
|
gone_secure,
|
||||||
gone_insecure,
|
gone_insecure,
|
||||||
still_secure,
|
still_secure,
|
||||||
log_message,
|
|
||||||
max_message_size,
|
max_message_size,
|
||||||
account_name,
|
account_name,
|
||||||
account_name_free,
|
account_name_free,
|
||||||
|
NULL, /* received_symkey */
|
||||||
|
error_message,
|
||||||
|
error_message_free,
|
||||||
|
NULL, /* resent_msg_prefix */
|
||||||
|
resent_msg_prefix_free,
|
||||||
|
handle_smp_event,
|
||||||
|
handle_msg_event,
|
||||||
|
NULL /* create_instag */,
|
||||||
|
NULL /* convert_msg */,
|
||||||
|
NULL /* convert_free */,
|
||||||
|
NULL /* timer_control */,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char kPrivateKeyData[] = "(privkeys (account (name \"account\") (protocol proto) (private-key (dsa (p #00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB8C031D3561FECEE72EBB4A090D450A9B7A857#) (q #00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#) (g #535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57597766A2F9CE3857D7ACE3E1E3BC1FC6F26#) (y #0AC8670AD767D7A8D9D14CC1AC6744CD7D76F993B77FFD9E39DF01E5A6536EF65E775FCEF2A983E2A19BD6415500F6979715D9FD1257E1FE2B6F5E1E74B333079E7C880D39868462A93454B41877BE62E5EF0A041C2EE9C9E76BD1E12AE25D9628DECB097025DD625EF49C3258A1A3C0FF501E3DC673B76D7BABF349009B6ECF#) (x #14D0345A3562C480A039E3C72764F72D79043216#)))))\n";
|
static const char kPrivateKeyData[] =
|
||||||
|
"(privkeys (account (name \"account\") (protocol proto) (private-key (dsa "
|
||||||
|
"(p "
|
||||||
|
"#00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F"
|
||||||
|
"30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E"
|
||||||
|
"5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB"
|
||||||
|
"8C031D3561FECEE72EBB4A090D450A9B7A857#) (q "
|
||||||
|
"#00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#) (g "
|
||||||
|
"#535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F"
|
||||||
|
"1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F"
|
||||||
|
"6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57"
|
||||||
|
"597766A2F9CE3857D7ACE3E1E3BC1FC6F26#) (y "
|
||||||
|
"#0AC8670AD767D7A8D9D14CC1AC6744CD7D76F993B77FFD9E39DF01E5A6536EF65E775FCEF"
|
||||||
|
"2A983E2A19BD6415500F6979715D9FD1257E1FE2B6F5E1E74B333079E7C880D39868462A93"
|
||||||
|
"454B41877BE62E5EF0A041C2EE9C9E76BD1E12AE25D9628DECB097025DD625EF49C3258A1A"
|
||||||
|
"3C0FF501E3DC673B76D7BABF349009B6ECF#) (x "
|
||||||
|
"#14D0345A3562C480A039E3C72764F72D79043216#)))))\n";
|
||||||
|
|
||||||
int
|
int main() {
|
||||||
main() {
|
|
||||||
OTRL_INIT;
|
OTRL_INIT;
|
||||||
|
|
||||||
// We have to write the private key information to a file because the libotr
|
// We have to write the private key information to a file because the libotr
|
||||||
|
@ -116,7 +134,8 @@ main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
char private_key_file[256];
|
char private_key_file[256];
|
||||||
snprintf(private_key_file, sizeof(private_key_file), "%s/libotr_test_helper_privatekeys-XXXXXX", tmpdir);
|
snprintf(private_key_file, sizeof(private_key_file),
|
||||||
|
"%s/libotr_test_helper_privatekeys-XXXXXX", tmpdir);
|
||||||
int fd = mkstemp(private_key_file);
|
int fd = mkstemp(private_key_file);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
perror("creating temp file");
|
perror("creating temp file");
|
||||||
|
@ -142,7 +161,9 @@ main() {
|
||||||
|
|
||||||
char *newmessage = NULL;
|
char *newmessage = NULL;
|
||||||
OtrlTLV *tlvs;
|
OtrlTLV *tlvs;
|
||||||
int ignore_message = otrl_message_receiving(userstate, &uiops, NULL, "account", "proto", "peer", message, &newmessage, &tlvs, NULL, NULL);
|
int ignore_message = otrl_message_receiving(
|
||||||
|
userstate, &uiops, NULL, "account", "proto", "peer", message,
|
||||||
|
&newmessage, &tlvs, NULL, NULL, NULL);
|
||||||
if (tlvs) {
|
if (tlvs) {
|
||||||
otrl_tlv_free(tlvs);
|
otrl_tlv_free(tlvs);
|
||||||
}
|
}
|
||||||
|
@ -154,16 +175,21 @@ main() {
|
||||||
gcry_error_t err;
|
gcry_error_t err;
|
||||||
char *newmessage = NULL;
|
char *newmessage = NULL;
|
||||||
|
|
||||||
err = otrl_message_sending(userstate, &uiops, NULL, "account", "proto", "peer", "test message", NULL, &newmessage, NULL, NULL);
|
err = otrl_message_sending(userstate, &uiops, NULL, "account", "proto",
|
||||||
|
"peer", 0, "test message", NULL, &newmessage,
|
||||||
|
OTRL_FRAGMENT_SEND_SKIP, NULL, NULL, NULL);
|
||||||
if (newmessage == NULL) {
|
if (newmessage == NULL) {
|
||||||
fprintf(stderr, "libotr didn't encrypt message\n");
|
fprintf(stderr, "libotr didn't encrypt message\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
write(1, newmessage, strlen(newmessage));
|
write(1, newmessage, strlen(newmessage));
|
||||||
write(1, "\n", 1);
|
write(1, "\n", 1);
|
||||||
g_session_established = 0;
|
fprintf(stderr, "libotr sent: %s\n", newmessage);
|
||||||
otrl_message_free(newmessage);
|
otrl_message_free(newmessage);
|
||||||
|
|
||||||
|
g_session_established = 0;
|
||||||
write(1, "?OTRv2?\n", 8);
|
write(1, "?OTRv2?\n", 8);
|
||||||
|
fprintf(stderr, "libotr sent: ?OTRv2\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
39
vendor/golang.org/x/crypto/otr/otr.go
generated
vendored
39
vendor/golang.org/x/crypto/otr/otr.go
generated
vendored
|
@ -277,7 +277,7 @@ func (c *Conversation) Receive(in []byte) (out []byte, encrypted bool, change Se
|
||||||
in = in[len(msgPrefix) : len(in)-1]
|
in = in[len(msgPrefix) : len(in)-1]
|
||||||
} else if version := isQuery(in); version > 0 {
|
} else if version := isQuery(in); version > 0 {
|
||||||
c.authState = authStateAwaitingDHKey
|
c.authState = authStateAwaitingDHKey
|
||||||
c.myKeyId = 0
|
c.reset()
|
||||||
toSend = c.encode(c.generateDHCommit())
|
toSend = c.encode(c.generateDHCommit())
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
@ -311,7 +311,7 @@ func (c *Conversation) Receive(in []byte) (out []byte, encrypted bool, change Se
|
||||||
if err = c.processDHCommit(msg); err != nil {
|
if err = c.processDHCommit(msg); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.myKeyId = 0
|
c.reset()
|
||||||
toSend = c.encode(c.generateDHKey())
|
toSend = c.encode(c.generateDHKey())
|
||||||
return
|
return
|
||||||
case authStateAwaitingDHKey:
|
case authStateAwaitingDHKey:
|
||||||
|
@ -330,7 +330,7 @@ func (c *Conversation) Receive(in []byte) (out []byte, encrypted bool, change Se
|
||||||
if err = c.processDHCommit(msg); err != nil {
|
if err = c.processDHCommit(msg); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.myKeyId = 0
|
c.reset()
|
||||||
toSend = c.encode(c.generateDHKey())
|
toSend = c.encode(c.generateDHKey())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ func (c *Conversation) Receive(in []byte) (out []byte, encrypted bool, change Se
|
||||||
if err = c.processDHCommit(msg); err != nil {
|
if err = c.processDHCommit(msg); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.myKeyId = 0
|
c.reset()
|
||||||
toSend = c.encode(c.generateDHKey())
|
toSend = c.encode(c.generateDHKey())
|
||||||
c.authState = authStateAwaitingRevealSig
|
c.authState = authStateAwaitingRevealSig
|
||||||
default:
|
default:
|
||||||
|
@ -417,12 +417,11 @@ func (c *Conversation) Receive(in []byte) (out []byte, encrypted bool, change Se
|
||||||
change = SMPSecretNeeded
|
change = SMPSecretNeeded
|
||||||
c.smp.saved = &inTLV
|
c.smp.saved = &inTLV
|
||||||
return
|
return
|
||||||
} else if err == smpFailureError {
|
}
|
||||||
|
if err == smpFailureError {
|
||||||
err = nil
|
err = nil
|
||||||
change = SMPFailed
|
change = SMPFailed
|
||||||
return
|
} else if complete {
|
||||||
}
|
|
||||||
if complete {
|
|
||||||
change = SMPComplete
|
change = SMPComplete
|
||||||
}
|
}
|
||||||
if reply.typ != 0 {
|
if reply.typ != 0 {
|
||||||
|
@ -848,7 +847,6 @@ func (c *Conversation) rotateDHKeys() {
|
||||||
slot := &c.keySlots[i]
|
slot := &c.keySlots[i]
|
||||||
if slot.used && slot.myKeyId == c.myKeyId-1 {
|
if slot.used && slot.myKeyId == c.myKeyId-1 {
|
||||||
slot.used = false
|
slot.used = false
|
||||||
c.oldMACs = append(c.oldMACs, slot.sendMACKey...)
|
|
||||||
c.oldMACs = append(c.oldMACs, slot.recvMACKey...)
|
c.oldMACs = append(c.oldMACs, slot.recvMACKey...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -924,7 +922,6 @@ func (c *Conversation) processData(in []byte) (out []byte, tlvs []tlv, err error
|
||||||
slot := &c.keySlots[i]
|
slot := &c.keySlots[i]
|
||||||
if slot.used && slot.theirKeyId == theirKeyId-1 {
|
if slot.used && slot.theirKeyId == theirKeyId-1 {
|
||||||
slot.used = false
|
slot.used = false
|
||||||
c.oldMACs = append(c.oldMACs, slot.sendMACKey...)
|
|
||||||
c.oldMACs = append(c.oldMACs, slot.recvMACKey...)
|
c.oldMACs = append(c.oldMACs, slot.recvMACKey...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -946,6 +943,7 @@ func (c *Conversation) processData(in []byte) (out []byte, tlvs []tlv, err error
|
||||||
t.data, tlvData, ok3 = getNBytes(tlvData, int(t.length))
|
t.data, tlvData, ok3 = getNBytes(tlvData, int(t.length))
|
||||||
if !ok1 || !ok2 || !ok3 {
|
if !ok1 || !ok2 || !ok3 {
|
||||||
err = errors.New("otr: corrupt tlv data")
|
err = errors.New("otr: corrupt tlv data")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
tlvs = append(tlvs, t)
|
tlvs = append(tlvs, t)
|
||||||
}
|
}
|
||||||
|
@ -1039,8 +1037,7 @@ func (c *Conversation) calcDataKeys(myKeyId, theirKeyId uint32) (slot *keySlot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if slot == nil {
|
if slot == nil {
|
||||||
err = errors.New("otr: internal error: no key slots")
|
return nil, errors.New("otr: internal error: no more key slots")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var myPriv, myPub, theirPub *big.Int
|
var myPriv, myPub, theirPub *big.Int
|
||||||
|
@ -1096,6 +1093,10 @@ func (c *Conversation) calcDataKeys(myKeyId, theirKeyId uint32) (slot *keySlot,
|
||||||
h.Write(slot.recvAESKey)
|
h.Write(slot.recvAESKey)
|
||||||
slot.recvMACKey = h.Sum(slot.recvMACKey[:0])
|
slot.recvMACKey = h.Sum(slot.recvMACKey[:0])
|
||||||
|
|
||||||
|
slot.theirKeyId = theirKeyId
|
||||||
|
slot.myKeyId = myKeyId
|
||||||
|
slot.used = true
|
||||||
|
|
||||||
zero(slot.theirLastCtr[:])
|
zero(slot.theirLastCtr[:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1162,6 +1163,14 @@ func (c *Conversation) encode(msg []byte) [][]byte {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) reset() {
|
||||||
|
c.myKeyId = 0
|
||||||
|
|
||||||
|
for i := range c.keySlots {
|
||||||
|
c.keySlots[i].used = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type PublicKey struct {
|
type PublicKey struct {
|
||||||
dsa.PublicKey
|
dsa.PublicKey
|
||||||
}
|
}
|
||||||
|
@ -1305,6 +1314,12 @@ func (priv *PrivateKey) Import(in []byte) bool {
|
||||||
mpis[i] = new(big.Int).SetBytes(mpiBytes)
|
mpis[i] = new(big.Int).SetBytes(mpiBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, mpi := range mpis {
|
||||||
|
if mpi.Sign() <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
priv.PrivateKey.P = mpis[0]
|
priv.PrivateKey.P = mpis[0]
|
||||||
priv.PrivateKey.Q = mpis[1]
|
priv.PrivateKey.Q = mpis[1]
|
||||||
priv.PrivateKey.G = mpis[2]
|
priv.PrivateKey.G = mpis[2]
|
||||||
|
|
7
vendor/golang.org/x/crypto/ssh/test/doc.go
generated
vendored
Normal file
7
vendor/golang.org/x/crypto/ssh/test/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package test contains integration tests for the
|
||||||
|
// golang.org/x/crypto/ssh package.
|
||||||
|
package test // import "golang.org/x/crypto/ssh/test"
|
173
vendor/golang.org/x/crypto/ssh/test/sshd_test_pw.c
generated
vendored
Normal file
173
vendor/golang.org/x/crypto/ssh/test/sshd_test_pw.c
generated
vendored
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// sshd_test_pw.c
|
||||||
|
// Wrapper to inject test password data for sshd PAM authentication
|
||||||
|
//
|
||||||
|
// This wrapper implements custom versions of getpwnam, getpwnam_r,
|
||||||
|
// getspnam and getspnam_r. These functions first call their real
|
||||||
|
// libc versions, then check if the requested user matches test user
|
||||||
|
// specified in env variable TEST_USER and if so replace the password
|
||||||
|
// with crypted() value of TEST_PASSWD env variable.
|
||||||
|
//
|
||||||
|
// Compile:
|
||||||
|
// gcc -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c
|
||||||
|
//
|
||||||
|
// Compile with debug:
|
||||||
|
// gcc -DVERBOSE -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c
|
||||||
|
//
|
||||||
|
// Run sshd:
|
||||||
|
// LD_PRELOAD="sshd_test_pw.so" TEST_USER="..." TEST_PASSWD="..." sshd ...
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <string.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <shadow.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef VERBOSE
|
||||||
|
#define DEBUG(X...) fprintf(stderr, X)
|
||||||
|
#else
|
||||||
|
#define DEBUG(X...) while (0) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* crypt() password */
|
||||||
|
static char *
|
||||||
|
pwhash(char *passwd) {
|
||||||
|
return strdup(crypt(passwd, "$6$"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pointers to real functions in libc */
|
||||||
|
static struct passwd * (*real_getpwnam)(const char *) = NULL;
|
||||||
|
static int (*real_getpwnam_r)(const char *, struct passwd *, char *, size_t, struct passwd **) = NULL;
|
||||||
|
static struct spwd * (*real_getspnam)(const char *) = NULL;
|
||||||
|
static int (*real_getspnam_r)(const char *, struct spwd *, char *, size_t, struct spwd **) = NULL;
|
||||||
|
|
||||||
|
/* Cached test user and test password */
|
||||||
|
static char *test_user = NULL;
|
||||||
|
static char *test_passwd_hash = NULL;
|
||||||
|
|
||||||
|
static void
|
||||||
|
init(void) {
|
||||||
|
/* Fetch real libc function pointers */
|
||||||
|
real_getpwnam = dlsym(RTLD_NEXT, "getpwnam");
|
||||||
|
real_getpwnam_r = dlsym(RTLD_NEXT, "getpwnam_r");
|
||||||
|
real_getspnam = dlsym(RTLD_NEXT, "getspnam");
|
||||||
|
real_getspnam_r = dlsym(RTLD_NEXT, "getspnam_r");
|
||||||
|
|
||||||
|
/* abort if env variables are not defined */
|
||||||
|
if (getenv("TEST_USER") == NULL || getenv("TEST_PASSWD") == NULL) {
|
||||||
|
fprintf(stderr, "env variables TEST_USER and TEST_PASSWD are missing\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fetch test user and test password from env */
|
||||||
|
test_user = strdup(getenv("TEST_USER"));
|
||||||
|
test_passwd_hash = pwhash(getenv("TEST_PASSWD"));
|
||||||
|
|
||||||
|
DEBUG("sshd_test_pw init():\n");
|
||||||
|
DEBUG("\treal_getpwnam: %p\n", real_getpwnam);
|
||||||
|
DEBUG("\treal_getpwnam_r: %p\n", real_getpwnam_r);
|
||||||
|
DEBUG("\treal_getspnam: %p\n", real_getspnam);
|
||||||
|
DEBUG("\treal_getspnam_r: %p\n", real_getspnam_r);
|
||||||
|
DEBUG("\tTEST_USER: '%s'\n", test_user);
|
||||||
|
DEBUG("\tTEST_PASSWD: '%s'\n", getenv("TEST_PASSWD"));
|
||||||
|
DEBUG("\tTEST_PASSWD_HASH: '%s'\n", test_passwd_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_test_user(const char *name) {
|
||||||
|
if (test_user != NULL && strcmp(test_user, name) == 0)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getpwnam */
|
||||||
|
|
||||||
|
struct passwd *
|
||||||
|
getpwnam(const char *name) {
|
||||||
|
struct passwd *pw;
|
||||||
|
|
||||||
|
DEBUG("sshd_test_pw getpwnam(%s)\n", name);
|
||||||
|
|
||||||
|
if (real_getpwnam == NULL)
|
||||||
|
init();
|
||||||
|
if ((pw = real_getpwnam(name)) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (is_test_user(name))
|
||||||
|
pw->pw_passwd = strdup(test_passwd_hash);
|
||||||
|
|
||||||
|
return pw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getpwnam_r */
|
||||||
|
|
||||||
|
int
|
||||||
|
getpwnam_r(const char *name,
|
||||||
|
struct passwd *pwd,
|
||||||
|
char *buf,
|
||||||
|
size_t buflen,
|
||||||
|
struct passwd **result) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
DEBUG("sshd_test_pw getpwnam_r(%s)\n", name);
|
||||||
|
|
||||||
|
if (real_getpwnam_r == NULL)
|
||||||
|
init();
|
||||||
|
if ((r = real_getpwnam_r(name, pwd, buf, buflen, result)) != 0 || *result == NULL)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (is_test_user(name))
|
||||||
|
pwd->pw_passwd = strdup(test_passwd_hash);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getspnam */
|
||||||
|
|
||||||
|
struct spwd *
|
||||||
|
getspnam(const char *name) {
|
||||||
|
struct spwd *sp;
|
||||||
|
|
||||||
|
DEBUG("sshd_test_pw getspnam(%s)\n", name);
|
||||||
|
|
||||||
|
if (real_getspnam == NULL)
|
||||||
|
init();
|
||||||
|
if ((sp = real_getspnam(name)) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (is_test_user(name))
|
||||||
|
sp->sp_pwdp = strdup(test_passwd_hash);
|
||||||
|
|
||||||
|
return sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getspnam_r */
|
||||||
|
|
||||||
|
int
|
||||||
|
getspnam_r(const char *name,
|
||||||
|
struct spwd *spbuf,
|
||||||
|
char *buf,
|
||||||
|
size_t buflen,
|
||||||
|
struct spwd **spbufp) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
DEBUG("sshd_test_pw getspnam_r(%s)\n", name);
|
||||||
|
|
||||||
|
if (real_getspnam_r == NULL)
|
||||||
|
init();
|
||||||
|
if ((r = real_getspnam_r(name, spbuf, buf, buflen, spbufp)) != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (is_test_user(name))
|
||||||
|
spbuf->sp_pwdp = strdup(test_passwd_hash);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
133
vendor/golang.org/x/net/publicsuffix/list.go
generated
vendored
133
vendor/golang.org/x/net/publicsuffix/list.go
generated
vendored
|
@ -1,133 +0,0 @@
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package publicsuffix provides a public suffix list based on data from
|
|
||||||
// http://publicsuffix.org/. A public suffix is one under which Internet users
|
|
||||||
// can directly register names.
|
|
||||||
package publicsuffix // import "golang.org/x/net/publicsuffix"
|
|
||||||
|
|
||||||
// TODO: specify case sensitivity and leading/trailing dot behavior for
|
|
||||||
// func PublicSuffix and func EffectiveTLDPlusOne.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http/cookiejar"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List implements the cookiejar.PublicSuffixList interface by calling the
|
|
||||||
// PublicSuffix function.
|
|
||||||
var List cookiejar.PublicSuffixList = list{}
|
|
||||||
|
|
||||||
type list struct{}
|
|
||||||
|
|
||||||
func (list) PublicSuffix(domain string) string {
|
|
||||||
ps, _ := PublicSuffix(domain)
|
|
||||||
return ps
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list) String() string {
|
|
||||||
return version
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicSuffix returns the public suffix of the domain using a copy of the
|
|
||||||
// publicsuffix.org database compiled into the library.
|
|
||||||
//
|
|
||||||
// icann is whether the public suffix is managed by the Internet Corporation
|
|
||||||
// for Assigned Names and Numbers. If not, the public suffix is privately
|
|
||||||
// managed. For example, foo.org and foo.co.uk are ICANN domains,
|
|
||||||
// foo.dyndns.org and foo.blogspot.co.uk are private domains.
|
|
||||||
//
|
|
||||||
// Use cases for distinguishing ICANN domains like foo.com from private
|
|
||||||
// domains like foo.appspot.com can be found at
|
|
||||||
// https://wiki.mozilla.org/Public_Suffix_List/Use_Cases
|
|
||||||
func PublicSuffix(domain string) (publicSuffix string, icann bool) {
|
|
||||||
lo, hi := uint32(0), uint32(numTLD)
|
|
||||||
s, suffix, wildcard := domain, len(domain), false
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
dot := strings.LastIndex(s, ".")
|
|
||||||
if wildcard {
|
|
||||||
suffix = 1 + dot
|
|
||||||
}
|
|
||||||
if lo == hi {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
f := find(s[1+dot:], lo, hi)
|
|
||||||
if f == notFound {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
u := nodes[f] >> (nodesBitsTextOffset + nodesBitsTextLength)
|
|
||||||
icann = u&(1<<nodesBitsICANN-1) != 0
|
|
||||||
u >>= nodesBitsICANN
|
|
||||||
u = children[u&(1<<nodesBitsChildren-1)]
|
|
||||||
lo = u & (1<<childrenBitsLo - 1)
|
|
||||||
u >>= childrenBitsLo
|
|
||||||
hi = u & (1<<childrenBitsHi - 1)
|
|
||||||
u >>= childrenBitsHi
|
|
||||||
switch u & (1<<childrenBitsNodeType - 1) {
|
|
||||||
case nodeTypeNormal:
|
|
||||||
suffix = 1 + dot
|
|
||||||
case nodeTypeException:
|
|
||||||
suffix = 1 + len(s)
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
u >>= childrenBitsNodeType
|
|
||||||
wildcard = u&(1<<childrenBitsWildcard-1) != 0
|
|
||||||
|
|
||||||
if dot == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
s = s[:dot]
|
|
||||||
}
|
|
||||||
if suffix == len(domain) {
|
|
||||||
// If no rules match, the prevailing rule is "*".
|
|
||||||
return domain[1+strings.LastIndex(domain, "."):], icann
|
|
||||||
}
|
|
||||||
return domain[suffix:], icann
|
|
||||||
}
|
|
||||||
|
|
||||||
const notFound uint32 = 1<<32 - 1
|
|
||||||
|
|
||||||
// find returns the index of the node in the range [lo, hi) whose label equals
|
|
||||||
// label, or notFound if there is no such node. The range is assumed to be in
|
|
||||||
// strictly increasing node label order.
|
|
||||||
func find(label string, lo, hi uint32) uint32 {
|
|
||||||
for lo < hi {
|
|
||||||
mid := lo + (hi-lo)/2
|
|
||||||
s := nodeLabel(mid)
|
|
||||||
if s < label {
|
|
||||||
lo = mid + 1
|
|
||||||
} else if s == label {
|
|
||||||
return mid
|
|
||||||
} else {
|
|
||||||
hi = mid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return notFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// nodeLabel returns the label for the i'th node.
|
|
||||||
func nodeLabel(i uint32) string {
|
|
||||||
x := nodes[i]
|
|
||||||
length := x & (1<<nodesBitsTextLength - 1)
|
|
||||||
x >>= nodesBitsTextLength
|
|
||||||
offset := x & (1<<nodesBitsTextOffset - 1)
|
|
||||||
return text[offset : offset+length]
|
|
||||||
}
|
|
||||||
|
|
||||||
// EffectiveTLDPlusOne returns the effective top level domain plus one more
|
|
||||||
// label. For example, the eTLD+1 for "foo.bar.golang.org" is "golang.org".
|
|
||||||
func EffectiveTLDPlusOne(domain string) (string, error) {
|
|
||||||
suffix, _ := PublicSuffix(domain)
|
|
||||||
if len(domain) <= len(suffix) {
|
|
||||||
return "", fmt.Errorf("publicsuffix: cannot derive eTLD+1 for domain %q", domain)
|
|
||||||
}
|
|
||||||
i := len(domain) - len(suffix) - 1
|
|
||||||
if domain[i] != '.' {
|
|
||||||
return "", fmt.Errorf("publicsuffix: invalid public suffix %q for domain %q", suffix, domain)
|
|
||||||
}
|
|
||||||
return domain[1+strings.LastIndex(domain[:i], "."):], nil
|
|
||||||
}
|
|
8786
vendor/golang.org/x/net/publicsuffix/table.go
generated
vendored
8786
vendor/golang.org/x/net/publicsuffix/table.go
generated
vendored
File diff suppressed because it is too large
Load diff
27
vendor/golang.org/x/time/LICENSE
generated
vendored
27
vendor/golang.org/x/time/LICENSE
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
22
vendor/golang.org/x/time/PATENTS
generated
vendored
22
vendor/golang.org/x/time/PATENTS
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
Google as part of the Go project.
|
|
||||||
|
|
||||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation of Go, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by Google and acquired in
|
|
||||||
the future, licensable by Google that are necessarily infringed by this
|
|
||||||
implementation of Go. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation of Go or any code incorporated within this
|
|
||||||
implementation of Go constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation of Go
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
1
vendor/golang.org/x/time/README
generated
vendored
1
vendor/golang.org/x/time/README
generated
vendored
|
@ -1 +0,0 @@
|
||||||
This repository provides supplementary Go time packages.
|
|
368
vendor/golang.org/x/time/rate/rate.go
generated
vendored
368
vendor/golang.org/x/time/rate/rate.go
generated
vendored
|
@ -1,368 +0,0 @@
|
||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package rate provides a rate limiter.
|
|
||||||
package rate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Limit defines the maximum frequency of some events.
|
|
||||||
// Limit is represented as number of events per second.
|
|
||||||
// A zero Limit allows no events.
|
|
||||||
type Limit float64
|
|
||||||
|
|
||||||
// Inf is the infinite rate limit; it allows all events (even if burst is zero).
|
|
||||||
const Inf = Limit(math.MaxFloat64)
|
|
||||||
|
|
||||||
// Every converts a minimum time interval between events to a Limit.
|
|
||||||
func Every(interval time.Duration) Limit {
|
|
||||||
if interval <= 0 {
|
|
||||||
return Inf
|
|
||||||
}
|
|
||||||
return 1 / Limit(interval.Seconds())
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Limiter controls how frequently events are allowed to happen.
|
|
||||||
// It implements a "token bucket" of size b, initially full and refilled
|
|
||||||
// at rate r tokens per second.
|
|
||||||
// Informally, in any large enough time interval, the Limiter limits the
|
|
||||||
// rate to r tokens per second, with a maximum burst size of b events.
|
|
||||||
// As a special case, if r == Inf (the infinite rate), b is ignored.
|
|
||||||
// See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets.
|
|
||||||
//
|
|
||||||
// The zero value is a valid Limiter, but it will reject all events.
|
|
||||||
// Use NewLimiter to create non-zero Limiters.
|
|
||||||
//
|
|
||||||
// Limiter has three main methods, Allow, Reserve, and Wait.
|
|
||||||
// Most callers should use Wait.
|
|
||||||
//
|
|
||||||
// Each of the three methods consumes a single token.
|
|
||||||
// They differ in their behavior when no token is available.
|
|
||||||
// If no token is available, Allow returns false.
|
|
||||||
// If no token is available, Reserve returns a reservation for a future token
|
|
||||||
// and the amount of time the caller must wait before using it.
|
|
||||||
// If no token is available, Wait blocks until one can be obtained
|
|
||||||
// or its associated context.Context is canceled.
|
|
||||||
//
|
|
||||||
// The methods AllowN, ReserveN, and WaitN consume n tokens.
|
|
||||||
type Limiter struct {
|
|
||||||
limit Limit
|
|
||||||
burst int
|
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
tokens float64
|
|
||||||
// last is the last time the limiter's tokens field was updated
|
|
||||||
last time.Time
|
|
||||||
// lastEvent is the latest time of a rate-limited event (past or future)
|
|
||||||
lastEvent time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit returns the maximum overall event rate.
|
|
||||||
func (lim *Limiter) Limit() Limit {
|
|
||||||
lim.mu.Lock()
|
|
||||||
defer lim.mu.Unlock()
|
|
||||||
return lim.limit
|
|
||||||
}
|
|
||||||
|
|
||||||
// Burst returns the maximum burst size. Burst is the maximum number of tokens
|
|
||||||
// that can be consumed in a single call to Allow, Reserve, or Wait, so higher
|
|
||||||
// Burst values allow more events to happen at once.
|
|
||||||
// A zero Burst allows no events, unless limit == Inf.
|
|
||||||
func (lim *Limiter) Burst() int {
|
|
||||||
return lim.burst
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLimiter returns a new Limiter that allows events up to rate r and permits
|
|
||||||
// bursts of at most b tokens.
|
|
||||||
func NewLimiter(r Limit, b int) *Limiter {
|
|
||||||
return &Limiter{
|
|
||||||
limit: r,
|
|
||||||
burst: b,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow is shorthand for AllowN(time.Now(), 1).
|
|
||||||
func (lim *Limiter) Allow() bool {
|
|
||||||
return lim.AllowN(time.Now(), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllowN reports whether n events may happen at time now.
|
|
||||||
// Use this method if you intend to drop / skip events that exceed the rate limit.
|
|
||||||
// Otherwise use Reserve or Wait.
|
|
||||||
func (lim *Limiter) AllowN(now time.Time, n int) bool {
|
|
||||||
return lim.reserveN(now, n, 0).ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Reservation holds information about events that are permitted by a Limiter to happen after a delay.
|
|
||||||
// A Reservation may be canceled, which may enable the Limiter to permit additional events.
|
|
||||||
type Reservation struct {
|
|
||||||
ok bool
|
|
||||||
lim *Limiter
|
|
||||||
tokens int
|
|
||||||
timeToAct time.Time
|
|
||||||
// This is the Limit at reservation time, it can change later.
|
|
||||||
limit Limit
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK returns whether the limiter can provide the requested number of tokens
|
|
||||||
// within the maximum wait time. If OK is false, Delay returns InfDuration, and
|
|
||||||
// Cancel does nothing.
|
|
||||||
func (r *Reservation) OK() bool {
|
|
||||||
return r.ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay is shorthand for DelayFrom(time.Now()).
|
|
||||||
func (r *Reservation) Delay() time.Duration {
|
|
||||||
return r.DelayFrom(time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
// InfDuration is the duration returned by Delay when a Reservation is not OK.
|
|
||||||
const InfDuration = time.Duration(1<<63 - 1)
|
|
||||||
|
|
||||||
// DelayFrom returns the duration for which the reservation holder must wait
|
|
||||||
// before taking the reserved action. Zero duration means act immediately.
|
|
||||||
// InfDuration means the limiter cannot grant the tokens requested in this
|
|
||||||
// Reservation within the maximum wait time.
|
|
||||||
func (r *Reservation) DelayFrom(now time.Time) time.Duration {
|
|
||||||
if !r.ok {
|
|
||||||
return InfDuration
|
|
||||||
}
|
|
||||||
delay := r.timeToAct.Sub(now)
|
|
||||||
if delay < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return delay
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancel is shorthand for CancelAt(time.Now()).
|
|
||||||
func (r *Reservation) Cancel() {
|
|
||||||
r.CancelAt(time.Now())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CancelAt indicates that the reservation holder will not perform the reserved action
|
|
||||||
// and reverses the effects of this Reservation on the rate limit as much as possible,
|
|
||||||
// considering that other reservations may have already been made.
|
|
||||||
func (r *Reservation) CancelAt(now time.Time) {
|
|
||||||
if !r.ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r.lim.mu.Lock()
|
|
||||||
defer r.lim.mu.Unlock()
|
|
||||||
|
|
||||||
if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate tokens to restore
|
|
||||||
// The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved
|
|
||||||
// after r was obtained. These tokens should not be restored.
|
|
||||||
restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct))
|
|
||||||
if restoreTokens <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// advance time to now
|
|
||||||
now, _, tokens := r.lim.advance(now)
|
|
||||||
// calculate new number of tokens
|
|
||||||
tokens += restoreTokens
|
|
||||||
if burst := float64(r.lim.burst); tokens > burst {
|
|
||||||
tokens = burst
|
|
||||||
}
|
|
||||||
// update state
|
|
||||||
r.lim.last = now
|
|
||||||
r.lim.tokens = tokens
|
|
||||||
if r.timeToAct == r.lim.lastEvent {
|
|
||||||
prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens)))
|
|
||||||
if !prevEvent.Before(now) {
|
|
||||||
r.lim.lastEvent = prevEvent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reserve is shorthand for ReserveN(time.Now(), 1).
|
|
||||||
func (lim *Limiter) Reserve() *Reservation {
|
|
||||||
return lim.ReserveN(time.Now(), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
|
|
||||||
// The Limiter takes this Reservation into account when allowing future events.
|
|
||||||
// ReserveN returns false if n exceeds the Limiter's burst size.
|
|
||||||
// Usage example:
|
|
||||||
// r, ok := lim.ReserveN(time.Now(), 1)
|
|
||||||
// if !ok {
|
|
||||||
// // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
|
|
||||||
// }
|
|
||||||
// time.Sleep(r.Delay())
|
|
||||||
// Act()
|
|
||||||
// Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events.
|
|
||||||
// If you need to respect a deadline or cancel the delay, use Wait instead.
|
|
||||||
// To drop or skip events exceeding rate limit, use Allow instead.
|
|
||||||
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation {
|
|
||||||
r := lim.reserveN(now, n, InfDuration)
|
|
||||||
return &r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait is shorthand for WaitN(ctx, 1).
|
|
||||||
func (lim *Limiter) Wait(ctx context.Context) (err error) {
|
|
||||||
return lim.WaitN(ctx, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WaitN blocks until lim permits n events to happen.
|
|
||||||
// It returns an error if n exceeds the Limiter's burst size, the Context is
|
|
||||||
// canceled, or the expected wait time exceeds the Context's Deadline.
|
|
||||||
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {
|
|
||||||
if n > lim.burst {
|
|
||||||
return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, lim.burst)
|
|
||||||
}
|
|
||||||
// Check if ctx is already cancelled
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
// Determine wait limit
|
|
||||||
now := time.Now()
|
|
||||||
waitLimit := InfDuration
|
|
||||||
if deadline, ok := ctx.Deadline(); ok {
|
|
||||||
waitLimit = deadline.Sub(now)
|
|
||||||
}
|
|
||||||
// Reserve
|
|
||||||
r := lim.reserveN(now, n, waitLimit)
|
|
||||||
if !r.ok {
|
|
||||||
return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n)
|
|
||||||
}
|
|
||||||
// Wait
|
|
||||||
t := time.NewTimer(r.DelayFrom(now))
|
|
||||||
defer t.Stop()
|
|
||||||
select {
|
|
||||||
case <-t.C:
|
|
||||||
// We can proceed.
|
|
||||||
return nil
|
|
||||||
case <-ctx.Done():
|
|
||||||
// Context was canceled before we could proceed. Cancel the
|
|
||||||
// reservation, which may permit other events to proceed sooner.
|
|
||||||
r.Cancel()
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLimit is shorthand for SetLimitAt(time.Now(), newLimit).
|
|
||||||
func (lim *Limiter) SetLimit(newLimit Limit) {
|
|
||||||
lim.SetLimitAt(time.Now(), newLimit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated
|
|
||||||
// or underutilized by those which reserved (using Reserve or Wait) but did not yet act
|
|
||||||
// before SetLimitAt was called.
|
|
||||||
func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) {
|
|
||||||
lim.mu.Lock()
|
|
||||||
defer lim.mu.Unlock()
|
|
||||||
|
|
||||||
now, _, tokens := lim.advance(now)
|
|
||||||
|
|
||||||
lim.last = now
|
|
||||||
lim.tokens = tokens
|
|
||||||
lim.limit = newLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
// reserveN is a helper method for AllowN, ReserveN, and WaitN.
|
|
||||||
// maxFutureReserve specifies the maximum reservation wait duration allowed.
|
|
||||||
// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN.
|
|
||||||
func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation {
|
|
||||||
lim.mu.Lock()
|
|
||||||
defer lim.mu.Unlock()
|
|
||||||
|
|
||||||
if lim.limit == Inf {
|
|
||||||
return Reservation{
|
|
||||||
ok: true,
|
|
||||||
lim: lim,
|
|
||||||
tokens: n,
|
|
||||||
timeToAct: now,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
now, last, tokens := lim.advance(now)
|
|
||||||
|
|
||||||
// Calculate the remaining number of tokens resulting from the request.
|
|
||||||
tokens -= float64(n)
|
|
||||||
|
|
||||||
// Calculate the wait duration
|
|
||||||
var waitDuration time.Duration
|
|
||||||
if tokens < 0 {
|
|
||||||
waitDuration = lim.limit.durationFromTokens(-tokens)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decide result
|
|
||||||
ok := n <= lim.burst && waitDuration <= maxFutureReserve
|
|
||||||
|
|
||||||
// Prepare reservation
|
|
||||||
r := Reservation{
|
|
||||||
ok: ok,
|
|
||||||
lim: lim,
|
|
||||||
limit: lim.limit,
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
r.tokens = n
|
|
||||||
r.timeToAct = now.Add(waitDuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update state
|
|
||||||
if ok {
|
|
||||||
lim.last = now
|
|
||||||
lim.tokens = tokens
|
|
||||||
lim.lastEvent = r.timeToAct
|
|
||||||
} else {
|
|
||||||
lim.last = last
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// advance calculates and returns an updated state for lim resulting from the passage of time.
|
|
||||||
// lim is not changed.
|
|
||||||
func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) {
|
|
||||||
last := lim.last
|
|
||||||
if now.Before(last) {
|
|
||||||
last = now
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid making delta overflow below when last is very old.
|
|
||||||
maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens)
|
|
||||||
elapsed := now.Sub(last)
|
|
||||||
if elapsed > maxElapsed {
|
|
||||||
elapsed = maxElapsed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the new number of tokens, due to time that passed.
|
|
||||||
delta := lim.limit.tokensFromDuration(elapsed)
|
|
||||||
tokens := lim.tokens + delta
|
|
||||||
if burst := float64(lim.burst); tokens > burst {
|
|
||||||
tokens = burst
|
|
||||||
}
|
|
||||||
|
|
||||||
return now, last, tokens
|
|
||||||
}
|
|
||||||
|
|
||||||
// durationFromTokens is a unit conversion function from the number of tokens to the duration
|
|
||||||
// of time it takes to accumulate them at a rate of limit tokens per second.
|
|
||||||
func (limit Limit) durationFromTokens(tokens float64) time.Duration {
|
|
||||||
seconds := tokens / float64(limit)
|
|
||||||
return time.Nanosecond * time.Duration(1e9*seconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tokensFromDuration is a unit conversion function from a time duration to the number of tokens
|
|
||||||
// which could be accumulated during that duration at a rate of limit tokens per second.
|
|
||||||
func (limit Limit) tokensFromDuration(d time.Duration) float64 {
|
|
||||||
return d.Seconds() * float64(limit)
|
|
||||||
}
|
|
202
vendor/gopkg.in/square/go-jose.v1/LICENSE
generated
vendored
202
vendor/gopkg.in/square/go-jose.v1/LICENSE
generated
vendored
|
@ -1,202 +0,0 @@
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
209
vendor/gopkg.in/square/go-jose.v1/README.md
generated
vendored
209
vendor/gopkg.in/square/go-jose.v1/README.md
generated
vendored
|
@ -1,209 +0,0 @@
|
||||||
# Go JOSE
|
|
||||||
|
|
||||||
[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v1) [![license](http://img.shields.io/badge/license-apache_2.0-red.svg?style=flat)](https://raw.githubusercontent.com/square/go-jose/master/LICENSE) [![build](https://travis-ci.org/square/go-jose.svg?branch=master)](https://travis-ci.org/square/go-jose) [![coverage](https://coveralls.io/repos/github/square/go-jose/badge.svg?branch=master)](https://coveralls.io/r/square/go-jose)
|
|
||||||
|
|
||||||
Package jose aims to provide an implementation of the Javascript Object Signing
|
|
||||||
and Encryption set of standards. For the moment, it mainly focuses on encryption
|
|
||||||
and signing based on the JSON Web Encryption and JSON Web Signature standards.
|
|
||||||
|
|
||||||
**Disclaimer**: This library contains encryption software that is subject to
|
|
||||||
the U.S. Export Administration Regulations. You may not export, re-export,
|
|
||||||
transfer or download this code or any part of it in violation of any United
|
|
||||||
States law, directive or regulation. In particular this software may not be
|
|
||||||
exported or re-exported in any form or on any media to Iran, North Sudan,
|
|
||||||
Syria, Cuba, or North Korea, or to denied persons or entities mentioned on any
|
|
||||||
US maintained blocked list.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The implementation follows the
|
|
||||||
[JSON Web Encryption](http://dx.doi.org/10.17487/RFC7516)
|
|
||||||
standard (RFC 7516) and
|
|
||||||
[JSON Web Signature](http://dx.doi.org/10.17487/RFC7515)
|
|
||||||
standard (RFC 7515). Tables of supported algorithms are shown below.
|
|
||||||
The library supports both the compact and full serialization formats, and has
|
|
||||||
optional support for multiple recipients. It also comes with a small
|
|
||||||
command-line utility
|
|
||||||
([`jose-util`](https://github.com/square/go-jose/tree/master/jose-util))
|
|
||||||
for dealing with JOSE messages in a shell.
|
|
||||||
|
|
||||||
**Note**: We use a forked version of the `encoding/json` package from the Go
|
|
||||||
standard library which uses case-sensitive matching for member names (instead
|
|
||||||
of [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/current/msg03763.html)).
|
|
||||||
This is to avoid differences in interpretation of messages between go-jose and
|
|
||||||
libraries in other languages. If you do not like this behavior, you can use the
|
|
||||||
`std_json` build tag to disable it (though we do not recommend doing so).
|
|
||||||
|
|
||||||
### Versions
|
|
||||||
|
|
||||||
We use [gopkg.in](https://gopkg.in) for versioning.
|
|
||||||
|
|
||||||
[Version 1](https://gopkg.in/square/go-jose.v1) is the current stable version:
|
|
||||||
|
|
||||||
import "gopkg.in/square/go-jose.v1"
|
|
||||||
|
|
||||||
The interface for [go-jose.v1](https://gopkg.in/square/go-jose.v1) will remain
|
|
||||||
backwards compatible. We're currently sketching out ideas for a new version, to
|
|
||||||
clean up the interface a bit. If you have ideas or feature requests [please let
|
|
||||||
us know](https://github.com/square/go-jose/issues/64)!
|
|
||||||
|
|
||||||
### Supported algorithms
|
|
||||||
|
|
||||||
See below for a table of supported algorithms. Algorithm identifiers match
|
|
||||||
the names in the
|
|
||||||
[JSON Web Algorithms](http://dx.doi.org/10.17487/RFC7518)
|
|
||||||
standard where possible. The
|
|
||||||
[Godoc reference](https://godoc.org/github.com/square/go-jose#pkg-constants)
|
|
||||||
has a list of constants.
|
|
||||||
|
|
||||||
Key encryption | Algorithm identifier(s)
|
|
||||||
:------------------------- | :------------------------------
|
|
||||||
RSA-PKCS#1v1.5 | RSA1_5
|
|
||||||
RSA-OAEP | RSA-OAEP, RSA-OAEP-256
|
|
||||||
AES key wrap | A128KW, A192KW, A256KW
|
|
||||||
AES-GCM key wrap | A128GCMKW, A192GCMKW, A256GCMKW
|
|
||||||
ECDH-ES + AES key wrap | ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW
|
|
||||||
ECDH-ES (direct) | ECDH-ES<sup>1</sup>
|
|
||||||
Direct encryption | dir<sup>1</sup>
|
|
||||||
|
|
||||||
<sup>1. Not supported in multi-recipient mode</sup>
|
|
||||||
|
|
||||||
Signing / MAC | Algorithm identifier(s)
|
|
||||||
:------------------------- | :------------------------------
|
|
||||||
RSASSA-PKCS#1v1.5 | RS256, RS384, RS512
|
|
||||||
RSASSA-PSS | PS256, PS384, PS512
|
|
||||||
HMAC | HS256, HS384, HS512
|
|
||||||
ECDSA | ES256, ES384, ES512
|
|
||||||
|
|
||||||
Content encryption | Algorithm identifier(s)
|
|
||||||
:------------------------- | :------------------------------
|
|
||||||
AES-CBC+HMAC | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512
|
|
||||||
AES-GCM | A128GCM, A192GCM, A256GCM
|
|
||||||
|
|
||||||
Compression | Algorithm identifiers(s)
|
|
||||||
:------------------------- | -------------------------------
|
|
||||||
DEFLATE (RFC 1951) | DEF
|
|
||||||
|
|
||||||
### Supported key types
|
|
||||||
|
|
||||||
See below for a table of supported key types. These are understood by the
|
|
||||||
library, and can be passed to corresponding functions such as `NewEncrypter` or
|
|
||||||
`NewSigner`. Note that if you are creating a new encrypter or signer with a
|
|
||||||
JsonWebKey, the key id of the JsonWebKey (if present) will be added to any
|
|
||||||
resulting messages.
|
|
||||||
|
|
||||||
Algorithm(s) | Corresponding types
|
|
||||||
:------------------------- | -------------------------------
|
|
||||||
RSA | *[rsa.PublicKey](http://golang.org/pkg/crypto/rsa/#PublicKey), *[rsa.PrivateKey](http://golang.org/pkg/crypto/rsa/#PrivateKey), *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey)
|
|
||||||
ECDH, ECDSA | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey), *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey)
|
|
||||||
AES, HMAC | []byte, *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey)
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
Encryption/decryption example using RSA:
|
|
||||||
|
|
||||||
```Go
|
|
||||||
// Generate a public/private key pair to use for this example. The library
|
|
||||||
// also provides two utility functions (LoadPublicKey and LoadPrivateKey)
|
|
||||||
// that can be used to load keys from PEM/DER-encoded data.
|
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instantiate an encrypter using RSA-OAEP with AES128-GCM. An error would
|
|
||||||
// indicate that the selected algorithm(s) are not currently supported.
|
|
||||||
publicKey := &privateKey.PublicKey
|
|
||||||
encrypter, err := NewEncrypter(RSA_OAEP, A128GCM, publicKey)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt a sample plaintext. Calling the encrypter returns an encrypted
|
|
||||||
// JWE object, which can then be serialized for output afterwards. An error
|
|
||||||
// would indicate a problem in an underlying cryptographic primitive.
|
|
||||||
var plaintext = []byte("Lorem ipsum dolor sit amet")
|
|
||||||
object, err := encrypter.Encrypt(plaintext)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the encrypted object using the full serialization format.
|
|
||||||
// Alternatively you can also use the compact format here by calling
|
|
||||||
// object.CompactSerialize() instead.
|
|
||||||
serialized := object.FullSerialize()
|
|
||||||
|
|
||||||
// Parse the serialized, encrypted JWE object. An error would indicate that
|
|
||||||
// the given input did not represent a valid message.
|
|
||||||
object, err = ParseEncrypted(serialized)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can decrypt and get back our original plaintext. An error here
|
|
||||||
// would indicate the the message failed to decrypt, e.g. because the auth
|
|
||||||
// tag was broken or the message was tampered with.
|
|
||||||
decrypted, err := object.Decrypt(privateKey)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf(string(decrypted))
|
|
||||||
// output: Lorem ipsum dolor sit amet
|
|
||||||
```
|
|
||||||
|
|
||||||
Signing/verification example using RSA:
|
|
||||||
|
|
||||||
```Go
|
|
||||||
// Generate a public/private key pair to use for this example. The library
|
|
||||||
// also provides two utility functions (LoadPublicKey and LoadPrivateKey)
|
|
||||||
// that can be used to load keys from PEM/DER-encoded data.
|
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instantiate a signer using RSASSA-PSS (SHA512) with the given private key.
|
|
||||||
signer, err := NewSigner(PS512, privateKey)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign a sample payload. Calling the signer returns a protected JWS object,
|
|
||||||
// which can then be serialized for output afterwards. An error would
|
|
||||||
// indicate a problem in an underlying cryptographic primitive.
|
|
||||||
var payload = []byte("Lorem ipsum dolor sit amet")
|
|
||||||
object, err := signer.Sign(payload)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the encrypted object using the full serialization format.
|
|
||||||
// Alternatively you can also use the compact format here by calling
|
|
||||||
// object.CompactSerialize() instead.
|
|
||||||
serialized := object.FullSerialize()
|
|
||||||
|
|
||||||
// Parse the serialized, protected JWS object. An error would indicate that
|
|
||||||
// the given input did not represent a valid message.
|
|
||||||
object, err = ParseSigned(serialized)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can verify the signature on the payload. An error here would
|
|
||||||
// indicate the the message failed to verify, e.g. because the signature was
|
|
||||||
// broken or the message was tampered with.
|
|
||||||
output, err := object.Verify(&privateKey.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf(string(output))
|
|
||||||
// output: Lorem ipsum dolor sit amet
|
|
||||||
```
|
|
||||||
|
|
||||||
More examples can be found in the [Godoc
|
|
||||||
reference](https://godoc.org/github.com/square/go-jose) for this package. The
|
|
||||||
[`jose-util`](https://github.com/square/go-jose/tree/master/jose-util)
|
|
||||||
subdirectory also contains a small command-line utility which might
|
|
||||||
be useful as an example.
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue