From 9b2423aabacf8ce3c306ea9df936eec8e7cde2c2 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 7 Jan 2019 18:30:06 +0100 Subject: [PATCH] Update Lego --- Gopkg.lock | 53 +- Gopkg.toml | 2 +- acme/account.go | 9 +- acme/acme.go | 95 +- acme/acme_test.go | 51 +- acme/challenge_http_provider.go | 4 +- acme/challenge_tls_provider.go | 7 +- config/static/static_config.go | 6 +- docs/configuration/acme.md | 8 + old/configuration/configuration.go | 6 +- provider/acme/account.go | 29 +- provider/acme/challenge_http.go | 7 +- provider/acme/challenge_tls.go | 7 +- provider/acme/provider.go | 189 ++- provider/acme/provider_test.go | 16 +- server/server_entrypoint.go | 4 +- vendor/github.com/dimchansky/utfbom/utfbom.go | 18 + .../dnsimple-go/dnsimple/authentication.go | 90 +- .../dnsimple/dnsimple-go/dnsimple/dnsimple.go | 25 +- .../dnsimple/dnsimple-go/dnsimple/oauth.go | 2 +- .../dnsimple/dnsimple-go/dnsimple/webhooks.go | 2 +- .../dnsimple/zone_distributions.go | 46 + .../dnsimple/dnsimple-go/dnsimple/zones.go | 4 +- .../dnsimple-go/dnsimple/zones_records.go | 4 +- vendor/github.com/miekg/dns/acceptfunc.go | 54 + vendor/github.com/miekg/dns/client.go | 50 +- .../github.com/miekg/dns/compress_generate.go | 188 --- vendor/github.com/miekg/dns/defaults.go | 2 +- vendor/github.com/miekg/dns/dns.go | 38 +- vendor/github.com/miekg/dns/dnssec.go | 53 +- vendor/github.com/miekg/dns/dnssec_keyscan.go | 185 ++- vendor/github.com/miekg/dns/dnssec_privkey.go | 2 +- vendor/github.com/miekg/dns/doc.go | 111 +- vendor/github.com/miekg/dns/duplicate.go | 25 + .../miekg/dns/duplicate_generate.go | 158 +++ vendor/github.com/miekg/dns/edns.go | 34 +- vendor/github.com/miekg/dns/generate.go | 309 +++-- vendor/github.com/miekg/dns/labels.go | 4 +- vendor/github.com/miekg/dns/listen_go111.go | 44 + .../github.com/miekg/dns/listen_go_not111.go | 23 + vendor/github.com/miekg/dns/msg.go | 651 ++++++---- vendor/github.com/miekg/dns/msg_generate.go | 21 +- vendor/github.com/miekg/dns/msg_helpers.go | 72 +- vendor/github.com/miekg/dns/nsecx.go | 47 +- vendor/github.com/miekg/dns/privaterr.go | 30 +- vendor/github.com/miekg/dns/rawmsg.go | 49 - vendor/github.com/miekg/dns/reverse.go | 5 + vendor/github.com/miekg/dns/sanitize.go | 1 + vendor/github.com/miekg/dns/scan.go | 1020 ++++++++++----- vendor/github.com/miekg/dns/scan_rr.go | 754 +++++------ vendor/github.com/miekg/dns/scanner.go | 56 - vendor/github.com/miekg/dns/serve_mux.go | 147 +++ vendor/github.com/miekg/dns/server.go | 705 ++++++---- vendor/github.com/miekg/dns/sig0.go | 7 +- vendor/github.com/miekg/dns/tsig.go | 2 +- vendor/github.com/miekg/dns/types.go | 221 ++-- vendor/github.com/miekg/dns/types_generate.go | 18 +- vendor/github.com/miekg/dns/udp.go | 33 +- vendor/github.com/miekg/dns/version.go | 2 +- vendor/github.com/miekg/dns/zcompress.go | 118 -- vendor/github.com/miekg/dns/zduplicate.go | 943 ++++++++++++++ vendor/github.com/miekg/dns/zmsg.go | 1156 ++++++++--------- vendor/github.com/miekg/dns/ztypes.go | 436 +++---- .../xenolf/lego/acme/api/account.go | 69 + vendor/github.com/xenolf/lego/acme/api/api.go | 151 +++ .../xenolf/lego/acme/api/authorization.go | 34 + .../xenolf/lego/acme/api/certificate.go | 99 ++ .../xenolf/lego/acme/api/challenge.go | 45 + .../acme/api/internal/nonces/nonce_manager.go | 78 ++ .../lego/acme/api/internal/secure/jws.go | 134 ++ .../lego/acme/api/internal/sender/sender.go | 146 +++ .../acme/api/internal/sender/useragent.go | 14 + .../github.com/xenolf/lego/acme/api/order.go | 65 + .../xenolf/lego/acme/api/service.go | 45 + .../github.com/xenolf/lego/acme/challenges.go | 17 - vendor/github.com/xenolf/lego/acme/client.go | 957 -------------- vendor/github.com/xenolf/lego/acme/commons.go | 284 ++++ vendor/github.com/xenolf/lego/acme/crypto.go | 334 ----- .../xenolf/lego/acme/dns_challenge.go | 343 ----- .../xenolf/lego/acme/dns_challenge_manual.go | 55 - vendor/github.com/xenolf/lego/acme/error.go | 91 -- vendor/github.com/xenolf/lego/acme/errors.go | 58 + vendor/github.com/xenolf/lego/acme/http.go | 212 --- .../xenolf/lego/acme/http_challenge.go | 42 - vendor/github.com/xenolf/lego/acme/jws.go | 167 --- .../github.com/xenolf/lego/acme/messages.go | 103 -- .../xenolf/lego/acme/tls_alpn_challenge.go | 104 -- .../xenolf/lego/certcrypto/crypto.go | 252 ++++ .../xenolf/lego/certificate/authorization.go | 69 + .../xenolf/lego/certificate/certificates.go | 493 +++++++ .../xenolf/lego/certificate/errors.go | 30 + .../xenolf/lego/challenge/challenges.go | 44 + .../lego/challenge/dns01/dns_challenge.go | 176 +++ .../challenge/dns01/dns_challenge_manual.go | 52 + .../xenolf/lego/challenge/dns01/fqdn.go | 19 + .../xenolf/lego/challenge/dns01/nameserver.go | 232 ++++ .../xenolf/lego/challenge/dns01/precheck.go | 114 ++ .../lego/challenge/http01/http_challenge.go | 65 + .../http01}/http_challenge_server.go | 38 +- .../lego/{acme => challenge}/provider.go | 20 +- .../xenolf/lego/challenge/resolver/errors.go | 25 + .../xenolf/lego/challenge/resolver/prober.go | 173 +++ .../lego/challenge/resolver/solver_manager.go | 154 +++ .../challenge/tlsalpn01/tls_alpn_challenge.go | 129 ++ .../tlsalpn01}/tls_alpn_challenge_server.go | 51 +- vendor/github.com/xenolf/lego/lego/client.go | 73 ++ .../xenolf/lego/lego/client_config.go | 96 ++ .../{acme/utils.go => platform/wait/wait.go} | 8 +- .../lego/providers/dns/acmedns/acmedns.go | 63 +- .../lego/providers/dns/alidns/alidns.go | 55 +- .../lego/providers/dns/auroradns/auroradns.go | 30 +- .../xenolf/lego/providers/dns/azure/azure.go | 67 +- .../lego/providers/dns/bluecat/bluecat.go | 270 +--- .../lego/providers/dns/bluecat/client.go | 233 ++++ .../providers/dns/cloudflare/cloudflare.go | 38 +- .../lego/providers/dns/cloudxns/cloudxns.go | 37 +- .../dns/cloudxns/{ => internal}/client.go | 10 +- .../lego/providers/dns/conoha/conoha.go | 28 +- .../dns/conoha/{ => internal}/client.go | 2 +- .../lego/providers/dns/digitalocean/client.go | 136 +- .../dns/digitalocean/digitalocean.go | 122 +- .../lego/providers/dns/dns_providers.go | 7 +- .../lego/providers/dns/dnsimple/dnsimple.go | 38 +- .../providers/dns/dnsmadeeasy/dnsmadeeasy.go | 35 +- .../dns/dnsmadeeasy/{ => internal}/client.go | 2 +- .../lego/providers/dns/dnspod/dnspod.go | 29 +- .../lego/providers/dns/dreamhost/dreamhost.go | 10 +- .../lego/providers/dns/duckdns/client.go | 68 + .../lego/providers/dns/duckdns/duckdns.go | 89 +- .../xenolf/lego/providers/dns/dyn/client.go | 115 +- .../xenolf/lego/providers/dns/dyn/dyn.go | 139 +- .../xenolf/lego/providers/dns/exec/doc.go | 42 - .../xenolf/lego/providers/dns/exec/exec.go | 53 +- .../lego/providers/dns/exoscale/exoscale.go | 33 +- .../lego/providers/dns/fastdns/fastdns.go | 35 +- .../xenolf/lego/providers/dns/gandi/client.go | 224 ++++ .../xenolf/lego/providers/dns/gandi/gandi.go | 255 +--- .../lego/providers/dns/gandiv5/client.go | 76 ++ .../lego/providers/dns/gandiv5/gandiv5.go | 109 +- .../lego/providers/dns/gcloud/googlecloud.go | 131 +- .../lego/providers/dns/glesys/client.go | 67 + .../lego/providers/dns/glesys/glesys.go | 87 +- .../lego/providers/dns/godaddy/client.go | 53 + .../lego/providers/dns/godaddy/godaddy.go | 92 +- .../lego/providers/dns/hostingde/client.go | 52 + .../lego/providers/dns/hostingde/hostingde.go | 63 +- .../lego/providers/dns/httpreq/httpreq.go | 10 +- .../xenolf/lego/providers/dns/iij/iij.go | 6 +- .../xenolf/lego/providers/dns/inwx/inwx.go | 26 +- .../lego/providers/dns/lightsail/lightsail.go | 13 +- .../lego/providers/dns/linode/linode.go | 25 +- .../lego/providers/dns/linodev4/linodev4.go | 15 +- .../lego/providers/dns/mydnsjp/client.go | 52 + .../lego/providers/dns/mydnsjp/mydnsjp.go | 55 +- .../lego/providers/dns/namecheap/client.go | 10 +- .../lego/providers/dns/namecheap/namecheap.go | 22 +- .../providers/dns/namedotcom/namedotcom.go | 27 +- .../dns/netcup/{ => internal}/client.go | 17 +- .../lego/providers/dns/netcup/netcup.go | 77 +- .../dns/nifcloud/{ => internal}/client.go | 7 +- .../lego/providers/dns/nifcloud/nifcloud.go | 55 +- .../xenolf/lego/providers/dns/ns1/ns1.go | 33 +- .../xenolf/lego/providers/dns/otc/client.go | 211 ++- .../xenolf/lego/providers/dns/otc/otc.go | 216 +-- .../xenolf/lego/providers/dns/ovh/ovh.go | 76 +- .../xenolf/lego/providers/dns/pdns/client.go | 220 ++++ .../xenolf/lego/providers/dns/pdns/pdns.go | 230 +--- .../lego/providers/dns/rackspace/client.go | 134 ++ .../lego/providers/dns/rackspace/rackspace.go | 152 +-- .../lego/providers/dns/rfc2136/rfc2136.go | 43 +- .../lego/providers/dns/route53/route53.go | 16 +- .../providers/dns/sakuracloud/sakuracloud.go | 37 +- .../{vscale => selectel/internal}/client.go | 2 +- .../lego/providers/dns/selectel/selectel.go | 14 +- .../lego/providers/dns/stackpath/client.go | 4 +- .../lego/providers/dns/stackpath/stackpath.go | 12 +- .../lego/providers/dns/transip/transip.go | 18 +- .../lego/providers/dns/vegadns/vegadns.go | 21 +- .../{selectel => vscale/internal}/client.go | 2 +- .../lego/providers/dns/vscale/vscale.go | 17 +- .../xenolf/lego/providers/dns/vultr/vultr.go | 28 +- .../xenolf/lego/registration/registar.go | 146 +++ .../xenolf/lego/registration/user.go | 13 + vendor/golang.org/x/crypto/ed25519/ed25519.go | 60 +- .../internal/edwards25519/edwards25519.go | 22 + vendor/golang.org/x/crypto/ocsp/ocsp.go | 13 +- vendor/golang.org/x/crypto/scrypt/scrypt.go | 4 +- .../golang.org/x/crypto/ssh/terminal/util.go | 8 +- .../x/crypto/ssh/terminal/util_aix.go | 12 + .../x/crypto/ssh/terminal/util_plan9.go | 2 +- .../x/crypto/ssh/terminal/util_solaris.go | 38 +- .../x/crypto/ssh/terminal/util_windows.go | 6 +- 192 files changed, 11105 insertions(+), 8535 deletions(-) create mode 100644 vendor/github.com/dnsimple/dnsimple-go/dnsimple/zone_distributions.go create mode 100644 vendor/github.com/miekg/dns/acceptfunc.go delete mode 100644 vendor/github.com/miekg/dns/compress_generate.go create mode 100644 vendor/github.com/miekg/dns/duplicate.go create mode 100644 vendor/github.com/miekg/dns/duplicate_generate.go create mode 100644 vendor/github.com/miekg/dns/listen_go111.go create mode 100644 vendor/github.com/miekg/dns/listen_go_not111.go delete mode 100644 vendor/github.com/miekg/dns/rawmsg.go delete mode 100644 vendor/github.com/miekg/dns/scanner.go create mode 100644 vendor/github.com/miekg/dns/serve_mux.go delete mode 100644 vendor/github.com/miekg/dns/zcompress.go create mode 100644 vendor/github.com/miekg/dns/zduplicate.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/account.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/api.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/authorization.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/certificate.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/challenge.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/internal/nonces/nonce_manager.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/internal/secure/jws.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/internal/sender/sender.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/internal/sender/useragent.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/order.go create mode 100644 vendor/github.com/xenolf/lego/acme/api/service.go delete mode 100644 vendor/github.com/xenolf/lego/acme/challenges.go delete mode 100644 vendor/github.com/xenolf/lego/acme/client.go create mode 100644 vendor/github.com/xenolf/lego/acme/commons.go delete mode 100644 vendor/github.com/xenolf/lego/acme/crypto.go delete mode 100644 vendor/github.com/xenolf/lego/acme/dns_challenge.go delete mode 100644 vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go delete mode 100644 vendor/github.com/xenolf/lego/acme/error.go create mode 100644 vendor/github.com/xenolf/lego/acme/errors.go delete mode 100644 vendor/github.com/xenolf/lego/acme/http.go delete mode 100644 vendor/github.com/xenolf/lego/acme/http_challenge.go delete mode 100644 vendor/github.com/xenolf/lego/acme/jws.go delete mode 100644 vendor/github.com/xenolf/lego/acme/messages.go delete mode 100644 vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go create mode 100644 vendor/github.com/xenolf/lego/certcrypto/crypto.go create mode 100644 vendor/github.com/xenolf/lego/certificate/authorization.go create mode 100644 vendor/github.com/xenolf/lego/certificate/certificates.go create mode 100644 vendor/github.com/xenolf/lego/certificate/errors.go create mode 100644 vendor/github.com/xenolf/lego/challenge/challenges.go create mode 100644 vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge.go create mode 100644 vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge_manual.go create mode 100644 vendor/github.com/xenolf/lego/challenge/dns01/fqdn.go create mode 100644 vendor/github.com/xenolf/lego/challenge/dns01/nameserver.go create mode 100644 vendor/github.com/xenolf/lego/challenge/dns01/precheck.go create mode 100644 vendor/github.com/xenolf/lego/challenge/http01/http_challenge.go rename vendor/github.com/xenolf/lego/{acme => challenge/http01}/http_challenge_server.go (61%) rename vendor/github.com/xenolf/lego/{acme => challenge}/provider.go (58%) create mode 100644 vendor/github.com/xenolf/lego/challenge/resolver/errors.go create mode 100644 vendor/github.com/xenolf/lego/challenge/resolver/prober.go create mode 100644 vendor/github.com/xenolf/lego/challenge/resolver/solver_manager.go create mode 100644 vendor/github.com/xenolf/lego/challenge/tlsalpn01/tls_alpn_challenge.go rename vendor/github.com/xenolf/lego/{acme => challenge/tlsalpn01}/tls_alpn_challenge_server.go (54%) create mode 100644 vendor/github.com/xenolf/lego/lego/client.go create mode 100644 vendor/github.com/xenolf/lego/lego/client_config.go rename vendor/github.com/xenolf/lego/{acme/utils.go => platform/wait/wait.go} (58%) rename vendor/github.com/xenolf/lego/providers/dns/cloudxns/{ => internal}/client.go (95%) rename vendor/github.com/xenolf/lego/providers/dns/conoha/{ => internal}/client.go (99%) rename vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/{ => internal}/client.go (99%) create mode 100644 vendor/github.com/xenolf/lego/providers/dns/duckdns/client.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/exec/doc.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/godaddy/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/mydnsjp/client.go rename vendor/github.com/xenolf/lego/providers/dns/netcup/{ => internal}/client.go (94%) rename vendor/github.com/xenolf/lego/providers/dns/nifcloud/{ => internal}/client.go (97%) create mode 100644 vendor/github.com/xenolf/lego/providers/dns/pdns/client.go rename vendor/github.com/xenolf/lego/providers/dns/{vscale => selectel/internal}/client.go (99%) rename vendor/github.com/xenolf/lego/providers/dns/{selectel => vscale/internal}/client.go (99%) create mode 100644 vendor/github.com/xenolf/lego/registration/registar.go create mode 100644 vendor/github.com/xenolf/lego/registration/user.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_aix.go diff --git a/Gopkg.lock b/Gopkg.lock index 2dc3daf36..e96a8fe6f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -430,19 +430,19 @@ [[projects]] branch = "master" - digest = "1:ec6271918b59b872a2d25e374569a4f75f1839d91e4191470c297b7eaaaf7641" + digest = "1:f9adc21a937e5da643ea14a3488cb7506788876737a5e205394e508627a6eec8" name = "github.com/dimchansky/utfbom" packages = ["."] pruneopts = "NUT" - revision = "c410c2305b32ec96caea4e24b4ecbf648e2eeb25" + revision = "d2133a1ce379ef6fa992b0514a77146c60db9d1c" [[projects]] - branch = "master" - digest = "1:e8055cec2992f8bbf63c390aa6b36d78c2f93b13617e4569168c09219f88c6b0" + digest = "1:e856fc44ab196970612bdc8c15e65ccf92ed8d4ccb3a2e65b88dc240a2fe5d0b" name = "github.com/dnsimple/dnsimple-go" packages = ["dnsimple"] pruneopts = "NUT" - revision = "bbe1a2c87affea187478e24d3aea3cac25f870b3" + revision = "f5ead9c20763fd925dea1362f2af5d671ed2a459" + version = "v0.21.0" [[projects]] digest = "1:cf7cba074c4d2f8e2a5cc2f10b1f6762c86cff2e39917b9f9a6dbd7df57fe9c9" @@ -1188,12 +1188,12 @@ source = "https://github.com/containous/mesos-dns.git" [[projects]] - branch = "master" - digest = "1:68cbf3a326abda169b26327b95302db8bcf297a49e689e85746bfc04d1cd8c33" + digest = "1:b83995756f9b1a24c518d40052d80f524f0a9024ee0479d8a8e91ec2548074d1" name = "github.com/miekg/dns" packages = ["."] pruneopts = "NUT" - revision = "906238edc6eb0ddface4a1923f6d41ef2a5ca59b" + revision = "7586a3cbe8ccfc63f82de3ab2ceeb08c9939af72" + version = "v1.1.1" [[projects]] branch = "master" @@ -1705,12 +1705,25 @@ [[projects]] branch = "master" - digest = "1:1c455a900935917ff95c6dfc95461ffc0d7b138a803e9504319f2d9a39e5d6f3" + digest = "1:f3f9f7b883b89edc06283285964a8125b15f31c1634a8ccb2860c8c8b3f6231d" name = "github.com/xenolf/lego" packages = [ "acme", + "acme/api", + "acme/api/internal/nonces", + "acme/api/internal/secure", + "acme/api/internal/sender", + "certcrypto", + "certificate", + "challenge", + "challenge/dns01", + "challenge/http01", + "challenge/resolver", + "challenge/tlsalpn01", + "lego", "log", "platform/config/env", + "platform/wait", "providers/dns", "providers/dns/acmedns", "providers/dns/alidns", @@ -1719,10 +1732,13 @@ "providers/dns/bluecat", "providers/dns/cloudflare", "providers/dns/cloudxns", + "providers/dns/cloudxns/internal", "providers/dns/conoha", + "providers/dns/conoha/internal", "providers/dns/digitalocean", "providers/dns/dnsimple", "providers/dns/dnsmadeeasy", + "providers/dns/dnsmadeeasy/internal", "providers/dns/dnspod", "providers/dns/dreamhost", "providers/dns/duckdns", @@ -1746,7 +1762,9 @@ "providers/dns/namecheap", "providers/dns/namedotcom", "providers/dns/netcup", + "providers/dns/netcup/internal", "providers/dns/nifcloud", + "providers/dns/nifcloud/internal", "providers/dns/ns1", "providers/dns/otc", "providers/dns/ovh", @@ -1756,18 +1774,21 @@ "providers/dns/route53", "providers/dns/sakuracloud", "providers/dns/selectel", + "providers/dns/selectel/internal", "providers/dns/stackpath", "providers/dns/transip", "providers/dns/vegadns", "providers/dns/vscale", + "providers/dns/vscale/internal", "providers/dns/vultr", + "registration", ] pruneopts = "NUT" - revision = "a5f0a3ff8026e05cbdd11c391c0e25122497c736" + revision = "43401f2475dd1f6cc2e220908f0caba246ea854e" [[projects]] branch = "master" - digest = "1:d25ae0946d8bebad296b1a167cd62b92c3079e90c59f91657f69da08fe0cf76c" + digest = "1:30c1930f8c9fee79f3af60c8b7cd92edd12a4f22187f5527d53509b1a794f555" name = "golang.org/x/crypto" packages = [ "bcrypt", @@ -1782,7 +1803,7 @@ "ssh/terminal", ] pruneopts = "NUT" - revision = "91a49db82a88618983a78a06c1cbd4e00ab749ab" + revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447" [[projects]] branch = "master" @@ -2343,8 +2364,16 @@ "github.com/vulcand/oxy/roundrobin", "github.com/vulcand/oxy/utils", "github.com/xenolf/lego/acme", + "github.com/xenolf/lego/certcrypto", + "github.com/xenolf/lego/certificate", + "github.com/xenolf/lego/challenge", + "github.com/xenolf/lego/challenge/dns01", + "github.com/xenolf/lego/challenge/http01", + "github.com/xenolf/lego/challenge/tlsalpn01", + "github.com/xenolf/lego/lego", "github.com/xenolf/lego/log", "github.com/xenolf/lego/providers/dns", + "github.com/xenolf/lego/registration", "golang.org/x/net/http/httpguts", "golang.org/x/net/http2", "golang.org/x/net/http2/hpack", diff --git a/Gopkg.toml b/Gopkg.toml index 2e799aad6..3ed84c8ad 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -246,7 +246,7 @@ revision = "7e6055773c5137efbeb3bd2410d705fe10ab6bfd" [[override]] - branch = "master" + version = "v1.1.1" name = "github.com/miekg/dns" [[constraint]] diff --git a/acme/account.go b/acme/account.go index 77bdcc83a..25521e892 100644 --- a/acme/account.go +++ b/acme/account.go @@ -18,15 +18,16 @@ import ( "github.com/containous/traefik/log" acmeprovider "github.com/containous/traefik/provider/acme" "github.com/containous/traefik/types" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/certcrypto" + "github.com/xenolf/lego/registration" ) // Account is used to store lets encrypt registration info type Account struct { Email string - Registration *acme.RegistrationResource + Registration *registration.Resource PrivateKey []byte - KeyType acme.KeyType + KeyType certcrypto.KeyType DomainsCertificate DomainsCertificates ChallengeCerts map[string]*ChallengeCert HTTPChallenge map[string]map[string][]byte @@ -101,7 +102,7 @@ func (a *Account) GetEmail() string { } // GetRegistration returns lets encrypt registration resource -func (a *Account) GetRegistration() *acme.RegistrationResource { +func (a *Account) GetRegistration() *registration.Resource { return a.Registration } diff --git a/acme/acme.go b/acme/acme.go index 61e6ca128..b699c686b 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -17,7 +17,6 @@ import ( "github.com/BurntSushi/ty/fun" "github.com/cenk/backoff" - "github.com/containous/flaeg" "github.com/containous/mux" "github.com/containous/staert" "github.com/containous/traefik/cluster" @@ -27,9 +26,14 @@ import ( "github.com/containous/traefik/types" "github.com/containous/traefik/version" "github.com/eapache/channels" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/certificate" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/challenge/dns01" + "github.com/xenolf/lego/challenge/http01" + "github.com/xenolf/lego/lego" legolog "github.com/xenolf/lego/log" "github.com/xenolf/lego/providers/dns" + "github.com/xenolf/lego/registration" ) var ( @@ -53,7 +57,7 @@ type ACME struct { TLSChallenge *acmeprovider.TLSChallenge `description:"Activate TLS-ALPN-01 Challenge"` ACMELogging bool `description:"Enable debug logging of ACME actions."` OverrideCertificates bool `description:"Enable to override certificates in key-value store when using storeconfig"` - client *acme.Client + client *lego.Client store cluster.Store challengeHTTPProvider *challengeHTTPProvider challengeTLSProvider *challengeTLSProvider @@ -66,8 +70,6 @@ type ACME struct { } func (a *ACME) init() error { - acme.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version) - if a.ACMELogging { legolog.Logger = log.WithoutContext() } else { @@ -85,7 +87,7 @@ func (a *ACME) init() error { // AddRoutes add routes on internal router func (a *ACME) AddRoutes(router *mux.Router) { router.Methods(http.MethodGet). - Path(acme.HTTP01ChallengePath("{token}")). + Path(http01.ChallengePath("{token}")). Handler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { if a.challengeHTTPProvider == nil { rw.WriteHeader(http.StatusNotFound) @@ -218,7 +220,7 @@ func (a *ACME) leadershipListener(elected bool) error { // New users will need to register; be sure to save it log.Debug("Register...") - reg, err := a.client.Register(true) + reg, err := a.client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) if err != nil { return err } @@ -363,7 +365,7 @@ func (a *ACME) renewCertificates() { } func (a *ACME) renewACMECertificate(certificateResource *DomainsCertificate) (*Certificate, error) { - renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{ + renewedCert, err := a.client.Certificate.Renew(certificate.Resource{ Domain: certificateResource.Certificate.Domain, CertURL: certificateResource.Certificate.CertURL, CertStableURL: certificateResource.Certificate.CertStableURL, @@ -412,28 +414,19 @@ func (a *ACME) storeRenewedCertificate(certificateResource *DomainsCertificate, return nil } -func dnsOverrideDelay(delay flaeg.Duration) error { - var err error - if delay > 0 { - log.Debugf("Delaying %d rather than validating DNS propagation", delay) - acme.PreCheckDNS = func(_, _ string) (bool, error) { - time.Sleep(time.Duration(delay)) - return true, nil - } - } else if delay < 0 { - err = fmt.Errorf("invalid negative DelayBeforeCheck: %d", delay) - } - return err -} - -func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { +func (a *ACME) buildACMEClient(account *Account) (*lego.Client, error) { log.Debug("Building ACME client...") caServer := "https://acme-v02.api.letsencrypt.org/directory" if len(a.CAServer) > 0 { caServer = a.CAServer } - client, err := acme.NewClient(caServer, account, account.KeyType) + config := lego.NewConfig(account) + config.CADirURL = caServer + config.KeyType = account.KeyType + config.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version) + + client, err := lego.NewClient(config) if err != nil { return nil, err } @@ -442,22 +435,23 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { if a.DNSChallenge != nil && len(a.DNSChallenge.Provider) > 0 { log.Debugf("Using DNS Challenge provider: %s", a.DNSChallenge.Provider) - err = dnsOverrideDelay(a.DNSChallenge.DelayBeforeCheck) - if err != nil { - return nil, err - } - - acmeprovider.SetRecursiveNameServers(a.DNSChallenge.Resolvers) - acmeprovider.SetPropagationCheck(a.DNSChallenge.DisablePropagationCheck) - - var provider acme.ChallengeProvider + var provider challenge.Provider provider, err = dns.NewDNSChallengeProviderByName(a.DNSChallenge.Provider) if err != nil { return nil, err } - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSALPN01}) - err = client.SetChallengeProvider(acme.DNS01, provider) + err = client.Challenge.SetDNS01Provider(provider, + dns01.CondOption(len(a.DNSChallenge.Resolvers) > 0, dns01.AddRecursiveNameservers(a.DNSChallenge.Resolvers)), + dns01.CondOption(a.DNSChallenge.DisablePropagationCheck || a.DNSChallenge.DelayBeforeCheck > 0, + dns01.AddPreCheck(func(_, _ string) (bool, error) { + if a.DNSChallenge.DelayBeforeCheck > 0 { + log.Debugf("Delaying %d rather than validating DNS propagation now.", a.DNSChallenge.DelayBeforeCheck) + time.Sleep(time.Duration(a.DNSChallenge.DelayBeforeCheck)) + } + return true, nil + })), + ) return client, err } @@ -465,17 +459,16 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { if a.HTTPChallenge != nil && len(a.HTTPChallenge.EntryPoint) > 0 { log.Debug("Using HTTP Challenge provider.") - client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSALPN01}) a.challengeHTTPProvider = &challengeHTTPProvider{store: a.store} - err = client.SetChallengeProvider(acme.HTTP01, a.challengeHTTPProvider) + err = client.Challenge.SetHTTP01Provider(a.challengeHTTPProvider) return client, err } // TLS Challenge if a.TLSChallenge != nil { log.Debug("Using TLS Challenge provider.") - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01}) - err = client.SetChallengeProvider(acme.TLSALPN01, a.challengeTLSProvider) + + err = client.Challenge.SetTLSALPN01Provider(a.challengeTLSProvider) return client, err } @@ -547,7 +540,7 @@ func (a *ACME) LoadCertificateForDomains(domains []string) { a.addResolvingDomains(uncheckedDomains) defer a.removeResolvingDomains(uncheckedDomains) - certificate, err := a.getDomainsCertificates(uncheckedDomains) + cert, err := a.getDomainsCertificates(uncheckedDomains) if err != nil { log.Errorf("Error getting ACME certificates %+v : %v", uncheckedDomains, err) return @@ -566,7 +559,7 @@ func (a *ACME) LoadCertificateForDomains(domains []string) { domain = types.Domain{Main: uncheckedDomains[0]} } account = object.(*Account) - _, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain) + _, err = account.DomainsCertificate.addCertificateForDomains(cert, domain) if err != nil { log.Errorf("Error adding ACME certificates %+v : %v", uncheckedDomains, err) return @@ -694,7 +687,7 @@ func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) { var cleanDomains []string for _, domain := range domains { canonicalDomain := types.CanonicalDomain(domain) - cleanDomain := acme.UnFqdn(canonicalDomain) + cleanDomain := dns01.UnFqdn(canonicalDomain) if canonicalDomain != cleanDomain { log.Warnf("FQDN detected, please remove the trailing dot: %s", canonicalDomain) } @@ -704,18 +697,24 @@ func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) { log.Debugf("Loading ACME certificates %s...", cleanDomains) bundle := true - certificate, err := a.client.ObtainCertificate(cleanDomains, bundle, nil, OSCPMustStaple) + request := certificate.ObtainRequest{ + Domains: cleanDomains, + Bundle: bundle, + MustStaple: OSCPMustStaple, + } + + cert, err := a.client.Certificate.Obtain(request) if err != nil { return nil, fmt.Errorf("cannot obtain certificates: %+v", err) } log.Debugf("Loaded ACME certificates %s", cleanDomains) return &Certificate{ - Domain: certificate.Domain, - CertURL: certificate.CertURL, - CertStableURL: certificate.CertStableURL, - PrivateKey: certificate.PrivateKey, - Certificate: certificate.Certificate, + Domain: cert.Domain, + CertURL: cert.CertURL, + CertStableURL: cert.CertStableURL, + PrivateKey: cert.PrivateKey, + Certificate: cert.Certificate, }, nil } diff --git a/acme/acme_test.go b/acme/acme_test.go index aadfa17b6..bfe76ac6e 100644 --- a/acme/acme_test.go +++ b/acme/acme_test.go @@ -15,7 +15,6 @@ import ( "github.com/containous/traefik/tls/generate" "github.com/containous/traefik/types" "github.com/stretchr/testify/assert" - "github.com/xenolf/lego/acme" ) func TestDomainsSet(t *testing.T) { @@ -258,39 +257,10 @@ func TestRemoveDuplicates(t *testing.T) { } } -func TestNoPreCheckOverride(t *testing.T) { - acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process - err := dnsOverrideDelay(0) - if err != nil { - t.Errorf("Error in dnsOverrideDelay :%v", err) - } - if acme.PreCheckDNS != nil { - t.Error("Unexpected change to acme.PreCheckDNS when leaving DNS verification as is.") - } -} - -func TestSillyPreCheckOverride(t *testing.T) { - err := dnsOverrideDelay(-5) - if err == nil { - t.Error("Missing expected error in dnsOverrideDelay!") - } -} - -func TestPreCheckOverride(t *testing.T) { - acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process - err := dnsOverrideDelay(5) - if err != nil { - t.Errorf("Error in dnsOverrideDelay :%v", err) - } - if acme.PreCheckDNS == nil { - t.Error("No change to acme.PreCheckDNS when meant to be adding enforcing override function.") - } -} - func TestAcmeClientCreation(t *testing.T) { - acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process // Lengthy setup to avoid external web requests - oh for easier golang testing! account := &Account{Email: "f@f"} + account.PrivateKey, _ = base64.StdEncoding.DecodeString(` MIIBPAIBAAJBAMp2Ni92FfEur+CAvFkgC12LT4l9D53ApbBpDaXaJkzzks+KsLw9zyAxvlrfAyTCQ 7tDnEnIltAXyQ0uOFUUdcMCAwEAAQJAK1FbipATZcT9cGVa5x7KD7usytftLW14heQUPXYNV80r/3 @@ -298,8 +268,9 @@ lmnpvjL06dffRpwkYeN8DATQF/QOcy3NNNGDw/4QIhAPAKmiZFxA/qmRXsuU8Zhlzf16WrNZ68K64 asn/h3qZrAiEA1+wFR3WXCPIolOvd7AHjfgcTKQNkoMPywU4FYUNQ1AkCIQDv8yk0qPjckD6HVCPJ llJh9MC0svjevGtNlxJoE3lmEQIhAKXy1wfZ32/XtcrnENPvi6lzxI0T94X7s5pP3aCoPPoJAiEAl cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(`{ + _, err := w.Write([]byte(`{ "GPHhmRVEDas": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417", "keyChange": "https://foo/acme/key-change", "meta": { @@ -310,9 +281,20 @@ cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`) "newOrder": "https://foo/acme/new-order", "revokeCert": "https://foo/acme/revoke-cert" }`)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } })) defer ts.Close() - a := ACME{DNSChallenge: &acmeprovider.DNSChallenge{Provider: "manual", DelayBeforeCheck: 10}, CAServer: ts.URL} + + a := ACME{ + CAServer: ts.URL, + DNSChallenge: &acmeprovider.DNSChallenge{ + Provider: "manual", + DelayBeforeCheck: 10, + DisablePropagationCheck: true, + }, + } client, err := a.buildACMEClient(account) if err != nil { @@ -321,9 +303,6 @@ cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`) if client == nil { t.Error("No client from buildACMEClient!") } - if acme.PreCheckDNS == nil { - t.Error("No change to acme.PreCheckDNS when meant to be adding enforcing override function.") - } } func TestAcme_getUncheckedCertificates(t *testing.T) { diff --git a/acme/challenge_http_provider.go b/acme/challenge_http_provider.go index 333221569..4b7bda321 100644 --- a/acme/challenge_http_provider.go +++ b/acme/challenge_http_provider.go @@ -9,10 +9,10 @@ import ( "github.com/containous/traefik/cluster" "github.com/containous/traefik/log" "github.com/containous/traefik/safe" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge" ) -var _ acme.ChallengeProviderTimeout = (*challengeHTTPProvider)(nil) +var _ challenge.ProviderTimeout = (*challengeHTTPProvider)(nil) type challengeHTTPProvider struct { store cluster.Store diff --git a/acme/challenge_tls_provider.go b/acme/challenge_tls_provider.go index a519bc8c6..9b40acc34 100644 --- a/acme/challenge_tls_provider.go +++ b/acme/challenge_tls_provider.go @@ -11,10 +11,11 @@ import ( "github.com/containous/traefik/cluster" "github.com/containous/traefik/log" "github.com/containous/traefik/safe" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/challenge/tlsalpn01" ) -var _ acme.ChallengeProviderTimeout = (*challengeTLSProvider)(nil) +var _ challenge.ProviderTimeout = (*challengeTLSProvider)(nil) type challengeTLSProvider struct { store cluster.Store @@ -113,7 +114,7 @@ func (c *challengeTLSProvider) Timeout() (timeout, interval time.Duration) { } func tlsALPN01ChallengeCert(domain, keyAuth string) (*ChallengeCert, error) { - tempCertPEM, rsaPrivPEM, err := acme.TLSALPNChallengeBlocks(domain, keyAuth) + tempCertPEM, rsaPrivPEM, err := tlsalpn01.ChallengeBlocks(domain, keyAuth) if err != nil { return nil, err } diff --git a/config/static/static_config.go b/config/static/static_config.go index 8c4370c54..5a606ecb8 100644 --- a/config/static/static_config.go +++ b/config/static/static_config.go @@ -31,7 +31,7 @@ import ( "github.com/containous/traefik/tracing/zipkin" "github.com/containous/traefik/types" "github.com/elazarl/go-bindata-assetfs" - lego "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" ) const ( @@ -374,11 +374,11 @@ func convertACMEChallenge(oldACMEChallenge *acme.ACME) *acmeprovider.Configurati } for _, domain := range oldACMEChallenge.Domains { - if domain.Main != lego.UnFqdn(domain.Main) { + if domain.Main != dns01.UnFqdn(domain.Main) { log.Warnf("FQDN detected, please remove the trailing dot: %s", domain.Main) } for _, san := range domain.SANs { - if san != lego.UnFqdn(san) { + if san != dns01.UnFqdn(san) { log.Warnf("FQDN detected, please remove the trailing dot: %s", san) } } diff --git a/docs/configuration/acme.md b/docs/configuration/acme.md index 318b7c438..159f2cbf0 100644 --- a/docs/configuration/acme.md +++ b/docs/configuration/acme.md @@ -332,6 +332,14 @@ Here is a list of supported `provider`s, that can automate the DNS verification, Use custom DNS servers to resolve the FQDN authority. +```toml +[acme] +# ... +[acme.dnsChallenge] + # ... + resolvers = ["1.1.1.1:53", "8.8.8.8:53"] +``` + ### `domains` You can provide SANs (alternative domains) to each main domain. diff --git a/old/configuration/configuration.go b/old/configuration/configuration.go index b6f231d5b..8aa1cf7ca 100644 --- a/old/configuration/configuration.go +++ b/old/configuration/configuration.go @@ -34,7 +34,7 @@ import ( acmeprovider "github.com/containous/traefik/provider/acme" newtypes "github.com/containous/traefik/types" "github.com/pkg/errors" - lego "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" ) const ( @@ -414,11 +414,11 @@ func convertACMEChallenge(oldACMEChallenge *acme.ACME) *acmeprovider.Configurati } for _, domain := range oldACMEChallenge.Domains { - if domain.Main != lego.UnFqdn(domain.Main) { + if domain.Main != dns01.UnFqdn(domain.Main) { log.Warnf("FQDN detected, please remove the trailing dot: %s", domain.Main) } for _, san := range domain.SANs { - if san != lego.UnFqdn(san) { + if san != dns01.UnFqdn(san) { log.Warnf("FQDN detected, please remove the trailing dot: %s", san) } } diff --git a/provider/acme/account.go b/provider/acme/account.go index 1f74811b5..cc7de4f15 100644 --- a/provider/acme/account.go +++ b/provider/acme/account.go @@ -8,15 +8,16 @@ import ( "crypto/x509" "github.com/containous/traefik/log" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/certcrypto" + "github.com/xenolf/lego/registration" ) // Account is used to store lets encrypt registration info type Account struct { Email string - Registration *acme.RegistrationResource + Registration *registration.Resource PrivateKey []byte - KeyType acme.KeyType + KeyType certcrypto.KeyType } const ( @@ -47,7 +48,7 @@ func (a *Account) GetEmail() string { } // GetRegistration returns lets encrypt registration resource -func (a *Account) GetRegistration() *acme.RegistrationResource { +func (a *Account) GetRegistration() *registration.Resource { return a.Registration } @@ -64,25 +65,25 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey { } // GetKeyType used to determine which algo to used -func GetKeyType(ctx context.Context, value string) acme.KeyType { +func GetKeyType(ctx context.Context, value string) certcrypto.KeyType { logger := log.FromContext(ctx) switch value { case "EC256": - return acme.EC256 + return certcrypto.EC256 case "EC384": - return acme.EC384 + return certcrypto.EC384 case "RSA2048": - return acme.RSA2048 + return certcrypto.RSA2048 case "RSA4096": - return acme.RSA4096 + return certcrypto.RSA4096 case "RSA8192": - return acme.RSA8192 + return certcrypto.RSA8192 case "": - logger.Infof("The key type is empty. Use default key type %v.", acme.RSA4096) - return acme.RSA4096 + logger.Infof("The key type is empty. Use default key type %v.", certcrypto.RSA4096) + return certcrypto.RSA4096 default: - logger.Infof("Unable to determine the key type value %q: falling back on %v.", value, acme.RSA4096) - return acme.RSA4096 + logger.Infof("Unable to determine the key type value %q: falling back on %v.", value, certcrypto.RSA4096) + return certcrypto.RSA4096 } } diff --git a/provider/acme/challenge_http.go b/provider/acme/challenge_http.go index 9a522a4f6..26d827794 100644 --- a/provider/acme/challenge_http.go +++ b/provider/acme/challenge_http.go @@ -10,10 +10,11 @@ import ( "github.com/containous/mux" "github.com/containous/traefik/log" "github.com/containous/traefik/safe" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/challenge/http01" ) -var _ acme.ChallengeProviderTimeout = (*challengeHTTP)(nil) +var _ challenge.ProviderTimeout = (*challengeHTTP)(nil) type challengeHTTP struct { Store Store @@ -37,7 +38,7 @@ func (c *challengeHTTP) Timeout() (timeout, interval time.Duration) { // Append adds routes on internal router func (p *Provider) Append(router *mux.Router) { router.Methods(http.MethodGet). - Path(acme.HTTP01ChallengePath("{token}")). + Path(http01.ChallengePath("{token}")). Handler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) diff --git a/provider/acme/challenge_tls.go b/provider/acme/challenge_tls.go index 6b818b2e1..dcbd449de 100644 --- a/provider/acme/challenge_tls.go +++ b/provider/acme/challenge_tls.go @@ -5,10 +5,11 @@ import ( "github.com/containous/traefik/log" "github.com/containous/traefik/types" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/challenge/tlsalpn01" ) -var _ acme.ChallengeProvider = (*challengeTLSALPN)(nil) +var _ challenge.Provider = (*challengeTLSALPN)(nil) type challengeTLSALPN struct { Store Store @@ -18,7 +19,7 @@ func (c *challengeTLSALPN) Present(domain, token, keyAuth string) error { log.WithoutContext().WithField(log.ProviderName, "acme"). Debugf("TLS Challenge Present temp certificate for %s", domain) - certPEMBlock, keyPEMBlock, err := acme.TLSALPNChallengeBlocks(domain, keyAuth) + certPEMBlock, keyPEMBlock, err := tlsalpn01.ChallengeBlocks(domain, keyAuth) if err != nil { return err } diff --git a/provider/acme/provider.go b/provider/acme/provider.go index 0d673cd9c..3315bd27a 100644 --- a/provider/acme/provider.go +++ b/provider/acme/provider.go @@ -7,7 +7,6 @@ import ( "fmt" "io/ioutil" fmtlog "log" - "net" "net/url" "reflect" "strings" @@ -25,9 +24,13 @@ import ( "github.com/containous/traefik/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/certificate" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/challenge/dns01" + "github.com/xenolf/lego/lego" legolog "github.com/xenolf/lego/log" "github.com/xenolf/lego/providers/dns" + "github.com/xenolf/lego/registration" ) var ( @@ -83,7 +86,7 @@ type Provider struct { Store Store certificates []*Certificate account *Account - client *acme.Client + client *lego.Client certsChan chan *Certificate configurationChan chan<- config.Message certificateStore *traefiktls.CertificateStore @@ -118,9 +121,9 @@ func (p *Provider) ListenRequest(domain string) (*tls.Certificate, error) { return nil, err } - certificate, err := tls.X509KeyPair(acmeCert.Certificate, acmeCert.PrivateKey) + cert, err := tls.X509KeyPair(acmeCert.Certificate, acmeCert.PrivateKey) - return &certificate, err + return &cert, err } // Init for compatibility reason the BaseProvider implements an empty Init @@ -128,8 +131,6 @@ func (p *Provider) Init() error { ctx := log.With(context.Background(), log.Str(log.ProviderName, "acme")) logger := log.FromContext(ctx) - acme.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version) - if p.ACMELogging { legolog.Logger = fmtlog.New(logger.WriterLevel(logrus.InfoLevel), "legolog: ", 0) } else { @@ -223,7 +224,7 @@ func (p *Provider) Provide(configurationChan chan<- config.Message, pool *safe.P return nil } -func (p *Provider) getClient() (*acme.Client, error) { +func (p *Provider) getClient() (*lego.Client, error) { p.clientMutex.Lock() defer p.clientMutex.Unlock() @@ -247,7 +248,12 @@ func (p *Provider) getClient() (*acme.Client, error) { } logger.Debug(caServer) - client, err := acme.NewClient(caServer, account, account.KeyType) + config := lego.NewConfig(account) + config.CADirURL = caServer + config.KeyType = account.KeyType + config.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version) + + client, err := lego.NewClient(config) if err != nil { return nil, err } @@ -256,7 +262,7 @@ func (p *Provider) getClient() (*acme.Client, error) { if account.GetRegistration() == nil { logger.Info("Register...") - reg, errR := client.Register(true) + reg, errR := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) if errR != nil { return nil, errR } @@ -274,23 +280,23 @@ func (p *Provider) getClient() (*acme.Client, error) { if p.DNSChallenge != nil && len(p.DNSChallenge.Provider) > 0 { logger.Debugf("Using DNS Challenge provider: %s", p.DNSChallenge.Provider) - SetRecursiveNameServers(p.DNSChallenge.Resolvers) - SetPropagationCheck(p.DNSChallenge.DisablePropagationCheck) - - err = dnsOverrideDelay(ctx, p.DNSChallenge.DelayBeforeCheck) - if err != nil { - return nil, err - } - - var provider acme.ChallengeProvider + var provider challenge.Provider provider, err = dns.NewDNSChallengeProviderByName(p.DNSChallenge.Provider) if err != nil { return nil, err } - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSALPN01}) - - err = client.SetChallengeProvider(acme.DNS01, provider) + err = client.Challenge.SetDNS01Provider(provider, + dns01.CondOption(len(p.DNSChallenge.Resolvers) > 0, dns01.AddRecursiveNameservers(p.DNSChallenge.Resolvers)), + dns01.CondOption(p.DNSChallenge.DisablePropagationCheck || p.DNSChallenge.DelayBeforeCheck > 0, + dns01.AddPreCheck(func(_, _ string) (bool, error) { + if p.DNSChallenge.DelayBeforeCheck > 0 { + log.Debugf("Delaying %d rather than validating DNS propagation now.", p.DNSChallenge.DelayBeforeCheck) + time.Sleep(time.Duration(p.DNSChallenge.DelayBeforeCheck)) + } + return true, nil + })), + ) if err != nil { return nil, err } @@ -300,25 +306,21 @@ func (p *Provider) getClient() (*acme.Client, error) { p.DNSChallenge.preCheckInterval = 2 * time.Second // Set the precheck timeout into the DNSChallenge provider - if challengeProviderTimeout, ok := provider.(acme.ChallengeProviderTimeout); ok { + if challengeProviderTimeout, ok := provider.(challenge.ProviderTimeout); ok { p.DNSChallenge.preCheckTimeout, p.DNSChallenge.preCheckInterval = challengeProviderTimeout.Timeout() } } else if p.HTTPChallenge != nil && len(p.HTTPChallenge.EntryPoint) > 0 { logger.Debug("Using HTTP Challenge provider.") - client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSALPN01}) - - err = client.SetChallengeProvider(acme.HTTP01, &challengeHTTP{Store: p.Store}) + err = client.Challenge.SetHTTP01Provider(&challengeHTTP{Store: p.Store}) if err != nil { return nil, err } } else if p.TLSChallenge != nil { logger.Debug("Using TLS Challenge provider.") - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01}) - - err = client.SetChallengeProvider(acme.TLSALPN01, &challengeTLSALPN{Store: p.Store}) + err = client.Challenge.SetTLSALPN01Provider(&challengeTLSALPN{Store: p.Store}) if err != nil { return nil, err } @@ -383,7 +385,6 @@ func (p *Provider) watchNewDomains(ctx context.Context) { } }) } - } case <-stop: return @@ -392,7 +393,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) { }) } -func (p *Provider) resolveCertificate(ctx context.Context, domain types.Domain, domainFromConfigurationFile bool) (*acme.CertificateResource, error) { +func (p *Provider) resolveCertificate(ctx context.Context, domain types.Domain, domainFromConfigurationFile bool) (*certificate.Resource, error) { domains, err := p.getValidDomains(ctx, domain, domainFromConfigurationFile) if err != nil { return nil, err @@ -415,22 +416,27 @@ func (p *Provider) resolveCertificate(ctx context.Context, domain types.Domain, return nil, fmt.Errorf("cannot get ACME client %v", err) } - var certificate *acme.CertificateResource + var cert *certificate.Resource bundle := true if p.useCertificateWithRetry(uncheckedDomains) { - certificate, err = obtainCertificateWithRetry(ctx, domains, client, p.DNSChallenge.preCheckTimeout, p.DNSChallenge.preCheckInterval, bundle) + cert, err = obtainCertificateWithRetry(ctx, domains, client, p.DNSChallenge.preCheckTimeout, p.DNSChallenge.preCheckInterval, bundle) } else { - certificate, err = client.ObtainCertificate(domains, bundle, nil, oscpMustStaple) + request := certificate.ObtainRequest{ + Domains: domains, + Bundle: bundle, + MustStaple: oscpMustStaple, + } + cert, err = client.Certificate.Obtain(request) } if err != nil { return nil, fmt.Errorf("unable to generate a certificate for the domains %v: %v", uncheckedDomains, err) } - if certificate == nil { + if cert == nil { return nil, fmt.Errorf("domains %v do not generate a certificate", uncheckedDomains) } - if len(certificate.Certificate) == 0 || len(certificate.PrivateKey) == 0 { - return nil, fmt.Errorf("domains %v generate certificate with no value: %v", uncheckedDomains, certificate) + if len(cert.Certificate) == 0 || len(cert.PrivateKey) == 0 { + return nil, fmt.Errorf("domains %v generate certificate with no value: %v", uncheckedDomains, cert) } logger.Debugf("Certificates obtained for domains %+v", uncheckedDomains) @@ -440,9 +446,9 @@ func (p *Provider) resolveCertificate(ctx context.Context, domain types.Domain, } else { domain = types.Domain{Main: uncheckedDomains[0]} } - p.addCertificateForDomain(domain, certificate.Certificate, certificate.PrivateKey) + p.addCertificateForDomain(domain, cert.Certificate, cert.PrivateKey) - return certificate, nil + return cert, nil } func (p *Provider) removeResolvingDomains(resolvingDomains []string) { @@ -489,14 +495,19 @@ func (p *Provider) useCertificateWithRetry(domains []string) bool { return false } -func obtainCertificateWithRetry(ctx context.Context, domains []string, client *acme.Client, timeout, interval time.Duration, bundle bool) (*acme.CertificateResource, error) { +func obtainCertificateWithRetry(ctx context.Context, domains []string, client *lego.Client, timeout, interval time.Duration, bundle bool) (*certificate.Resource, error) { logger := log.FromContext(ctx) - var certificate *acme.CertificateResource + var cert *certificate.Resource var err error operation := func() error { - certificate, err = client.ObtainCertificate(domains, bundle, nil, oscpMustStaple) + request := certificate.ObtainRequest{ + Domains: domains, + Bundle: bundle, + MustStaple: oscpMustStaple, + } + cert, err = client.Certificate.Obtain(request) return err } @@ -516,25 +527,7 @@ func obtainCertificateWithRetry(ctx context.Context, domains []string, client *a return nil, err } - return certificate, nil -} - -func dnsOverrideDelay(ctx context.Context, delay parse.Duration) error { - if delay == 0 { - return nil - } - - if delay > 0 { - log.FromContext(ctx).Debugf("Delaying %d rather than validating DNS propagation now.", delay) - - acme.PreCheckDNS = func(_, _ string) (bool, error) { - time.Sleep(time.Duration(delay)) - return true, nil - } - } else { - return fmt.Errorf("delayBeforeCheck: %d cannot be less than 0", delay) - } - return nil + return cert, nil } func (p *Provider) addCertificateForDomain(domain types.Domain, certificate []byte, key []byte) { @@ -649,8 +642,8 @@ func (p *Provider) refreshCertificates() { } for _, cert := range p.certificates { - certificate := &traefiktls.Certificate{CertFile: traefiktls.FileOrContent(cert.Certificate), KeyFile: traefiktls.FileOrContent(cert.Key)} - conf.Configuration.TLS = append(conf.Configuration.TLS, &traefiktls.Configuration{Certificate: certificate, EntryPoints: []string{p.EntryPoint}}) + cert := &traefiktls.Certificate{CertFile: traefiktls.FileOrContent(cert.Certificate), KeyFile: traefiktls.FileOrContent(cert.Key)} + conf.Configuration.TLS = append(conf.Configuration.TLS, &traefiktls.Configuration{Certificate: cert, EntryPoints: []string{p.EntryPoint}}) } p.configurationChan <- conf } @@ -659,36 +652,36 @@ func (p *Provider) renewCertificates(ctx context.Context) { logger := log.FromContext(ctx) logger.Info("Testing certificate renew...") - for _, certificate := range p.certificates { - crt, err := getX509Certificate(ctx, certificate) + for _, cert := range p.certificates { + crt, err := getX509Certificate(ctx, cert) // If there's an error, we assume the cert is broken, and needs update // <= 30 days left, renew certificate if err != nil || crt == nil || crt.NotAfter.Before(time.Now().Add(24*30*time.Hour)) { client, err := p.getClient() if err != nil { - logger.Infof("Error renewing certificate from LE : %+v, %v", certificate.Domain, err) + logger.Infof("Error renewing certificate from LE : %+v, %v", cert.Domain, err) continue } - logger.Infof("Renewing certificate from LE : %+v", certificate.Domain) + logger.Infof("Renewing certificate from LE : %+v", cert.Domain) - renewedCert, err := client.RenewCertificate(acme.CertificateResource{ - Domain: certificate.Domain.Main, - PrivateKey: certificate.Key, - Certificate: certificate.Certificate, + renewedCert, err := client.Certificate.Renew(certificate.Resource{ + Domain: cert.Domain.Main, + PrivateKey: cert.Key, + Certificate: cert.Certificate, }, true, oscpMustStaple) if err != nil { - logger.Errorf("Error renewing certificate from LE: %v, %v", certificate.Domain, err) + logger.Errorf("Error renewing certificate from LE: %v, %v", cert.Domain, err) continue } if len(renewedCert.Certificate) == 0 || len(renewedCert.PrivateKey) == 0 { - logger.Errorf("domains %v renew certificate with no value: %v", certificate.Domain.ToStrArray(), certificate) + logger.Errorf("domains %v renew certificate with no value: %v", cert.Domain.ToStrArray(), cert) continue } - p.addCertificateForDomain(certificate.Domain, renewedCert.Certificate, renewedCert.PrivateKey) + p.addCertificateForDomain(cert.Domain, renewedCert.Certificate, renewedCert.PrivateKey) } } } @@ -704,8 +697,8 @@ func (p *Provider) getUncheckedDomains(ctx context.Context, domainsToCheck []str allDomains := p.certificateStore.GetAllDomains() // Get ACME certificates - for _, certificate := range p.certificates { - allDomains = append(allDomains, strings.Join(certificate.Domain.ToStrArray(), ",")) + for _, cert := range p.certificates { + allDomains = append(allDomains, strings.Join(cert.Domain.ToStrArray(), ",")) } // Get currently resolved domains @@ -740,12 +733,12 @@ func searchUncheckedDomains(ctx context.Context, domainsToCheck []string, existe return uncheckedDomains } -func getX509Certificate(ctx context.Context, certificate *Certificate) (*x509.Certificate, error) { +func getX509Certificate(ctx context.Context, cert *Certificate) (*x509.Certificate, error) { logger := log.FromContext(ctx) - tlsCert, err := tls.X509KeyPair(certificate.Certificate, certificate.Key) + tlsCert, err := tls.X509KeyPair(cert.Certificate, cert.Key) if err != nil { - logger.Errorf("Failed to load TLS key pair from ACME certificate for domain %q (SAN : %q), certificate will be renewed : %v", certificate.Domain.Main, strings.Join(certificate.Domain.SANs, ","), err) + logger.Errorf("Failed to load TLS key pair from ACME certificate for domain %q (SAN : %q), certificate will be renewed : %v", cert.Domain.Main, strings.Join(cert.Domain.SANs, ","), err) return nil, err } @@ -753,7 +746,7 @@ func getX509Certificate(ctx context.Context, certificate *Certificate) (*x509.Ce if crt == nil { crt, err = x509.ParseCertificate(tlsCert.Certificate[0]) if err != nil { - logger.Errorf("Failed to parse TLS key pair from ACME certificate for domain %q (SAN : %q), certificate will be renewed : %v", certificate.Domain.Main, strings.Join(certificate.Domain.SANs, ","), err) + logger.Errorf("Failed to parse TLS key pair from ACME certificate for domain %q (SAN : %q), certificate will be renewed : %v", cert.Domain.Main, strings.Join(cert.Domain.SANs, ","), err) } } @@ -790,7 +783,7 @@ func (p *Provider) getValidDomains(ctx context.Context, domain types.Domain, wil var cleanDomains []string for _, domain := range domains { canonicalDomain := types.CanonicalDomain(domain) - cleanDomain := acme.UnFqdn(canonicalDomain) + cleanDomain := dns01.UnFqdn(canonicalDomain) if canonicalDomain != cleanDomain { log.FromContext(ctx).Warnf("FQDN detected, please remove the trailing dot: %s", canonicalDomain) } @@ -810,37 +803,3 @@ func isDomainAlreadyChecked(domainToCheck string, existentDomains []string) bool } return false } - -// SetPropagationCheck to disable the Lego PreCheck. -func SetPropagationCheck(disable bool) { - if disable { - acme.PreCheckDNS = func(_, _ string) (bool, error) { - return true, nil - } - } -} - -// SetRecursiveNameServers to provide a custom DNS resolver. -func SetRecursiveNameServers(dnsResolvers []string) { - resolvers := normaliseDNSResolvers(dnsResolvers) - if len(resolvers) > 0 { - acme.RecursiveNameservers = resolvers - log.Infof("Validating FQDN authority with DNS using %+v", resolvers) - } -} - -// ensure all servers have a port number -func normaliseDNSResolvers(dnsResolvers []string) []string { - var normalisedResolvers []string - for _, server := range dnsResolvers { - srv := strings.TrimSpace(server) - if len(srv) > 0 { - if host, port, err := net.SplitHostPort(srv); err != nil { - normalisedResolvers = append(normalisedResolvers, net.JoinHostPort(srv, "53")) - } else { - normalisedResolvers = append(normalisedResolvers, net.JoinHostPort(host, port)) - } - } - } - return normalisedResolvers -} diff --git a/provider/acme/provider_test.go b/provider/acme/provider_test.go index f792cfb48..efb5ca916 100644 --- a/provider/acme/provider_test.go +++ b/provider/acme/provider_test.go @@ -9,7 +9,7 @@ import ( traefiktls "github.com/containous/traefik/tls" "github.com/containous/traefik/types" "github.com/stretchr/testify/assert" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/certcrypto" ) func TestGetUncheckedCertificates(t *testing.T) { @@ -592,11 +592,11 @@ func TestInitAccount(t *testing.T) { desc: "Existing account with all information", account: &Account{ Email: "foo@foo.net", - KeyType: acme.EC256, + KeyType: certcrypto.EC256, }, expectedAccount: &Account{ Email: "foo@foo.net", - KeyType: acme.EC256, + KeyType: certcrypto.EC256, }, }, { @@ -605,19 +605,19 @@ func TestInitAccount(t *testing.T) { keyType: "EC256", expectedAccount: &Account{ Email: "foo@foo.net", - KeyType: acme.EC256, + KeyType: certcrypto.EC256, }, }, { desc: "Existing account with no email", account: &Account{ - KeyType: acme.RSA4096, + KeyType: certcrypto.RSA4096, }, email: "foo@foo.net", keyType: "EC256", expectedAccount: &Account{ Email: "foo@foo.net", - KeyType: acme.EC256, + KeyType: certcrypto.EC256, }, }, { @@ -629,7 +629,7 @@ func TestInitAccount(t *testing.T) { keyType: "EC256", expectedAccount: &Account{ Email: "foo@foo.net", - KeyType: acme.EC256, + KeyType: certcrypto.EC256, }, }, { @@ -640,7 +640,7 @@ func TestInitAccount(t *testing.T) { email: "bar@foo.net", expectedAccount: &Account{ Email: "foo@foo.net", - KeyType: acme.RSA4096, + KeyType: certcrypto.RSA4096, }, }, } diff --git a/server/server_entrypoint.go b/server/server_entrypoint.go index 849b58129..dae1785d2 100644 --- a/server/server_entrypoint.go +++ b/server/server_entrypoint.go @@ -22,7 +22,7 @@ import ( "github.com/containous/traefik/tls/generate" "github.com/containous/traefik/types" "github.com/sirupsen/logrus" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/tlsalpn01" ) // EntryPoints map of EntryPoint @@ -380,7 +380,7 @@ func buildTLSConfig(tlsOption traefiktls.TLS) (*tls.Config, error) { conf := &tls.Config{} // ensure http2 enabled - conf.NextProtos = []string{"h2", "http/1.1", acme.ACMETLS1Protocol} + conf.NextProtos = []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol} if len(tlsOption.ClientCA.Files) > 0 { pool := x509.NewCertPool() diff --git a/vendor/github.com/dimchansky/utfbom/utfbom.go b/vendor/github.com/dimchansky/utfbom/utfbom.go index 648184a12..77a303e56 100644 --- a/vendor/github.com/dimchansky/utfbom/utfbom.go +++ b/vendor/github.com/dimchansky/utfbom/utfbom.go @@ -32,6 +32,24 @@ const ( UTF32LittleEndian ) +// String returns a user-friendly string representation of the encoding. Satisfies fmt.Stringer interface. +func (e Encoding) String() string { + switch e { + case UTF8: + return "UTF8" + case UTF16BigEndian: + return "UTF16BigEndian" + case UTF16LittleEndian: + return "UTF16LittleEndian" + case UTF32BigEndian: + return "UTF32BigEndian" + case UTF32LittleEndian: + return "UTF32LittleEndian" + default: + return "Unknown" + } +} + const maxConsecutiveEmptyReads = 100 // Skip creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary. diff --git a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/authentication.go b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/authentication.go index 48e375a7e..dac6f51b3 100644 --- a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/authentication.go +++ b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/authentication.go @@ -1,68 +1,52 @@ package dnsimple import ( - "encoding/base64" + "net/http" ) -const ( - httpHeaderDomainToken = "X-DNSimple-Domain-Token" - httpHeaderApiToken = "X-DNSimple-Token" - httpHeaderAuthorization = "Authorization" -) +// BasicAuthTransport is an http.RoundTripper that authenticates all requests +// using HTTP Basic Authentication with the provided username and password. +type BasicAuthTransport struct { + Username string + Password string -// Provides credentials that can be used for authenticating with DNSimple. -// -// See https://developer.dnsimple.com/v2/#authentication -type Credentials interface { - // Returns the HTTP headers that should be set - // to authenticate the HTTP Request. - Headers() map[string]string + // Transport is the transport RoundTripper used to make HTTP requests. + // If nil, http.DefaultTransport is used. + Transport http.RoundTripper } -// Domain token authentication -type domainTokenCredentials struct { - domainToken string +// RoundTrip implements the RoundTripper interface. We just add the +// basic auth and return the RoundTripper for this transport type. +func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { + req2 := cloneRequest(req) // per RoundTripper contract + + req2.SetBasicAuth(t.Username, t.Password) + return t.transport().RoundTrip(req2) } -// NewDomainTokenCredentials construct Credentials using the DNSimple Domain Token method. -func NewDomainTokenCredentials(domainToken string) Credentials { - return &domainTokenCredentials{domainToken: domainToken} +// Client returns an *http.Client that uses the BasicAuthTransport transport +// to authenticate the request via HTTP Basic Auth. +func (t *BasicAuthTransport) Client() *http.Client { + return &http.Client{Transport: t} } -func (c *domainTokenCredentials) Headers() map[string]string { - return map[string]string{httpHeaderDomainToken: c.domainToken} +func (t *BasicAuthTransport) transport() http.RoundTripper { + if t.Transport != nil { + return t.Transport + } + return http.DefaultTransport } -// HTTP basic authentication -type httpBasicCredentials struct { - email string - password string -} - -// NewHTTPBasicCredentials construct Credentials using HTTP Basic Auth. -func NewHTTPBasicCredentials(email, password string) Credentials { - return &httpBasicCredentials{email, password} -} - -func (c *httpBasicCredentials) Headers() map[string]string { - return map[string]string{httpHeaderAuthorization: "Basic " + c.basicAuth(c.email, c.password)} -} - -func (c *httpBasicCredentials) basicAuth(username, password string) string { - auth := username + ":" + password - return base64.StdEncoding.EncodeToString([]byte(auth)) -} - -// OAuth token authentication -type oauthTokenCredentials struct { - oauthToken string -} - -// NewOauthTokenCredentials construct Credentials using the OAuth access token. -func NewOauthTokenCredentials(oauthToken string) Credentials { - return &oauthTokenCredentials{oauthToken: oauthToken} -} - -func (c *oauthTokenCredentials) Headers() map[string]string { - return map[string]string{httpHeaderAuthorization: "Bearer " + c.oauthToken} +// cloneRequest returns a clone of the provided *http.Request. +// The clone is a shallow copy of the struct and its Header map. +func cloneRequest(r *http.Request) *http.Request { + // shallow copy of the struct + r2 := new(http.Request) + *r2 = *r + // deep copy of the Header + r2.Header = make(http.Header, len(r.Header)) + for k, s := range r.Header { + r2.Header[k] = append([]string(nil), s...) + } + return r2 } diff --git a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/dnsimple.go b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/dnsimple.go index 36887a67b..6c32e1632 100644 --- a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/dnsimple.go +++ b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/dnsimple.go @@ -23,7 +23,7 @@ const ( // This is a pro-forma convention given that Go dependencies // tends to be fetched directly from the repo. // It is also used in the user-agent identify the client. - Version = "0.16.0" + Version = "0.21.0" // defaultBaseURL to the DNSimple production API. defaultBaseURL = "https://api.dnsimple.com" @@ -37,12 +37,9 @@ const ( // Client represents a client to the DNSimple API. type Client struct { - // HttpClient is the underlying HTTP client + // httpClient is the underlying HTTP client // used to communicate with the API. - HttpClient *http.Client - - // Credentials used for accessing the DNSimple API - Credentials Credentials + httpClient *http.Client // BaseURL for API requests. // Defaults to the public DNSimple API, but can be set to a different endpoint (e.g. the sandbox). @@ -85,9 +82,12 @@ type ListOptions struct { Sort string `url:"sort,omitempty"` } -// NewClient returns a new DNSimple API client using the given credentials. -func NewClient(credentials Credentials) *Client { - c := &Client{Credentials: credentials, HttpClient: &http.Client{}, BaseURL: defaultBaseURL} +// NewClient returns a new DNSimple API client. +// +// To authenticate you must provide an http.Client that will perform authentication +// for you with one of the currently supported mechanisms: OAuth or HTTP Basic. +func NewClient(httpClient *http.Client) *Client { + c := &Client{httpClient: httpClient, BaseURL: defaultBaseURL} c.Identity = &IdentityService{client: c} c.Accounts = &AccountsService{client: c} c.Certificates = &CertificatesService{client: c} @@ -126,9 +126,6 @@ func (c *Client) NewRequest(method, path string, payload interface{}) (*http.Req req.Header.Set("Content-Type", "application/json") req.Header.Add("Accept", "application/json") req.Header.Add("User-Agent", formatUserAgent(c.UserAgent)) - for key, value := range c.Credentials.Headers() { - req.Header.Add(key, value) - } return req, nil } @@ -212,7 +209,7 @@ func (c *Client) Do(req *http.Request, obj interface{}) (*http.Response, error) log.Printf("Executing request (%v): %#v", req.URL, req) } - resp, err := c.HttpClient.Do(req) + resp, err := c.httpClient.Do(req) if err != nil { return nil, err } @@ -231,7 +228,7 @@ func (c *Client) Do(req *http.Request, obj interface{}) (*http.Response, error) // the response body is decoded into v. if obj != nil { if w, ok := obj.(io.Writer); ok { - io.Copy(w, resp.Body) + _, err = io.Copy(w, resp.Body) } else { err = json.NewDecoder(resp.Body).Decode(obj) } diff --git a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/oauth.go b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/oauth.go index fc209891c..74a2b34a2 100644 --- a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/oauth.go +++ b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/oauth.go @@ -72,7 +72,7 @@ func (s *OauthService) ExchangeAuthorizationForToken(authorization *ExchangeAuth return nil, err } - resp, err := s.client.HttpClient.Do(req) + resp, err := s.client.httpClient.Do(req) if err != nil { return nil, err } diff --git a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/webhooks.go b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/webhooks.go index e6ac4194d..14263338d 100644 --- a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/webhooks.go +++ b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/webhooks.go @@ -14,7 +14,7 @@ type WebhooksService struct { // Webhook represents a DNSimple webhook. type Webhook struct { - ID int64 `json:"id,omitempty"` + ID int64 `json:"id,omitempty"` URL string `json:"url,omitempty"` } diff --git a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zone_distributions.go b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zone_distributions.go new file mode 100644 index 000000000..f92d75790 --- /dev/null +++ b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zone_distributions.go @@ -0,0 +1,46 @@ +package dnsimple + +import "fmt" + +// ZoneDistribution is the result of the zone distribution check. +type ZoneDistribution struct { + Distributed bool `json:"distributed"` +} + +// zoneDistributionResponse represents a response from an API method that returns a ZoneDistribution struct. +type zoneDistributionResponse struct { + Response + Data *ZoneDistribution `json:"data"` +} + +// CheckZoneDistribution checks if a zone is fully distributed across DNSimple nodes. +// +// See https://developer.dnsimple.com/v2/zones/#checkZoneDistribution +func (s *ZonesService) CheckZoneDistribution(accountID string, zoneName string) (*zoneDistributionResponse, error) { + path := versioned(fmt.Sprintf("/%v/zones/%v/distribution", accountID, zoneName)) + zoneDistributionResponse := &zoneDistributionResponse{} + + resp, err := s.client.get(path, zoneDistributionResponse) + if err != nil { + return nil, err + } + + zoneDistributionResponse.HttpResponse = resp + return zoneDistributionResponse, nil +} + +// CheckZoneRecordDistribution checks if a zone is fully distributed across DNSimple nodes. +// +// See https://developer.dnsimple.com/v2/zones/#checkZoneRecordDistribution +func (s *ZonesService) CheckZoneRecordDistribution(accountID string, zoneName string, recordID int64) (*zoneDistributionResponse, error) { + path := versioned(fmt.Sprintf("/%v/zones/%v/records/%v/distribution", accountID, zoneName, recordID)) + zoneDistributionResponse := &zoneDistributionResponse{} + + resp, err := s.client.get(path, zoneDistributionResponse) + if err != nil { + return nil, err + } + + zoneDistributionResponse.HttpResponse = resp + return zoneDistributionResponse, nil +} diff --git a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zones.go b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zones.go index a5f67d953..f521fcfac 100644 --- a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zones.go +++ b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zones.go @@ -14,8 +14,8 @@ type ZonesService struct { // Zone represents a Zone in DNSimple. type Zone struct { - ID int64 `json:"id,omitempty"` - AccountID int64 `json:"account_id,omitempty"` + ID int64 `json:"id,omitempty"` + AccountID int64 `json:"account_id,omitempty"` Name string `json:"name,omitempty"` Reverse bool `json:"reverse,omitempty"` CreatedAt string `json:"created_at,omitempty"` diff --git a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zones_records.go b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zones_records.go index cd017e487..fbb432d5a 100644 --- a/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zones_records.go +++ b/vendor/github.com/dnsimple/dnsimple-go/dnsimple/zones_records.go @@ -6,9 +6,9 @@ import ( // ZoneRecord represents a DNS record in DNSimple. type ZoneRecord struct { - ID int64 `json:"id,omitempty"` + ID int64 `json:"id,omitempty"` ZoneID string `json:"zone_id,omitempty"` - ParentID int64 `json:"parent_id,omitempty"` + ParentID int64 `json:"parent_id,omitempty"` Type string `json:"type,omitempty"` Name string `json:"name"` Content string `json:"content,omitempty"` diff --git a/vendor/github.com/miekg/dns/acceptfunc.go b/vendor/github.com/miekg/dns/acceptfunc.go new file mode 100644 index 000000000..fcc6104f2 --- /dev/null +++ b/vendor/github.com/miekg/dns/acceptfunc.go @@ -0,0 +1,54 @@ +package dns + +// MsgAcceptFunc is used early in the server code to accept or reject a message with RcodeFormatError. +// It returns a MsgAcceptAction to indicate what should happen with the message. +type MsgAcceptFunc func(dh Header) MsgAcceptAction + +// DefaultMsgAcceptFunc checks the request and will reject if: +// +// * isn't a request (don't respond in that case). +// * opcode isn't OpcodeQuery or OpcodeNotify +// * Zero bit isn't zero +// * has more than 1 question in the question section +// * has more than 0 RRs in the Answer section +// * has more than 0 RRs in the Authority section +// * has more than 2 RRs in the Additional section +var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc + +// MsgAcceptAction represents the action to be taken. +type MsgAcceptAction int + +const ( + MsgAccept MsgAcceptAction = iota // Accept the message + MsgReject // Reject the message with a RcodeFormatError + MsgIgnore // Ignore the error and send nothing back. +) + +var defaultMsgAcceptFunc = func(dh Header) MsgAcceptAction { + if isResponse := dh.Bits&_QR != 0; isResponse { + return MsgIgnore + } + + // Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs. + opcode := int(dh.Bits>>11) & 0xF + if opcode != OpcodeQuery && opcode != OpcodeNotify { + return MsgReject + } + + if isZero := dh.Bits&_Z != 0; isZero { + return MsgReject + } + if dh.Qdcount != 1 { + return MsgReject + } + if dh.Ancount != 0 { + return MsgReject + } + if dh.Nscount != 0 { + return MsgReject + } + if dh.Arcount > 2 { + return MsgReject + } + return MsgAccept +} diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index 856b1698d..770a946cd 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -13,16 +13,16 @@ import ( "time" ) -const dnsTimeout time.Duration = 2 * time.Second -const tcpIdleTimeout time.Duration = 8 * time.Second +const ( + dnsTimeout time.Duration = 2 * time.Second + 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 must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) - rtt time.Duration - t time.Time tsigRequestMAC string } @@ -83,33 +83,22 @@ func (c *Client) Dial(address string) (conn *Conn, err error) { // create a new dialer with the appropriate timeout var d net.Dialer if c.Dialer == nil { - d = net.Dialer{} + d = net.Dialer{Timeout: c.getTimeoutForRequest(c.dialTimeout())} } else { - d = net.Dialer(*c.Dialer) + d = *c.Dialer } - d.Timeout = c.getTimeoutForRequest(c.writeTimeout()) - network := "udp" - useTLS := false - - switch c.Net { - case "tcp-tls": - network = "tcp" - useTLS = true - case "tcp4-tls": - network = "tcp4" - useTLS = true - case "tcp6-tls": - network = "tcp6" - useTLS = true - default: - if c.Net != "" { - network = c.Net - } + network := c.Net + if network == "" { + network = "udp" } + useTLS := strings.HasPrefix(network, "tcp") && strings.HasSuffix(network, "-tls") + conn = new(Conn) if useTLS { + network = strings.TrimSuffix(network, "-tls") + conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig) } else { conn.Conn, err = d.Dial(network, address) @@ -117,6 +106,7 @@ func (c *Client) Dial(address string) (conn *Conn, err error) { if err != nil { return nil, err } + return conn, nil } @@ -177,8 +167,9 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } co.TsigSecret = c.TsigSecret + t := time.Now() // write with the appropriate write timeout - co.SetWriteDeadline(time.Now().Add(c.getTimeoutForRequest(c.writeTimeout()))) + co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout()))) if err = co.WriteMsg(m); err != nil { return nil, 0, err } @@ -188,7 +179,8 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro if err == nil && r.Id != m.Id { err = ErrId } - return r, co.rtt, err + rtt = time.Since(t) + return r, rtt, err } // ReadMsg reads a message from the connection co. @@ -240,7 +232,6 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) { } 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) @@ -248,7 +239,6 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) { p = make([]byte, MinMsgSize) } n, err = co.Read(p) - co.rtt = time.Since(co.t) } if err != nil { @@ -361,7 +351,6 @@ func (co *Conn) WriteMsg(m *Msg) (err error) { if err != nil { return err } - co.t = time.Now() if _, err = co.Write(out); err != nil { return err } @@ -497,10 +486,11 @@ func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, if deadline, ok := ctx.Deadline(); !ok { timeout = 0 } else { - timeout = deadline.Sub(time.Now()) + timeout = time.Until(deadline) } // not passing the context to the underlying calls, as the API does not support // context. For timeouts you should set up Client.Dialer and call Client.Exchange. + // TODO(tmthrgd,miekg): this is a race condition. c.Dialer = &net.Dialer{Timeout: timeout} return c.Exchange(m, a) } diff --git a/vendor/github.com/miekg/dns/compress_generate.go b/vendor/github.com/miekg/dns/compress_generate.go deleted file mode 100644 index 87fb36f68..000000000 --- a/vendor/github.com/miekg/dns/compress_generate.go +++ /dev/null @@ -1,188 +0,0 @@ -//+build ignore - -// compression_generate.go is meant to run with go generate. It will use -// go/{importer,types} to track down all the RR struct types. Then for each type -// it will look to see if there are (compressible) names, if so it will add that -// type to compressionLenHelperType and comressionLenSearchType which "fake" the -// compression so that Len() is fast. -package main - -import ( - "bytes" - "fmt" - "go/format" - "go/importer" - "go/types" - "log" - "os" -) - -var packageHdr = ` -// Code generated by "go run compress_generate.go"; DO NOT EDIT. - -package dns - -` - -// getTypeStruct will take a type and the package scope, and return the -// (innermost) struct if the type is considered a RR type (currently defined as -// those structs beginning with a RR_Header, could be redefined as implementing -// the RR interface). The bool return value indicates if embedded structs were -// resolved. -func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) { - st, ok := t.Underlying().(*types.Struct) - if !ok { - return nil, false - } - if st.Field(0).Type() == scope.Lookup("RR_Header").Type() { - return st, false - } - if st.Field(0).Anonymous() { - st, _ := getTypeStruct(st.Field(0).Type(), scope) - return st, true - } - return nil, false -} - -func main() { - // Import and type-check the package - pkg, err := importer.Default().Import("github.com/miekg/dns") - fatalIfErr(err) - scope := pkg.Scope() - - var domainTypes []string // Types that have a domain name in them (either compressible or not). - var cdomainTypes []string // Types that have a compressible domain name in them (subset of domainType) -Names: - for _, name := range scope.Names() { - o := scope.Lookup(name) - if o == nil || !o.Exported() { - continue - } - st, _ := getTypeStruct(o.Type(), scope) - if st == nil { - continue - } - if name == "PrivateRR" { - continue - } - - if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" { - log.Fatalf("Constant Type%s does not exist.", o.Name()) - } - - for i := 1; i < st.NumFields(); i++ { - if _, ok := st.Field(i).Type().(*types.Slice); ok { - if st.Tag(i) == `dns:"domain-name"` { - domainTypes = append(domainTypes, o.Name()) - continue Names - } - if st.Tag(i) == `dns:"cdomain-name"` { - cdomainTypes = append(cdomainTypes, o.Name()) - domainTypes = append(domainTypes, o.Name()) - continue Names - } - continue - } - - switch { - case st.Tag(i) == `dns:"domain-name"`: - domainTypes = append(domainTypes, o.Name()) - continue Names - case st.Tag(i) == `dns:"cdomain-name"`: - cdomainTypes = append(cdomainTypes, o.Name()) - domainTypes = append(domainTypes, o.Name()) - continue Names - } - } - } - - b := &bytes.Buffer{} - b.WriteString(packageHdr) - - // compressionLenHelperType - all types that have domain-name/cdomain-name can be used for compressing names - - fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR) {\n") - fmt.Fprint(b, "switch x := r.(type) {\n") - for _, name := range domainTypes { - o := scope.Lookup(name) - st, _ := getTypeStruct(o.Type(), scope) - - fmt.Fprintf(b, "case *%s:\n", name) - for i := 1; i < st.NumFields(); i++ { - out := func(s string) { fmt.Fprintf(b, "compressionLenHelper(c, x.%s)\n", st.Field(i).Name()) } - - if _, ok := st.Field(i).Type().(*types.Slice); ok { - switch st.Tag(i) { - case `dns:"domain-name"`: - fallthrough - case `dns:"cdomain-name"`: - // For HIP we need to slice over the elements in this slice. - fmt.Fprintf(b, `for i := range x.%s { - compressionLenHelper(c, x.%s[i]) - } -`, st.Field(i).Name(), st.Field(i).Name()) - } - continue - } - - switch { - case st.Tag(i) == `dns:"cdomain-name"`: - fallthrough - case st.Tag(i) == `dns:"domain-name"`: - out(st.Field(i).Name()) - } - } - } - fmt.Fprintln(b, "}\n}\n\n") - - // compressionLenSearchType - search cdomain-tags types for compressible names. - - fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool) {\n") - fmt.Fprint(b, "switch x := r.(type) {\n") - for _, name := range cdomainTypes { - o := scope.Lookup(name) - st, _ := getTypeStruct(o.Type(), scope) - - fmt.Fprintf(b, "case *%s:\n", name) - j := 1 - for i := 1; i < st.NumFields(); i++ { - out := func(s string, j int) { - fmt.Fprintf(b, "k%d, ok%d := compressionLenSearch(c, x.%s)\n", j, j, st.Field(i).Name()) - } - - // There are no slice types with names that can be compressed. - - switch { - case st.Tag(i) == `dns:"cdomain-name"`: - out(st.Field(i).Name(), j) - j++ - } - } - k := "k1" - ok := "ok1" - for i := 2; i < j; i++ { - k += fmt.Sprintf(" + k%d", i) - ok += fmt.Sprintf(" && ok%d", i) - } - fmt.Fprintf(b, "return %s, %s\n", k, ok) - } - fmt.Fprintln(b, "}\nreturn 0, false\n}\n\n") - - // gofmt - res, err := format.Source(b.Bytes()) - if err != nil { - b.WriteTo(os.Stderr) - log.Fatal(err) - } - - f, err := os.Create("zcompress.go") - fatalIfErr(err) - defer f.Close() - f.Write(res) -} - -func fatalIfErr(err error) { - if err != nil { - log.Fatal(err) - } -} diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index 14e18b0b3..1778b1cad 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -166,7 +166,7 @@ func (dns *Msg) IsEdns0() *OPT { // 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) + _, labels, err := packDomainName(s, nil, 0, compressionMap{}, false) return labels, err == nil } diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go index 5133eac72..aefffa793 100644 --- a/vendor/github.com/miekg/dns/dns.go +++ b/vendor/github.com/miekg/dns/dns.go @@ -34,10 +34,15 @@ type RR interface { // copy returns a copy of the RR copy() RR - // len returns the length (in octets) of the uncompressed RR in wire format. - len() int + + // len returns the length (in octets) of the compressed or uncompressed RR in wire format. + // + // If compression is nil, the uncompressed size will be returned, otherwise the compressed + // size will be returned and domain names will be added to the map for future compression. + len(off int, compression map[string]struct{}) int + // pack packs an RR into wire format. - pack([]byte, int, map[string]int, bool) (int, error) + pack(msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) } // RR_Header is the header all DNS resource records share. @@ -55,16 +60,6 @@ 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 @@ -80,28 +75,29 @@ func (h *RR_Header) String() string { return s } -func (h *RR_Header) len() int { - l := len(h.Name) + 1 +func (h *RR_Header) len(off int, compression map[string]struct{}) int { + l := domainNameLen(h.Name, off, compression, true) 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) + buf := make([]byte, Len(r)*2) + headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, 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)) + hdr := *r.Header() + hdr.Rdlength = uint16(off - headerEnd) + + rfc3597, _, err := unpackRFC3597(hdr, buf, headerEnd) if err != nil { return err } + *rr = *rfc3597.(*RFC3597) return nil } diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go index ac9fdd45e..9b39d4273 100644 --- a/vendor/github.com/miekg/dns/dnssec.go +++ b/vendor/github.com/miekg/dns/dnssec.go @@ -73,6 +73,7 @@ var StringToAlgorithm = reverseInt8(AlgorithmToString) // AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's. var AlgorithmToHash = map[uint8]crypto.Hash{ RSAMD5: crypto.MD5, // Deprecated in RFC 6725 + DSA: crypto.SHA1, RSASHA1: crypto.SHA1, RSASHA1NSEC3SHA1: crypto.SHA1, RSASHA256: crypto.SHA256, @@ -172,7 +173,7 @@ func (k *DNSKEY) KeyTag() uint16 { keytag += int(v) << 8 } } - keytag += (keytag >> 16) & 0xFFFF + keytag += keytag >> 16 & 0xFFFF keytag &= 0xFFFF } return uint16(keytag) @@ -239,7 +240,7 @@ func (k *DNSKEY) ToDS(h uint8) *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 = k.Hdr c.Hdr.Rrtype = TypeCDNSKEY return c } @@ -247,7 +248,7 @@ func (k *DNSKEY) ToCDNSKEY() *CDNSKEY { // 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 = d.Hdr c.Hdr.Rrtype = TypeCDS return c } @@ -400,7 +401,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { if rr.Algorithm != k.Algorithm { return ErrKey } - if strings.ToLower(rr.SignerName) != strings.ToLower(k.Hdr.Name) { + if !strings.EqualFold(rr.SignerName, k.Hdr.Name) { return ErrKey } if k.Protocol != 3 { @@ -511,8 +512,8 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool { } modi := (int64(rr.Inception) - utc) / year68 mode := (int64(rr.Expiration) - utc) / year68 - ti := int64(rr.Inception) + (modi * year68) - te := int64(rr.Expiration) + (mode * year68) + ti := int64(rr.Inception) + modi*year68 + te := int64(rr.Expiration) + mode*year68 return ti <= utc && utc <= te } @@ -532,6 +533,11 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey { return nil } + if len(keybuf) < 1+1+64 { + // Exponent must be at least 1 byte and modulus at least 64 + 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 @@ -541,25 +547,36 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey { explen = uint16(keybuf[1])<<8 | uint16(keybuf[2]) keyoff = 3 } + + if explen > 4 || explen == 0 || keybuf[keyoff] == 0 { + // Exponent larger than supported by the crypto package, + // empty, or contains prohibited leading zero. + return nil + } + + modoff := keyoff + int(explen) + modlen := len(keybuf) - modoff + if modlen < 64 || modlen > 512 || keybuf[modoff] == 0 { + // Modulus is too small, large, or contains prohibited leading zero. + return nil + } + 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 + for i := 0; i < int(explen); i++ { + expo <<= 8 + expo |= uint64(keybuf[keyoff+i]) } - // Remainder - expo += uint64(keybuf[keyoff]) - if expo > (2<<31)+1 { - // Larger expo than supported. - // println("dns: F5 primes (or larger) are not supported") + if expo > 1<<31-1 { + // Larger exponent than supported by the crypto package. return nil } pubkey.E = int(expo) - pubkey.N.SetBytes(keybuf[keyoff+int(explen):]) + pubkey.N = big.NewInt(0) + pubkey.N.SetBytes(keybuf[modoff:]) + return pubkey } @@ -707,7 +724,7 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { x.Target = strings.ToLower(x.Target) } // 6.2. Canonical RR Form. (5) - origTTL - wire := make([]byte, r1.len()+1) // +1 to be safe(r) + wire := make([]byte, Len(r1)+1) // +1 to be safe(r) off, err1 := PackRR(r1, wire, 0, nil, false) if err1 != nil { return nil, err1 diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go index e2d9d8f92..5e6542230 100644 --- a/vendor/github.com/miekg/dns/dnssec_keyscan.go +++ b/vendor/github.com/miekg/dns/dnssec_keyscan.go @@ -1,7 +1,7 @@ package dns import ( - "bytes" + "bufio" "crypto" "crypto/dsa" "crypto/ecdsa" @@ -181,22 +181,10 @@ func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) { if err != nil { return nil, err } - if len(p1) != 32 { + if len(p1) != ed25519.SeedSize { return nil, ErrPrivKey } - // RFC 8080 and Golang's x/crypto/ed25519 differ as to how the - // private keys are represented. RFC 8080 specifies that private - // keys be stored solely as the seed value (p1 above) while the - // ed25519 package represents them as the seed value concatenated - // to the public key, which is derived from the seed value. - // - // ed25519.GenerateKey reads exactly 32 bytes from the passed in - // io.Reader and uses them as the seed. It also derives the - // public key and produces a compatible private key. - _, p, err = ed25519.GenerateKey(bytes.NewReader(p1)) - if err != nil { - return nil, err - } + p = ed25519.NewKeyFromSeed(p1) case "created", "publish", "activate": /* not used in Go (yet) */ } @@ -207,23 +195,12 @@ func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) { // 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, cancel := scanInit(r) m := make(map[string]string) - c := make(chan lex) - k := "" - defer func() { - cancel() - // zlexer can send up to two tokens, the next one and possibly 1 remainders. - // Do a non-blocking read. - _, ok := <-c - _, ok = <-c - if !ok { - // too bad - } - }() - // Start the lexer - go klexer(s, c) - for l := range c { + var k string + + c := newKLexer(r) + + for l, ok := c.Next(); ok; l, ok = c.Next() { // It should alternate switch l.value { case zKey: @@ -232,41 +209,111 @@ func parseKey(r io.Reader, file string) (map[string]string, error) { 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 = "" } } + + // Surface any read errors from r. + if err := c.Err(); err != nil { + return nil, &ParseError{file: file, err: err.Error()} + } + 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 +type klexer struct { + br io.ByteReader + + readErr error + + line int + column int + + key bool + + eol bool // end-of-line +} + +func newKLexer(r io.Reader) *klexer { + br, ok := r.(io.ByteReader) + if !ok { + br = bufio.NewReaderSize(r, 1024) + } + + return &klexer{ + br: br, + + line: 1, + + key: true, + } +} + +func (kl *klexer) Err() error { + if kl.readErr == io.EOF { + return nil + } + + return kl.readErr +} + +// readByte returns the next byte from the input +func (kl *klexer) readByte() (byte, bool) { + if kl.readErr != nil { + return 0, false + } + + c, err := kl.br.ReadByte() + if err != nil { + kl.readErr = err + return 0, false + } + + // delay the newline handling until the next token is delivered, + // fixes off-by-one errors when reporting a parse error. + if kl.eol { + kl.line++ + kl.column = 0 + kl.eol = false + } + + if c == '\n' { + kl.eol = true + } else { + kl.column++ + } + + return c, true +} + +func (kl *klexer) Next() (lex, bool) { + var ( + l lex + + str strings.Builder + + commt bool + ) + + for x, ok := kl.readByte(); ok; x, ok = kl.readByte() { + l.line, l.column = kl.line, kl.column + switch x { case ':': - if commt { + if commt || !kl.key { 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 - } + + kl.key = false + + // Next token is a space, eat it + kl.readByte() + + l.value = zKey + l.token = str.String() + return l, true case ';': commt = true case '\n': @@ -274,24 +321,32 @@ func klexer(s *scan, c chan lex) { // Reset a comment commt = false } + + kl.key = true + l.value = zValue - l.token = str - c <- l - str = "" - commt = false - key = true + l.token = str.String() + return l, true default: if commt { break } - str += string(x) + + str.WriteByte(x) } - x, err = s.tokenText() } - if len(str) > 0 { + + if kl.readErr != nil && kl.readErr != io.EOF { + // Don't return any tokens after a read error occurs. + return lex{value: zEOF}, false + } + + if str.Len() > 0 { // Send remainder - l.token = str l.value = zValue - c <- l + l.token = str.String() + return l, true } + + return lex{value: zEOF}, false } diff --git a/vendor/github.com/miekg/dns/dnssec_privkey.go b/vendor/github.com/miekg/dns/dnssec_privkey.go index 46f3215c8..0c65be17b 100644 --- a/vendor/github.com/miekg/dns/dnssec_privkey.go +++ b/vendor/github.com/miekg/dns/dnssec_privkey.go @@ -82,7 +82,7 @@ func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string { "Public_value(y): " + pub + "\n" case ed25519.PrivateKey: - private := toBase64(p[:32]) + private := toBase64(p.Seed()) return format + "Algorithm: " + algorithm + "\n" + "PrivateKey: " + private + "\n" diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index 1d8114744..d3d7cec9e 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -1,20 +1,20 @@ /* 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 sent out to the DNS. The package -API follows the less-is-more principle, by presenting a small, clean interface. +Both server- and client-side programming is supported. The package allows +complete control over what is sent out to the DNS. The API follows the +less-is-more principle, by presenting a small, clean interface. -The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers, +It 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 + +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: +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.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} r.Preference = 10 r.Mx = "mx.miek.nl." @@ -30,8 +30,8 @@ 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: +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) @@ -40,8 +40,8 @@ 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 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: @@ -51,9 +51,8 @@ The following is slightly more verbose, but more flexible: m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} -After creating a message it can be sent. -Basic use pattern for synchronous querying the DNS at a -server configured on 127.0.0.1 and port 53: +After creating a message it can be sent. 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") @@ -73,11 +72,11 @@ and port to use for the connection: Port: 12345, Zone: "", } - d := net.Dialer{ + c.Dialer := &net.Dialer{ Timeout: 200 * time.Millisecond, LocalAddr: &laddr, } - in, rtt, err := c.ExchangeWithDialer(&d, m1, "8.8.8.8:53") + in, rtt, err := c.Exchange(m1, "8.8.8.8:53") If these "advanced" features are not needed, a simple UDP query can be sent, with: @@ -99,25 +98,24 @@ the Answer section: 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. +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. +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. +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. +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. +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) @@ -126,9 +124,9 @@ Signature generation, signature verification and key generation are all supporte 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. +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 @@ -145,10 +143,9 @@ DNS function shows which functions exist to specify the prerequisites. 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. +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 @@ -181,10 +178,10 @@ changes to the RRset after calling SetTsig() the signature will be incorrect. ... // 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: +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) @@ -194,8 +191,8 @@ and using the server 176.58.119.54: 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. +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. @@ -220,29 +217,30 @@ Basic use pattern validating and replying to a message that has TSIG set. 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 +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 +See https://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 +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: +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 { @@ -262,10 +260,9 @@ From RFC 2931: ... 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. +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. */ diff --git a/vendor/github.com/miekg/dns/duplicate.go b/vendor/github.com/miekg/dns/duplicate.go new file mode 100644 index 000000000..6372e8a19 --- /dev/null +++ b/vendor/github.com/miekg/dns/duplicate.go @@ -0,0 +1,25 @@ +package dns + +//go:generate go run duplicate_generate.go + +// IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL. +// So this means the header data is equal *and* the RDATA is the same. Return true +// is so, otherwise false. +// It's is a protocol violation to have identical RRs in a message. +func IsDuplicate(r1, r2 RR) bool { + if r1.Header().Class != r2.Header().Class { + return false + } + if r1.Header().Rrtype != r2.Header().Rrtype { + return false + } + if !isDulicateName(r1.Header().Name, r2.Header().Name) { + return false + } + // ignore TTL + + return isDuplicateRdata(r1, r2) +} + +// isDulicateName checks if the domain names s1 and s2 are equal. +func isDulicateName(s1, s2 string) bool { return equal(s1, s2) } diff --git a/vendor/github.com/miekg/dns/duplicate_generate.go b/vendor/github.com/miekg/dns/duplicate_generate.go new file mode 100644 index 000000000..83ac1cf77 --- /dev/null +++ b/vendor/github.com/miekg/dns/duplicate_generate.go @@ -0,0 +1,158 @@ +//+build ignore + +// types_generate.go is meant to run with go generate. It will use +// go/{importer,types} to track down all the RR struct types. Then for each type +// it will generate conversion tables (TypeToRR and TypeToString) and banal +// methods (len, Header, copy) based on the struct tags. The generated source is +// written to ztypes.go, and is meant to be checked into git. +package main + +import ( + "bytes" + "fmt" + "go/format" + "go/importer" + "go/types" + "log" + "os" +) + +var packageHdr = ` +// Code generated by "go run duplicate_generate.go"; DO NOT EDIT. + +package dns + +` + +func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) { + st, ok := t.Underlying().(*types.Struct) + if !ok { + return nil, false + } + if st.Field(0).Type() == scope.Lookup("RR_Header").Type() { + return st, false + } + if st.Field(0).Anonymous() { + st, _ := getTypeStruct(st.Field(0).Type(), scope) + return st, true + } + return nil, false +} + +func main() { + // Import and type-check the package + pkg, err := importer.Default().Import("github.com/miekg/dns") + fatalIfErr(err) + scope := pkg.Scope() + + // Collect actual types (*X) + var namedTypes []string + for _, name := range scope.Names() { + o := scope.Lookup(name) + if o == nil || !o.Exported() { + continue + } + + if st, _ := getTypeStruct(o.Type(), scope); st == nil { + continue + } + + if name == "PrivateRR" || name == "RFC3597" { + continue + } + if name == "OPT" || name == "ANY" || name == "IXFR" || name == "AXFR" { + continue + } + + namedTypes = append(namedTypes, o.Name()) + } + + b := &bytes.Buffer{} + b.WriteString(packageHdr) + + // Generate the giant switch that calls the correct function for each type. + fmt.Fprint(b, "// isDuplicateRdata calls the rdata specific functions\n") + fmt.Fprint(b, "func isDuplicateRdata(r1, r2 RR) bool {\n") + fmt.Fprint(b, "switch r1.Header().Rrtype {\n") + + for _, name := range namedTypes { + + o := scope.Lookup(name) + _, isEmbedded := getTypeStruct(o.Type(), scope) + if isEmbedded { + continue + } + fmt.Fprintf(b, "case Type%s:\nreturn isDuplicate%s(r1.(*%s), r2.(*%s))\n", name, name, name, name) + } + fmt.Fprintf(b, "}\nreturn false\n}\n") + + // Generate the duplicate check for each type. + fmt.Fprint(b, "// isDuplicate() functions\n\n") + for _, name := range namedTypes { + + o := scope.Lookup(name) + st, isEmbedded := getTypeStruct(o.Type(), scope) + if isEmbedded { + continue + } + fmt.Fprintf(b, "func isDuplicate%s(r1, r2 *%s) bool {\n", name, name) + for i := 1; i < st.NumFields(); i++ { + field := st.Field(i).Name() + o2 := func(s string) { fmt.Fprintf(b, s+"\n", field, field) } + o3 := func(s string) { fmt.Fprintf(b, s+"\n", field, field, field) } + + // For some reason, a and aaaa don't pop up as *types.Slice here (mostly like because the are + // *indirectly* defined as a slice in the net package). + if _, ok := st.Field(i).Type().(*types.Slice); ok || st.Tag(i) == `dns:"a"` || st.Tag(i) == `dns:"aaaa"` { + o2("if len(r1.%s) != len(r2.%s) {\nreturn false\n}") + + if st.Tag(i) == `dns:"cdomain-name"` || st.Tag(i) == `dns:"domain-name"` { + o3(`for i := 0; i < len(r1.%s); i++ { + if !isDulicateName(r1.%s[i], r2.%s[i]) { + return false + } + }`) + + continue + } + + o3(`for i := 0; i < len(r1.%s); i++ { + if r1.%s[i] != r2.%s[i] { + return false + } + }`) + + continue + } + + switch st.Tag(i) { + case `dns:"-"`: + // ignored + case `dns:"cdomain-name"`, `dns:"domain-name"`: + o2("if !isDulicateName(r1.%s, r2.%s) {\nreturn false\n}") + default: + o2("if r1.%s != r2.%s {\nreturn false\n}") + } + } + fmt.Fprintf(b, "return true\n}\n\n") + } + + // gofmt + res, err := format.Source(b.Bytes()) + if err != nil { + b.WriteTo(os.Stderr) + log.Fatal(err) + } + + // write result + f, err := os.Create("zduplicate.go") + fatalIfErr(err) + defer f.Close() + f.Write(res) +} + +func fatalIfErr(err error) { + if err != nil { + log.Fatal(err) + } +} diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index 6f9d2ea39..f8c60e616 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -78,8 +78,8 @@ func (rr *OPT) String() string { return s } -func (rr *OPT) len() int { - l := rr.Hdr.len() +func (rr *OPT) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) 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() @@ -92,22 +92,24 @@ func (rr *OPT) len() int { // Version returns the EDNS version used. Only zero is defined. func (rr *OPT) Version() uint8 { - return uint8((rr.Hdr.Ttl & 0x00FF0000) >> 16) + 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) + 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) + return int(rr.Hdr.Ttl&0xFF000000>>24) << 4 } // SetExtendedRcode sets the EDNS extended RCODE field. -func (rr *OPT) SetExtendedRcode(v uint8) { - rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 24) +// +// If the RCODE is not an extended RCODE, will reset the extended RCODE field to 0. +func (rr *OPT) SetExtendedRcode(v uint16) { + rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v>>4)<<24 } // UDPSize returns the UDP buffer size. @@ -271,22 +273,16 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error { 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]) + addr := make(net.IP, net.IPv4len) + copy(addr, b[4:]) + e.Address = addr.To16() 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]} + addr := make(net.IP, net.IPv6len) + copy(addr, b[4:]) + e.Address = addr default: return errors.New("dns: bad address family") } diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go index e4481a4b0..97bc39f58 100644 --- a/vendor/github.com/miekg/dns/generate.go +++ b/vendor/github.com/miekg/dns/generate.go @@ -2,8 +2,8 @@ package dns import ( "bytes" - "errors" "fmt" + "io" "strconv" "strings" ) @@ -18,142 +18,225 @@ import ( // * 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 { +func (zp *ZoneParser) generate(l lex) (RR, bool) { + token := l.token step := 1 - if i := strings.IndexAny(l.token, "/"); i != -1 { - if i+1 == len(l.token) { - return "bad step in $GENERATE range" + if i := strings.IndexByte(token, '/'); i >= 0 { + if i+1 == len(token) { + return zp.setParseError("bad step in $GENERATE range", l) } - 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" + + s, err := strconv.Atoi(token[i+1:]) + if err != nil || s <= 0 { + return zp.setParseError("bad step in $GENERATE range", l) } - l.token = l.token[:i] + + step = s + token = token[:i] } - sx := strings.SplitN(l.token, "-", 2) + + sx := strings.SplitN(token, "-", 2) if len(sx) != 2 { - return "bad start-stop in $GENERATE range" + return zp.setParseError("bad start-stop in $GENERATE range", l) } + start, err := strconv.Atoi(sx[0]) if err != nil { - return "bad start in $GENERATE range" + return zp.setParseError("bad start in $GENERATE range", l) } + end, err := strconv.Atoi(sx[1]) if err != nil { - return "bad stop in $GENERATE range" + return zp.setParseError("bad stop in $GENERATE range", l) } if end < 0 || start < 0 || end < start { - return "bad range in $GENERATE range" + return zp.setParseError("bad range in $GENERATE range", l) } - <-c // _BLANK + zp.c.Next() // _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 - ) + var s string + for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() { + if l.err { + return zp.setParseError("bad data in $GENERATE directive", l) + } + if l.value == zNewline { + break + } - 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? + s += l.token + } + + r := &generateReader{ + s: s, + + cur: start, + start: start, + end: end, + step: step, + + file: zp.file, + lex: &l, + } + zp.sub = NewZoneParser(r, zp.origin, zp.file) + zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed + zp.sub.SetDefaultTTL(defaultTtl) + return zp.subNext() +} + +type generateReader struct { + s string + si int + + cur int + start int + end int + step int + + mod bytes.Buffer + + escape bool + + eof bool + + file string + lex *lex +} + +func (r *generateReader) parseError(msg string, end int) *ParseError { + r.eof = true // Make errors sticky. + + l := *r.lex + l.token = r.s[r.si-1 : end] + l.column += r.si // l.column starts one zBLANK before r.s + + return &ParseError{r.file, msg, l} +} + +func (r *generateReader) Read(p []byte) (int, error) { + // NewZLexer, through NewZoneParser, should use ReadByte and + // not end up here. + + panic("not implemented") +} + +func (r *generateReader) ReadByte() (byte, error) { + if r.eof { + return 0, io.EOF + } + if r.mod.Len() > 0 { + return r.mod.ReadByte() + } + + if r.si >= len(r.s) { + r.si = 0 + r.cur += r.step + + r.eof = r.cur > r.end || r.cur < 0 + return '\n', nil + } + + si := r.si + r.si++ + + switch r.s[si] { + case '\\': + if r.escape { + r.escape = false + return '\\', nil + } + + r.escape = true + return r.ReadByte() + case '$': + if r.escape { + r.escape = false + return '$', nil + } + + mod := "%d" + + if si >= len(r.s)-1 { + // End of the string + fmt.Fprintf(&r.mod, mod, r.cur) + return r.mod.ReadByte() + } + + if r.s[si+1] == '$' { + r.si++ + return '$', nil + } + + var offset int + + // Search for { and } + if r.s[si+1] == '{' { + // Modifier block + sep := strings.Index(r.s[si+2:], "}") + if sep < 0 { + return 0, r.parseError("bad modifier in $GENERATE", len(r.s)) + } + + var errMsg string + mod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep]) + if errMsg != "" { + return 0, r.parseError(errMsg, si+3+sep) + } + if r.start+offset < 0 || r.end+offset > 1<<31-1 { + return 0, r.parseError("bad offset in $GENERATE", si+3+sep) + } + + r.si += 2 + sep // Jump to it + } + + fmt.Fprintf(&r.mod, mod, r.cur+offset) + return r.mod.ReadByte() + default: + if r.escape { // Pretty useless here + r.escape = false + return r.ReadByte() + } + + return r.s[si], nil } - 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") +func modToPrintf(s string) (string, int, string) { + // Modifier is { offset [ ,width [ ,base ] ] } - provide default + // values for optional width and type, if necessary. + var offStr, widthStr, base string + switch xs := strings.Split(s, ","); len(xs) { + case 1: + offStr, widthStr, base = xs[0], "0", "d" + case 2: + offStr, widthStr, base = xs[0], xs[1], "d" + case 3: + offStr, widthStr, base = xs[0], xs[1], xs[2] + default: + return "", 0, "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") + + switch base { + case "o", "d", "x", "X": + default: + return "", 0, "bad base in $GENERATE" } - offset, err := strconv.Atoi(xs[0]) - if err != nil || offset > 255 { - return "", 0, errors.New("bad offset in $GENERATE") + + offset, err := strconv.Atoi(offStr) + if err != nil { + return "", 0, "bad offset in $GENERATE" } - width, err := strconv.Atoi(xs[1]) - if err != nil || width > 255 { - return "", offset, errors.New("bad width in $GENERATE") + + width, err := strconv.Atoi(widthStr) + if err != nil || width < 0 || width > 255 { + return "", 0, "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 + + if width == 0 { + return "%" + base, offset, "" } - return "%0" + xs[1] + xs[2], offset, nil + + return "%0" + widthStr + base, offset, "" } diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index 760b89e71..577fc59d2 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -178,10 +178,10 @@ func equal(a, b string) bool { ai := a[i] bi := b[i] if ai >= 'A' && ai <= 'Z' { - ai |= ('a' - 'A') + ai |= 'a' - 'A' } if bi >= 'A' && bi <= 'Z' { - bi |= ('a' - 'A') + bi |= 'a' - 'A' } if ai != bi { return false diff --git a/vendor/github.com/miekg/dns/listen_go111.go b/vendor/github.com/miekg/dns/listen_go111.go new file mode 100644 index 000000000..fad195cfe --- /dev/null +++ b/vendor/github.com/miekg/dns/listen_go111.go @@ -0,0 +1,44 @@ +// +build go1.11 +// +build aix darwin dragonfly freebsd linux netbsd openbsd + +package dns + +import ( + "context" + "net" + "syscall" + + "golang.org/x/sys/unix" +) + +const supportsReusePort = true + +func reuseportControl(network, address string, c syscall.RawConn) error { + var opErr error + err := c.Control(func(fd uintptr) { + opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + }) + if err != nil { + return err + } + + return opErr +} + +func listenTCP(network, addr string, reuseport bool) (net.Listener, error) { + var lc net.ListenConfig + if reuseport { + lc.Control = reuseportControl + } + + return lc.Listen(context.Background(), network, addr) +} + +func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) { + var lc net.ListenConfig + if reuseport { + lc.Control = reuseportControl + } + + return lc.ListenPacket(context.Background(), network, addr) +} diff --git a/vendor/github.com/miekg/dns/listen_go_not111.go b/vendor/github.com/miekg/dns/listen_go_not111.go new file mode 100644 index 000000000..b9201417a --- /dev/null +++ b/vendor/github.com/miekg/dns/listen_go_not111.go @@ -0,0 +1,23 @@ +// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd + +package dns + +import "net" + +const supportsReusePort = false + +func listenTCP(network, addr string, reuseport bool) (net.Listener, error) { + if reuseport { + // TODO(tmthrgd): return an error? + } + + return net.Listen(network, addr) +} + +func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) { + if reuseport { + // TODO(tmthrgd): return an error? + } + + return net.ListenPacket(network, addr) +} diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index 975dde781..33f14c489 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -9,7 +9,6 @@ package dns //go:generate go run msg_generate.go -//go:generate go run compress_generate.go import ( crand "crypto/rand" @@ -18,12 +17,35 @@ import ( "math/big" "math/rand" "strconv" + "strings" "sync" ) const ( maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer maxDomainNameWireOctets = 255 // See RFC 1035 section 2.3.4 + + // This is the maximum number of compression pointers that should occur in a + // semantically valid message. Each label in a domain name must be at least one + // octet and is separated by a period. The root label won't be represented by a + // compression pointer to a compression pointer, hence the -2 to exclude the + // smallest valid root label. + // + // It is possible to construct a valid message that has more compression pointers + // than this, and still doesn't loop, by pointing to a previous pointer. This is + // not something a well written implementation should ever do, so we leave them + // to trip the maximum compression pointer check. + maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2 + + // This is the maximum length of a domain name in presentation format. The + // maximum wire length of a domain name is 255 octets (see above), with the + // maximum label length being 63. The wire format requires one extra byte over + // the presentation format, reducing the number of octets by 1. Each label in + // the name will be separated by a single period, with each octet in the label + // expanding to at most 4 bytes (\DDD). If all other labels are of the maximum + // length, then the final label can only be 61 octets long to not exceed the + // maximum allowed wire length. + maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1 ) // Errors defined in this package. @@ -46,10 +68,9 @@ var ( ErrRRset error = &Error{err: "bad rrset"} ErrSecret error = &Error{err: "no secrets defined"} ErrShortRead error = &Error{err: "short read"} - ErrSig error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated. - ErrSoa error = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers. - ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. - ErrTruncated error = &Error{err: "failed to unpack truncated message"} // ErrTruncated indicates that we failed to unpack a truncated message. We unpacked as much as we had so Msg can still be used, if desired. + ErrSig error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated. + ErrSoa error = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers. + ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. ) // Id by default, returns a 16 bits random number to be used as a @@ -151,7 +172,7 @@ var RcodeToString = map[int]string{ RcodeFormatError: "FORMERR", RcodeServerFailure: "SERVFAIL", RcodeNameError: "NXDOMAIN", - RcodeNotImplemented: "NOTIMPL", + RcodeNotImplemented: "NOTIMP", RcodeRefused: "REFUSED", RcodeYXDomain: "YXDOMAIN", // See RFC 2136 RcodeYXRrset: "YXRRSET", @@ -169,6 +190,39 @@ var RcodeToString = map[int]string{ RcodeBadCookie: "BADCOOKIE", } +// compressionMap is used to allow a more efficient compression map +// to be used for internal packDomainName calls without changing the +// signature or functionality of public API. +// +// In particular, map[string]uint16 uses 25% less per-entry memory +// than does map[string]int. +type compressionMap struct { + ext map[string]int // external callers + int map[string]uint16 // internal callers +} + +func (m compressionMap) valid() bool { + return m.int != nil || m.ext != nil +} + +func (m compressionMap) insert(s string, pos int) { + if m.ext != nil { + m.ext[s] = pos + } else { + m.int[s] = uint16(pos) + } +} + +func (m compressionMap) find(s string) (int, bool) { + if m.ext != nil { + pos, ok := m.ext[s] + return pos, ok + } + + pos, ok := m.int[s] + return int(pos), ok +} + // Domain names are a sequence of counted strings // split at the dots. They end with a zero-length string. @@ -177,143 +231,168 @@ var RcodeToString = map[int]string{ // map needs to hold a mapping between domain names and offsets // pointing into msg. func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { - off1, _, err = packDomainName(s, msg, off, compression, compress) + off1, _, err = packDomainName(s, msg, off, compressionMap{ext: compression}, compress) return } -func packDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, labels int, err error) { +func packDomainName(s string, msg []byte, off int, compression compressionMap, compress bool) (off1 int, labels int, err error) { // special case if msg == nil lenmsg := 256 if msg != nil { lenmsg = len(msg) } + ls := len(s) if ls == 0 { // Ok, for instance when dealing with update RR without any rdata. return off, 0, nil } - // If not fully qualified, error out, but only if msg == nil #ugly - switch { - case msg == nil: - if s[ls-1] != '.' { - s += "." - ls++ - } - case msg != nil: - if s[ls-1] != '.' { + + // If not fully qualified, error out, but only if msg != nil #ugly + if s[ls-1] != '.' { + if msg != nil { return lenmsg, 0, ErrFqdn } + s += "." + ls++ } + // Each dot ends a segment of the name. // We trade each dot byte for a length byte. // Except for escaped dots (\.), which are normal dots. // There is also a trailing zero. // Compression - nameoffset := -1 pointer := -1 + // Emit sequence of counted strings, chopping at dots. - begin := 0 - bs := []byte(s) - roBs, bsFresh, escapedDot := s, true, false + var ( + begin int + compBegin int + compOff int + bs []byte + wasDot bool + ) +loop: for i := 0; i < ls; i++ { - if bs[i] == '\\' { - for j := i; j < ls-1; j++ { - bs[j] = bs[j+1] - } - ls-- + var c byte + if bs == nil { + c = s[i] + } else { + c = bs[i] + } + + switch c { + case '\\': if off+1 > lenmsg { return lenmsg, labels, ErrBuf } - // check for \DDD - if i+2 < ls && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { - bs[i] = dddToByte(bs[i:]) - for j := i + 1; j < ls-2; j++ { - bs[j] = bs[j+2] - } - ls -= 2 - } - escapedDot = bs[i] == '.' - bsFresh = false - continue - } - if bs[i] == '.' { - if i > 0 && bs[i-1] == '.' && !escapedDot { + if bs == nil { + bs = []byte(s) + } + + // check for \DDD + if i+3 < ls && isDigit(bs[i+1]) && isDigit(bs[i+2]) && isDigit(bs[i+3]) { + bs[i] = dddToByte(bs[i+1:]) + copy(bs[i+1:ls-3], bs[i+4:]) + ls -= 3 + compOff += 3 + } else { + copy(bs[i:ls-1], bs[i+1:]) + ls-- + compOff++ + } + + wasDot = false + case '.': + if wasDot { // two dots back to back is not legal return lenmsg, labels, ErrRdata } - if i-begin >= 1<<6 { // top two bits of length must be clear + wasDot = true + + labelLen := i - begin + if labelLen >= 1<<6 { // top two bits of length must be clear return lenmsg, labels, ErrRdata } + // off can already (we're in a loop) be bigger than len(msg) // this happens when a name isn't fully qualified - if off+1 > lenmsg { + if off+1+labelLen > lenmsg { return lenmsg, labels, ErrBuf } - if msg != nil { - msg[off] = byte(i - begin) - } - offset := off - off++ - for j := begin; j < i; j++ { - if off+1 > lenmsg { - return lenmsg, labels, ErrBuf - } - if msg != nil { - msg[off] = bs[j] - } - off++ - } - if compress && !bsFresh { - roBs = string(bs) - bsFresh = true - } + // Don't try to compress '.' - // We should only compress when compress it true, but we should also still pick + // We should only compress when compress is true, but we should also still pick // up names that can be used for *future* compression(s). - if compression != nil && roBs[begin:] != "." { - if p, ok := compression[roBs[begin:]]; !ok { - // Only offsets smaller than this can be used. - if offset < maxCompressionOffset { - compression[roBs[begin:]] = offset - } - } else { + if compression.valid() && !isRootLabel(s, bs, begin, ls) { + if p, ok := compression.find(s[compBegin:]); ok { // The first hit is the longest matching dname // keep the pointer offset we get back and store // the offset of the current name, because that's // where we need to insert the pointer later // If compress is true, we're allowed to compress this dname - if pointer == -1 && compress { - pointer = p // Where to point to - nameoffset = offset // Where to point from - break + if compress { + pointer = p // Where to point to + break loop } + } else if off < maxCompressionOffset { + // Only offsets smaller than maxCompressionOffset can be used. + compression.insert(s[compBegin:], off) } } + + // The following is covered by the length check above. + if msg != nil { + msg[off] = byte(labelLen) + + if bs == nil { + copy(msg[off+1:], s[begin:i]) + } else { + copy(msg[off+1:], bs[begin:i]) + } + } + off += 1 + labelLen + labels++ begin = i + 1 + compBegin = begin + compOff + default: + wasDot = false } - escapedDot = false } + // Root label is special - if len(bs) == 1 && bs[0] == '.' { + if isRootLabel(s, bs, 0, ls) { return off, labels, nil } + // If we did compression and we find something add the pointer here if pointer != -1 { // We have two bytes (14 bits) to put the pointer in // if msg == nil, we will never do compression - binary.BigEndian.PutUint16(msg[nameoffset:], uint16(pointer^0xC000)) - off = nameoffset + 1 - goto End + binary.BigEndian.PutUint16(msg[off:], uint16(pointer^0xC000)) + return off + 2, labels, nil } - if msg != nil && off < len(msg) { + + if msg != nil && off < lenmsg { msg[off] = 0 } -End: - off++ - return off, labels, nil + + return off + 1, labels, nil +} + +// isRootLabel returns whether s or bs, from off to end, is the root +// label ".". +// +// If bs is nil, s will be checked, otherwise bs will be checked. +func isRootLabel(s string, bs []byte, off, end int) bool { + if bs == nil { + return s[off:end] == "." + } + + return end-off == 1 && bs[off] == '.' } // Unpack a domain name. @@ -330,12 +409,16 @@ End: // In theory, the pointers are only allowed to jump backward. // We let them jump anywhere and stop jumping after a while. -// UnpackDomainName unpacks a domain name into a string. +// UnpackDomainName unpacks a domain name into a string. It returns +// the name, the new offset into msg and any error that occurred. +// +// When an error is encountered, the unpacked name will be discarded +// and len(msg) will be returned as the offset. func UnpackDomainName(msg []byte, off int) (string, int, error) { - s := make([]byte, 0, 64) + s := make([]byte, 0, maxDomainNamePresentationLength) off1 := 0 lenmsg := len(msg) - maxLen := maxDomainNameWireOctets + budget := maxDomainNameWireOctets ptr := 0 // number of pointers followed Loop: for { @@ -354,27 +437,19 @@ Loop: if off+c > lenmsg { return "", lenmsg, ErrBuf } + budget -= c + 1 // +1 for the label separator + if budget <= 0 { + return "", lenmsg, ErrLongDomain + } for j := off; j < off+c; j++ { switch b := msg[j]; b { case '.', '(', ')', ';', ' ', '@': fallthrough case '"', '\\': s = append(s, '\\', b) - // presentation-format \X escapes add an extra byte - maxLen++ default: - if b < 32 || b >= 127 { // unprintable, use \DDD - 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) - } - // presentation-format \DDD escapes add 3 extra bytes - maxLen += 3 + if b < ' ' || b > '~' { // unprintable, use \DDD + s = append(s, escapeByte(b)...) } else { s = append(s, b) } @@ -396,7 +471,7 @@ Loop: if ptr == 0 { off1 = off } - if ptr++; ptr > 10 { + if ptr++; ptr > maxCompressionPointers { return "", lenmsg, &Error{err: "too many compression pointers"} } // pointer should guarantee that it advances and points forwards at least @@ -412,10 +487,7 @@ Loop: off1 = off } if len(s) == 0 { - s = []byte(".") - } else if len(s) >= maxLen { - // error if the name is too long, but don't throw it away - return string(s), lenmsg, ErrLongDomain + return ".", off1, nil } return string(s), off1, nil } @@ -512,7 +584,7 @@ func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { off = off0 var s string for off < len(msg) && err == nil { - s, off, err = unpackTxtString(msg, off) + s, off, err = unpackString(msg, off) if err == nil { ss = append(ss, s) } @@ -520,43 +592,16 @@ func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { return } -func unpackTxtString(msg []byte, offset int) (string, int, error) { - if offset+1 > len(msg) { - return "", offset, &Error{err: "overflow unpacking txt"} - } - l := int(msg[offset]) - if offset+l+1 > len(msg) { - return "", offset, &Error{err: "overflow unpacking txt"} - } - s := make([]byte, 0, l) - for _, b := range msg[offset+1 : offset+1+l] { - switch b { - case '"', '\\': - 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) - } - } - } - offset += 1 + l - return string(s), offset, nil -} - // Helpers for dealing with escaped bytes func isDigit(b byte) bool { return b >= '0' && b <= '9' } func dddToByte(s []byte) byte { + _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 + return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) +} + +func dddStringToByte(s string) byte { + _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) } @@ -574,19 +619,33 @@ func intToBytes(i *big.Int, length int) []byte { // PackRR packs a resource record rr into msg[off:]. // See PackDomainName for documentation about the compression. func PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { + headerEnd, off1, err := packRR(rr, msg, off, compressionMap{ext: compression}, compress) + if err == nil { + // packRR no longer sets the Rdlength field on the rr, but + // callers might be expecting it so we set it here. + rr.Header().Rdlength = uint16(off1 - headerEnd) + } + return off1, err +} + +func packRR(rr RR, msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) { if rr == nil { - return len(msg), &Error{err: "nil rr"} + return len(msg), len(msg), &Error{err: "nil rr"} } - off1, err = rr.pack(msg, off, compression, compress) + headerEnd, off1, err = rr.pack(msg, off, compression, compress) if err != nil { - return len(msg), err + return headerEnd, len(msg), err } - // TODO(miek): Not sure if this is needed? If removed we can remove rawmsg.go as well. - if rawSetRdlength(msg, off, off1) { - return off1, nil + + rdlength := off1 - headerEnd + if int(uint16(rdlength)) != rdlength { // overflow + return headerEnd, len(msg), ErrRdata } - return off, ErrRdata + + // The RDLENGTH field is the last field in the header and we set it here. + binary.BigEndian.PutUint16(msg[headerEnd-2:], uint16(rdlength)) + return headerEnd, off1, nil } // UnpackRR unpacks msg[off:] into an RR. @@ -595,6 +654,13 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) { if err != nil { return nil, len(msg), err } + + return UnpackRRWithHeader(h, msg, off) +} + +// UnpackRRWithHeader unpacks the record type specific payload given an existing +// RR_Header. +func UnpackRRWithHeader(h RR_Header, msg []byte, off int) (rr RR, off1 int, err error) { end := off + int(h.Rdlength) if fn, known := typeToUnpack[h.Rrtype]; !known { @@ -684,35 +750,37 @@ func (dns *Msg) Pack() (msg []byte, err error) { return dns.PackBuffer(nil) } -// PackBuffer packs a Msg, using the given buffer buf. If buf is too small -// a new buffer is allocated. +// PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated. func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) { - // We use a similar function in tsig.go's stripTsig. - var ( - dh Header - compression map[string]int - ) - - if dns.Compress { - compression = make(map[string]int) // Compression pointer mappings + // If this message can't be compressed, avoid filling the + // compression map and creating garbage. + if dns.Compress && dns.isCompressible() { + compression := make(map[string]uint16) // Compression pointer mappings. + return dns.packBufferWithCompressionMap(buf, compressionMap{int: compression}, true) } + return dns.packBufferWithCompressionMap(buf, compressionMap{}, false) +} + +// packBufferWithCompressionMap packs a Msg, using the given buffer buf. +func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compressionMap, compress bool) (msg []byte, err error) { if dns.Rcode < 0 || dns.Rcode > 0xFFF { return nil, ErrRcode } - if dns.Rcode > 0xF { - // Regular RCODE field is 4 bits - opt := dns.IsEdns0() - if opt == nil { - return nil, ErrExtendedRcode - } - opt.SetExtendedRcode(uint8(dns.Rcode >> 4)) - dns.Rcode &= 0xF + + // Set extended rcode unconditionally if we have an opt, this will allow + // reseting the extended rcode bits if they need to. + if opt := dns.IsEdns0(); opt != nil { + opt.SetExtendedRcode(uint16(dns.Rcode)) + } else if dns.Rcode > 0xF { + // If Rcode is an extended one and opt is nil, error out. + return nil, ErrExtendedRcode } // Convert convenient Msg into wire-like Header. + var dh Header dh.Id = dns.Id - dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode) + dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode&0xF) if dns.Response { dh.Bits |= _QR } @@ -738,50 +806,44 @@ func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) { dh.Bits |= _CD } - // Prepare variable sized arrays. - question := dns.Question - answer := dns.Answer - ns := dns.Ns - extra := dns.Extra - - dh.Qdcount = uint16(len(question)) - dh.Ancount = uint16(len(answer)) - dh.Nscount = uint16(len(ns)) - dh.Arcount = uint16(len(extra)) + dh.Qdcount = uint16(len(dns.Question)) + dh.Ancount = uint16(len(dns.Answer)) + dh.Nscount = uint16(len(dns.Ns)) + dh.Arcount = uint16(len(dns.Extra)) // We need the uncompressed length here, because we first pack it and then compress it. msg = buf - uncompressedLen := compressedLen(dns, false) + uncompressedLen := msgLenWithCompressionMap(dns, nil) if packLen := uncompressedLen + 1; len(msg) < packLen { msg = make([]byte, packLen) } // Pack it in: header and then the pieces. off := 0 - off, err = dh.pack(msg, off, compression, dns.Compress) + off, err = dh.pack(msg, off, compression, compress) if err != nil { return nil, err } - for i := 0; i < len(question); i++ { - off, err = question[i].pack(msg, off, compression, dns.Compress) + for _, r := range dns.Question { + off, err = r.pack(msg, off, compression, compress) if err != nil { return nil, err } } - for i := 0; i < len(answer); i++ { - off, err = PackRR(answer[i], msg, off, compression, dns.Compress) + for _, r := range dns.Answer { + _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } } - for i := 0; i < len(ns); i++ { - off, err = PackRR(ns[i], msg, off, compression, dns.Compress) + for _, r := range dns.Ns { + _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } } - for i := 0; i < len(extra); i++ { - off, err = PackRR(extra[i], msg, off, compression, dns.Compress) + for _, r := range dns.Extra { + _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } @@ -789,28 +851,7 @@ func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) { return msg[:off], nil } -// Unpack unpacks a binary message to a Msg structure. -func (dns *Msg) Unpack(msg []byte) (err error) { - var ( - dh Header - off int - ) - if dh, off, err = unpackMsgHdr(msg, off); err != nil { - return err - } - - dns.Id = dh.Id - dns.Response = (dh.Bits & _QR) != 0 - dns.Opcode = int(dh.Bits>>11) & 0xF - dns.Authoritative = (dh.Bits & _AA) != 0 - dns.Truncated = (dh.Bits & _TC) != 0 - dns.RecursionDesired = (dh.Bits & _RD) != 0 - dns.RecursionAvailable = (dh.Bits & _RA) != 0 - dns.Zero = (dh.Bits & _Z) != 0 - dns.AuthenticatedData = (dh.Bits & _AD) != 0 - dns.CheckingDisabled = (dh.Bits & _CD) != 0 - dns.Rcode = int(dh.Bits & 0xF) - +func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { // If we are at the end of the message we should return *just* the // header. This can still be useful to the caller. 9.9.9.9 sends these // when responding with REFUSED for instance. @@ -829,8 +870,6 @@ func (dns *Msg) Unpack(msg []byte) (err error) { var q Question q, off, err = unpackQuestion(msg, off) if err != nil { - // Even if Truncated is set, we only will set ErrTruncated if we - // actually got the questions return err } if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie! @@ -854,16 +893,29 @@ func (dns *Msg) Unpack(msg []byte) (err error) { // The header counts might have been wrong so we need to update it dh.Arcount = uint16(len(dns.Extra)) + // Set extended Rcode + if opt := dns.IsEdns0(); opt != nil { + dns.Rcode |= opt.ExtendedRcode() + } + if off != len(msg) { // TODO(miek) make this an error? // use PackOpt to let people tell how detailed the error reporting should be? // println("dns: extra bytes in dns packet", off, "<", len(msg)) - } else if dns.Truncated { - // Whether we ran into a an error or not, we want to return that it - // was truncated - err = ErrTruncated } return err + +} + +// Unpack unpacks a binary message to a Msg structure. +func (dns *Msg) Unpack(msg []byte) (err error) { + dh, off, err := unpackMsgHdr(msg, 0) + if err != nil { + return err + } + + dns.setHdr(dh) + return dns.unpack(dh, msg, off) } // Convert a complete message to a string with dig-like output. @@ -909,99 +961,109 @@ func (dns *Msg) String() string { return s } +// isCompressible returns whether the msg may be compressible. +func (dns *Msg) isCompressible() bool { + // If we only have one question, there is nothing we can ever compress. + return len(dns.Question) > 1 || len(dns.Answer) > 0 || + len(dns.Ns) > 0 || len(dns.Extra) > 0 +} + // Len returns the message length when in (un)compressed wire format. // If dns.Compress is true compression it is taken into account. Len() // is provided to be a faster way to get the size of the resulting packet, // than packing it, measuring the size and discarding the buffer. -func (dns *Msg) Len() int { return compressedLen(dns, dns.Compress) } +func (dns *Msg) Len() int { + // If this message can't be compressed, avoid filling the + // compression map and creating garbage. + if dns.Compress && dns.isCompressible() { + compression := make(map[string]struct{}) + return msgLenWithCompressionMap(dns, compression) + } -// compressedLen returns the message length when in compressed wire format -// when compress is true, otherwise the uncompressed length is returned. -func compressedLen(dns *Msg, compress bool) int { - // We always return one more than needed. + return msgLenWithCompressionMap(dns, nil) +} + +func msgLenWithCompressionMap(dns *Msg, compression map[string]struct{}) int { l := 12 // Message header is always 12 bytes - if compress { - compression := map[string]int{} - for _, r := range dns.Question { - l += r.len() - compressionLenHelper(compression, r.Name) - } - l += compressionLenSlice(compression, dns.Answer) - l += compressionLenSlice(compression, dns.Ns) - l += compressionLenSlice(compression, dns.Extra) - } else { - for _, r := range dns.Question { - l += r.len() - } - for _, r := range dns.Answer { - if r != nil { - l += r.len() - } - } - for _, r := range dns.Ns { - if r != nil { - l += r.len() - } - } - for _, r := range dns.Extra { - if r != nil { - l += r.len() - } + + for _, r := range dns.Question { + l += r.len(l, compression) + } + for _, r := range dns.Answer { + if r != nil { + l += r.len(l, compression) } } + for _, r := range dns.Ns { + if r != nil { + l += r.len(l, compression) + } + } + for _, r := range dns.Extra { + if r != nil { + l += r.len(l, compression) + } + } + return l } -func compressionLenSlice(c map[string]int, rs []RR) int { - var l int - for _, r := range rs { - if r == nil { +func domainNameLen(s string, off int, compression map[string]struct{}, compress bool) int { + if s == "" || s == "." { + return 1 + } + + escaped := strings.Contains(s, "\\") + + if compression != nil && (compress || off < maxCompressionOffset) { + // compressionLenSearch will insert the entry into the compression + // map if it doesn't contain it. + if l, ok := compressionLenSearch(compression, s, off); ok && compress { + if escaped { + return escapedNameLen(s[:l]) + 2 + } + + return l + 2 + } + } + + if escaped { + return escapedNameLen(s) + 1 + } + + return len(s) + 1 +} + +func escapedNameLen(s string) int { + nameLen := len(s) + for i := 0; i < len(s); i++ { + if s[i] != '\\' { continue } - l += r.len() - k, ok := compressionLenSearch(c, r.Header().Name) - if ok { - l += 1 - k + + if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { + nameLen -= 3 + i += 3 + } else { + nameLen-- + i++ } - compressionLenHelper(c, r.Header().Name) - k, ok = compressionLenSearchType(c, r) - if ok { - l += 1 - k - } - compressionLenHelperType(c, r) } - return l + + return nameLen } -// Put the parts of the name in the compression map. -func compressionLenHelper(c map[string]int, s string) { - pref := "" - lbs := Split(s) - for j := len(lbs) - 1; j >= 0; j-- { - pref = s[lbs[j]:] - if _, ok := c[pref]; !ok { - c[pref] = len(pref) - } - } -} - -// Look for each part in the compression map and returns its length, -// keep on searching so we get the longest match. -func compressionLenSearch(c map[string]int, s string) (int, bool) { - off := 0 - end := false - if s == "" { // don't bork on bogus data - return 0, false - } - for { +func compressionLenSearch(c map[string]struct{}, s string, msgOff int) (int, bool) { + for off, end := 0, false; !end; off, end = NextLabel(s, off) { if _, ok := c[s[off:]]; ok { - return len(s[off:]), true + return off, true } - if end { - break + + if msgOff+off < maxCompressionOffset { + c[s[off:]] = struct{}{} } - off, end = NextLabel(s, off) } + return 0, false } @@ -1009,7 +1071,7 @@ func compressionLenSearch(c map[string]int, s string) (int, bool) { func Copy(r RR) RR { r1 := r.copy(); return r1 } // Len returns the length (in octets) of the uncompressed RR in wire format. -func Len(r RR) int { return r.len() } +func Len(r RR) int { return r.len(0, nil) } // Copy returns a new *Msg which is a deep-copy of dns. func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) } @@ -1057,8 +1119,8 @@ func (dns *Msg) CopyTo(r1 *Msg) *Msg { return r1 } -func (q *Question) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := PackDomainName(q.Name, msg, off, compression, compress) +func (q *Question) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { + off, _, err := packDomainName(q.Name, msg, off, compression, compress) if err != nil { return off, err } @@ -1099,7 +1161,7 @@ func unpackQuestion(msg []byte, off int) (Question, int, error) { return q, off, err } -func (dh *Header) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { +func (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { off, err := packUint16(dh.Id, msg, off) if err != nil { return off, err @@ -1152,3 +1214,18 @@ func unpackMsgHdr(msg []byte, off int) (Header, int, error) { dh.Arcount, off, err = unpackUint16(msg, off) return dh, off, err } + +// setHdr set the header in the dns using the binary data in dh. +func (dns *Msg) setHdr(dh Header) { + dns.Id = dh.Id + dns.Response = dh.Bits&_QR != 0 + dns.Opcode = int(dh.Bits>>11) & 0xF + dns.Authoritative = dh.Bits&_AA != 0 + dns.Truncated = dh.Bits&_TC != 0 + dns.RecursionDesired = dh.Bits&_RD != 0 + dns.RecursionAvailable = dh.Bits&_RA != 0 + dns.Zero = dh.Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite. + dns.AuthenticatedData = dh.Bits&_AD != 0 + dns.CheckingDisabled = dh.Bits&_CD != 0 + dns.Rcode = int(dh.Bits & 0xF) +} diff --git a/vendor/github.com/miekg/dns/msg_generate.go b/vendor/github.com/miekg/dns/msg_generate.go index 8ba609f72..86ed04fcb 100644 --- a/vendor/github.com/miekg/dns/msg_generate.go +++ b/vendor/github.com/miekg/dns/msg_generate.go @@ -80,18 +80,17 @@ func main() { o := scope.Lookup(name) st, _ := getTypeStruct(o.Type(), scope) - fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {\n", name) - fmt.Fprint(b, `off, err := rr.Hdr.pack(msg, off, compression, compress) + fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) {\n", name) + fmt.Fprint(b, `headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } -headerEnd := off `) for i := 1; i < st.NumFields(); i++ { o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) fmt.Fprint(b, `if err != nil { -return off, err +return headerEnd, off, err } `) } @@ -106,7 +105,7 @@ return off, err case `dns:"nsec"`: o("off, err = packDataNsec(rr.%s, msg, off)\n") case `dns:"domain-name"`: - o("off, err = packDataDomainNames(rr.%s, msg, off, compression, compress)\n") + o("off, err = packDataDomainNames(rr.%s, msg, off, compression, false)\n") default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } @@ -116,9 +115,9 @@ return off, err switch { case st.Tag(i) == `dns:"-"`: // ignored case st.Tag(i) == `dns:"cdomain-name"`: - o("off, err = PackDomainName(rr.%s, msg, off, compression, compress)\n") + o("off, _, err = packDomainName(rr.%s, msg, off, compression, compress)\n") case st.Tag(i) == `dns:"domain-name"`: - o("off, err = PackDomainName(rr.%s, msg, off, compression, false)\n") + o("off, _, err = packDomainName(rr.%s, msg, off, compression, false)\n") case st.Tag(i) == `dns:"a"`: o("off, err = packDataA(rr.%s, msg, off)\n") case st.Tag(i) == `dns:"aaaa"`: @@ -145,7 +144,7 @@ return off, err if rr.%s != "-" { off, err = packStringHex(rr.%s, msg, off) if err != nil { - return off, err + return headerEnd, off, err } } `, field, field) @@ -176,9 +175,7 @@ if rr.%s != "-" { log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } } - // We have packed everything, only now we know the rdlength of this RR - fmt.Fprintln(b, "rr.Header().Rdlength = uint16(off-headerEnd)") - fmt.Fprintln(b, "return off, nil }\n") + fmt.Fprintln(b, "return headerEnd, off, nil }\n") } fmt.Fprint(b, "// unpack*() functions\n\n") diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go index 946d5acbf..36345e162 100644 --- a/vendor/github.com/miekg/dns/msg_helpers.go +++ b/vendor/github.com/miekg/dns/msg_helpers.go @@ -6,7 +6,7 @@ import ( "encoding/binary" "encoding/hex" "net" - "strconv" + "strings" ) // helper functions called from the generated zmsg.go @@ -101,32 +101,32 @@ func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, // 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) { +func (hdr RR_Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { if off == len(msg) { - return off, nil + return off, off, nil } - off, err = PackDomainName(hdr.Name, msg, off, compression, compress) + off, _, err := packDomainName(hdr.Name, msg, off, compression, compress) if err != nil { - return len(msg), err + return off, len(msg), err } off, err = packUint16(hdr.Rrtype, msg, off) if err != nil { - return len(msg), err + return off, len(msg), err } off, err = packUint16(hdr.Class, msg, off) if err != nil { - return len(msg), err + return off, len(msg), err } off, err = packUint32(hdr.Ttl, msg, off) if err != nil { - return len(msg), err + return off, len(msg), err } - off, err = packUint16(hdr.Rdlength, msg, off) + off, err = packUint16(0, msg, off) // The RDLENGTH field will be set later in packRR. if err != nil { - return len(msg), err + return off, len(msg), err } - return off, nil + return off, off, nil } // helper helper functions. @@ -141,20 +141,24 @@ func truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []b return msg[:lenrd], nil } +var base32HexNoPadEncoding = base32.HexEncoding.WithPadding(base32.NoPadding) + func fromBase32(s []byte) (buf []byte, err error) { for i, b := range s { if b >= 'a' && b <= 'z' { s[i] = b - 32 } } - buflen := base32.HexEncoding.DecodedLen(len(s)) + buflen := base32HexNoPadEncoding.DecodedLen(len(s)) buf = make([]byte, buflen) - n, err := base32.HexEncoding.Decode(buf, s) + n, err := base32HexNoPadEncoding.Decode(buf, s) buf = buf[:n] return } -func toBase32(b []byte) string { return base32.HexEncoding.EncodeToString(b) } +func toBase32(b []byte) string { + return base32HexNoPadEncoding.EncodeToString(b) +} func fromBase64(s []byte) (buf []byte, err error) { buflen := base64.StdEncoding.DecodedLen(len(s)) @@ -219,8 +223,8 @@ func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) { 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]))) + i = 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 } @@ -263,29 +267,21 @@ func unpackString(msg []byte, off int) (string, int, error) { if off+l+1 > len(msg) { return "", off, &Error{err: "overflow unpacking txt"} } - s := make([]byte, 0, l) + var s strings.Builder + s.Grow(l) for _, b := range msg[off+1 : off+1+l] { - switch b { - case '"', '\\': - s = append(s, '\\', b) + switch { + case b == '"' || b == '\\': + s.WriteByte('\\') + s.WriteByte(b) + case b < ' ' || b > '~': // unprintable + s.WriteString(escapeByte(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) - } + s.WriteByte(b) } } off += 1 + l - return string(s), off, nil + return s.String(), off, nil } func packString(s string, msg []byte, off int) (int, error) { @@ -359,7 +355,7 @@ func packStringHex(s string, msg []byte, off int) (int, error) { if err != nil { return len(msg), err } - if off+(len(h)) > len(msg) { + if off+len(h) > len(msg) { return len(msg), &Error{err: "overflow packing hex"} } copy(msg[off:off+len(h)], h) @@ -599,7 +595,7 @@ func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) { // 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))) + msg[off+1+int(length)] |= byte(1 << (7 - t%8)) lastwindow, lastlength = window, length } off += int(lastlength) + 2 @@ -625,10 +621,10 @@ func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) { return servers, off, nil } -func packDataDomainNames(names []string, msg []byte, off int, compression map[string]int, compress bool) (int, error) { +func packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) { var err error for j := 0; j < len(names); j++ { - off, err = PackDomainName(names[j], msg, off, compression, false && compress) + off, _, err = packDomainName(names[j], msg, off, compression, compress) if err != nil { return len(msg), err } diff --git a/vendor/github.com/miekg/dns/nsecx.go b/vendor/github.com/miekg/dns/nsecx.go index 9b908c447..8f071a473 100644 --- a/vendor/github.com/miekg/dns/nsecx.go +++ b/vendor/github.com/miekg/dns/nsecx.go @@ -2,49 +2,44 @@ package dns import ( "crypto/sha1" - "hash" + "encoding/hex" "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 ha != SHA1 { + return "" + } + + wireSalt := make([]byte, hex.DecodedLen(len(salt))) + n, err := packStringHex(salt, wireSalt, 0) if err != nil { return "" } - wire = wire[:n] + wireSalt = wireSalt[: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 "" - } + s := sha1.New() // k = 0 s.Write(name) - s.Write(wire) + s.Write(wireSalt) nsec3 := s.Sum(nil) + // k > 0 for k := uint16(0); k < iter; k++ { s.Reset() s.Write(nsec3) - s.Write(wire) + s.Write(wireSalt) nsec3 = s.Sum(nsec3[:0]) } + return toBase32(nsec3) } @@ -63,8 +58,10 @@ func (rr *NSEC3) Cover(name string) bool { } nextHash := rr.NextDomain - if ownerHash == nextHash { // empty interval - return false + + // if empty interval found, try cover wildcard hashes so nameHash shouldn't match with ownerHash + if ownerHash == nextHash && nameHash != ownerHash { // empty interval + return true } if ownerHash > nextHash { // end of zone if nameHash > ownerHash { // covered since there is nothing after ownerHash @@ -96,11 +93,3 @@ func (rr *NSEC3) Match(name string) bool { } 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 -} diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go index 6b08e6e95..18355f9be 100644 --- a/vendor/github.com/miekg/dns/privaterr.go +++ b/vendor/github.com/miekg/dns/privaterr.go @@ -52,12 +52,16 @@ 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) len(off int, compression map[string]struct{}) int { + l := r.Hdr.len(off, compression) + l += r.Data.Len() + return l +} + func (r *PrivateRR) copy() RR { // make new RR like this: rr := mkPrivateRR(r.Hdr.Rrtype) - newh := r.Hdr.copyHeader() - rr.Hdr = *newh + rr.Hdr = r.Hdr err := r.Data.Copy(rr.Data) if err != nil { @@ -65,19 +69,18 @@ func (r *PrivateRR) copy() RR { } 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) + +func (r *PrivateRR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := r.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return off, off, err } - headerEnd := off n, err := r.Data.Pack(msg[off:]) if err != nil { - return len(msg), err + return headerEnd, len(msg), err } off += n - r.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } // PrivateHandle registers a private resource record type. It requires @@ -106,7 +109,7 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) return rr, off, err } - setPrivateRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { + setPrivateRR := func(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := mkPrivateRR(h.Rrtype) rr.Hdr = h @@ -116,7 +119,7 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) 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 { + switch l, _ = c.Next(); l.value { case zNewline, zEOF: break Fetch case zString: @@ -135,7 +138,7 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) typeToparserFunc[rtype] = parserFunc{setPrivateRR, true} } -// PrivateHandleRemove removes defenitions required to support private RR type. +// PrivateHandleRemove removes definitions required to support private RR type. func PrivateHandleRemove(rtype uint16) { rtypestr, ok := TypeToString[rtype] if ok { @@ -145,5 +148,4 @@ func PrivateHandleRemove(rtype uint16) { delete(StringToType, rtypestr) delete(typeToUnpack, rtype) } - return } diff --git a/vendor/github.com/miekg/dns/rawmsg.go b/vendor/github.com/miekg/dns/rawmsg.go deleted file mode 100644 index 6e21fba7e..000000000 --- a/vendor/github.com/miekg/dns/rawmsg.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/miekg/dns/reverse.go b/vendor/github.com/miekg/dns/reverse.go index f6e7a47a6..1f0e2b2a4 100644 --- a/vendor/github.com/miekg/dns/reverse.go +++ b/vendor/github.com/miekg/dns/reverse.go @@ -12,6 +12,11 @@ var StringToOpcode = reverseInt(OpcodeToString) // StringToRcode is a map of rcodes to strings. var StringToRcode = reverseInt(RcodeToString) +func init() { + // Preserve previous NOTIMP typo, see github.com/miekg/dns/issues/733. + StringToRcode["NOTIMPL"] = RcodeNotImplemented +} + // Reverse a map func reverseInt8(m map[uint8]string) map[string]uint8 { n := make(map[string]uint8, len(m)) diff --git a/vendor/github.com/miekg/dns/sanitize.go b/vendor/github.com/miekg/dns/sanitize.go index c415bdd6c..cac15787a 100644 --- a/vendor/github.com/miekg/dns/sanitize.go +++ b/vendor/github.com/miekg/dns/sanitize.go @@ -5,6 +5,7 @@ package dns // rrs. // m is used to store the RRs temporary. 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) } diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go index f9cd47401..61ace121e 100644 --- a/vendor/github.com/miekg/dns/scan.go +++ b/vendor/github.com/miekg/dns/scan.go @@ -1,6 +1,7 @@ package dns import ( + "bufio" "fmt" "io" "os" @@ -10,7 +11,10 @@ import ( ) const maxTok = 2048 // Largest token we can return. -const maxUint16 = 1<<16 - 1 + +// The maximum depth of $INCLUDE directives supported by the +// ZoneParser API. +const maxIncludeDepth = 7 // Tokinize a RFC 1035 zone file. The tokenizer will normalize it: // * Add ownernames if they are left blank; @@ -75,15 +79,13 @@ func (e *ParseError) Error() (s string) { } 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 string // text of the token + err bool // when true, token text has lexer error + value uint8 // value: zString, _BLANK, etc. + torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar + line int // line in the file + column int // column in the file + comment string // any comment text seen } // Token holds the token that are returned when a zone file is parsed. @@ -103,10 +105,14 @@ type ttlState struct { } // 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. +// returned. If s contains no records, NewRR will 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"), "") @@ -114,28 +120,31 @@ func NewRR(s string) (RR, error) { return ReadRR(strings.NewReader(s), "") } -// ReadRR reads the RR contained in q. +// ReadRR reads the RR contained in r. +// +// The string file is used in error reporting and to resolve relative +// $INCLUDE directives. +// // See NewRR for more documentation. -func ReadRR(q io.Reader, filename string) (RR, error) { - defttl := &ttlState{defaultTtl, false} - r := <-parseZoneHelper(q, ".", filename, defttl, 1) - if r == nil { - return nil, nil - } - - if r.Error != nil { - return nil, r.Error - } - return r.RR, nil +func ReadRR(r io.Reader, file string) (RR, error) { + zp := NewZoneParser(r, ".", file) + zp.SetDefaultTTL(defaultTtl) + zp.SetIncludeAllowed(true) + rr, _ := zp.Next() + return rr, zp.Err() } -// ParseZone reads a RFC 1035 style zonefile from r. It returns *Tokens on the -// returned channel, each consisting of either a parsed RR and optional comment -// or a nil RR and an error. The string file is only used -// in error reporting. The string origin is used as the initial origin, as -// if the file would start with an $ORIGIN directive. -// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported. -// The channel t is closed by ParseZone when the end of r is reached. +// ParseZone reads a RFC 1035 style zonefile from r. It returns +// *Tokens on the returned channel, each consisting of either a +// parsed RR and optional comment or a nil RR and an error. The +// channel is closed by ParseZone when the end of r is reached. +// +// The string file is used in error reporting and to resolve relative +// $INCLUDE directives. The string origin is used as the initial +// origin, as if the file would start with an $ORIGIN directive. +// +// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all +// supported. // // Basic usage pattern when reading from a string (z) containing the // zone data: @@ -148,91 +157,246 @@ func ReadRR(q io.Reader, filename string) (RR, error) { // } // } // -// Comments specified after an RR (and on the same line!) are returned too: +// 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. +// The text "; this is comment" is returned in Token.Comment. +// Comments inside the RR are returned concatenated along with the +// RR. Comments on a line by themselves are discarded. +// +// To prevent memory leaks it is important to always fully drain the +// returned channel. If an error occurs, it will always be the last +// Token sent on the channel. +// +// Deprecated: New users should prefer the ZoneParser API. func ParseZone(r io.Reader, origin, file string) chan *Token { - return parseZoneHelper(r, origin, file, nil, 10000) -} - -func parseZoneHelper(r io.Reader, origin, file string, defttl *ttlState, chansize int) chan *Token { - t := make(chan *Token, chansize) - go parseZone(r, origin, file, defttl, t, 0) + t := make(chan *Token, 10000) + go parseZone(r, origin, file, t) return t } -func parseZone(r io.Reader, origin, f string, defttl *ttlState, t chan *Token, include int) { - defer func() { - if include == 0 { - close(t) - } - }() - s, cancel := scanInit(r) - c := make(chan lex) - // Start the lexer - go zlexer(s, c) +func parseZone(r io.Reader, origin, file string, t chan *Token) { + defer close(t) - defer func() { - cancel() - // zlexer can send up to three tokens, the next one and possibly 2 remainders. - // Do a non-blocking read. - _, ok := <-c - _, ok = <-c - _, ok = <-c + zp := NewZoneParser(r, origin, file) + zp.SetIncludeAllowed(true) + + for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { + t <- &Token{RR: rr, Comment: zp.Comment()} + } + + if err := zp.Err(); err != nil { + pe, ok := err.(*ParseError) if !ok { - // too bad + pe = &ParseError{file: file, err: err.Error()} } - }() - // 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. + t <- &Token{Error: pe} + } +} + +// ZoneParser is a parser for an RFC 1035 style zonefile. +// +// Each parsed RR in the zone is returned sequentially from Next. An +// optional comment can be retrieved with Comment. +// +// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all +// supported. Although $INCLUDE is disabled by default. +// +// Basic usage pattern when reading from a string (z) containing the +// zone data: +// +// zp := NewZoneParser(strings.NewReader(z), "", "") +// +// for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { +// // Do something with rr +// } +// +// if err := zp.Err(); err != nil { +// // log.Println(err) +// } +// +// 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 from Comment. Comments inside +// the RR are returned concatenated along with the RR. Comments on a line +// by themselves are discarded. +type ZoneParser struct { + c *zlexer + + parseErr *ParseError + + origin string + file string + + defttl *ttlState + + h RR_Header + + // sub is used to parse $INCLUDE files and $GENERATE directives. + // Next, by calling subNext, forwards the resulting RRs from this + // sub parser to the calling code. + sub *ZoneParser + osFile *os.File + + com string + + includeDepth uint8 + + includeAllowed bool +} + +// NewZoneParser returns an RFC 1035 style zonefile parser that reads +// from r. +// +// The string file is used in error reporting and to resolve relative +// $INCLUDE directives. The string origin is used as the initial +// origin, as if the file would start with an $ORIGIN directive. +func NewZoneParser(r io.Reader, origin, file string) *ZoneParser { + var pe *ParseError if origin != "" { origin = Fqdn(origin) if _, ok := IsDomainName(origin); !ok { - t <- &Token{Error: &ParseError{f, "bad initial origin name", lex{}}} - return + pe = &ParseError{file, "bad initial origin name", lex{}} } } - st := zExpectOwnerDir // initial state - var h RR_Header - 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 + return &ZoneParser{ + c: newZLexer(r), + parseErr: pe, + + origin: origin, + file: file, + } +} + +// SetDefaultTTL sets the parsers default TTL to ttl. +func (zp *ZoneParser) SetDefaultTTL(ttl uint32) { + zp.defttl = &ttlState{ttl, false} +} + +// SetIncludeAllowed controls whether $INCLUDE directives are +// allowed. $INCLUDE directives are not supported by default. +// +// The $INCLUDE directive will open and read from a user controlled +// file on the system. Even if the file is not a valid zonefile, the +// contents of the file may be revealed in error messages, such as: +// +// /etc/passwd: dns: not a TTL: "root:x:0:0:root:/root:/bin/bash" at line: 1:31 +// /etc/shadow: dns: not a TTL: "root:$6$::0:99999:7:::" at line: 1:125 +func (zp *ZoneParser) SetIncludeAllowed(v bool) { + zp.includeAllowed = v +} + +// Err returns the first non-EOF error that was encountered by the +// ZoneParser. +func (zp *ZoneParser) Err() error { + if zp.parseErr != nil { + return zp.parseErr + } + + if zp.sub != nil { + if err := zp.sub.Err(); err != nil { + return err } + } + + return zp.c.Err() +} + +func (zp *ZoneParser) setParseError(err string, l lex) (RR, bool) { + zp.parseErr = &ParseError{zp.file, err, l} + return nil, false +} + +// Comment returns an optional text comment that occurred alongside +// the RR. +func (zp *ZoneParser) Comment() string { + return zp.com +} + +func (zp *ZoneParser) subNext() (RR, bool) { + if rr, ok := zp.sub.Next(); ok { + zp.com = zp.sub.com + return rr, true + } + + if zp.sub.osFile != nil { + zp.sub.osFile.Close() + zp.sub.osFile = nil + } + + if zp.sub.Err() != nil { + // We have errors to surface. + return nil, false + } + + zp.sub = nil + return zp.Next() +} + +// Next advances the parser to the next RR in the zonefile and +// returns the (RR, true). It will return (nil, false) when the +// parsing stops, either by reaching the end of the input or an +// error. After Next returns (nil, false), the Err method will return +// any error that occurred during parsing. +func (zp *ZoneParser) Next() (RR, bool) { + zp.com = "" + + if zp.parseErr != nil { + return nil, false + } + if zp.sub != nil { + return zp.subNext() + } + + // 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. + + st := zExpectOwnerDir // initial state + h := &zp.h + + for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() { + // zlexer spotted an error already + if l.err { + return zp.setParseError(l.token, l) + } + switch st { case zExpectOwnerDir: // We can also expect a directive, like $TTL or $ORIGIN - if defttl != nil { - h.Ttl = defttl.ttl + if zp.defttl != nil { + h.Ttl = zp.defttl.ttl } + h.Class = ClassINET + switch l.value { case zNewline: st = zExpectOwnerDir case zOwner: - h.Name = l.token - name, ok := toAbsoluteName(l.token, origin) + name, ok := toAbsoluteName(l.token, zp.origin) if !ok { - t <- &Token{Error: &ParseError{f, "bad owner name", l}} - return + return zp.setParseError("bad owner name", l) } + h.Name = name - prevName = h.Name + st = zExpectOwnerBl case zDirTTL: st = zExpectDirTTLBl @@ -243,12 +407,12 @@ func parseZone(r io.Reader, origin, f string, defttl *ttlState, t chan *Token, i 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 @@ -256,297 +420,400 @@ func parseZone(r io.Reader, origin, f string, defttl *ttlState, t chan *Token, i case zString: ttl, ok := stringToTTL(l.token) if !ok { - t <- &Token{Error: &ParseError{f, "not a TTL", l}} - return + return zp.setParseError("not a TTL", l) } - h.Ttl = ttl - if defttl == nil || !defttl.isByDirective { - defttl = &ttlState{ttl, false} - } - st = zExpectAnyNoTTLBl + h.Ttl = ttl + + if zp.defttl == nil || !zp.defttl.isByDirective { + zp.defttl = &ttlState{ttl, false} + } + + st = zExpectAnyNoTTLBl default: - t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}} - return + return zp.setParseError("syntax error at beginning", l) } case zExpectDirIncludeBl: if l.value != zBlank { - t <- &Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}} - return + return zp.setParseError("no blank after $INCLUDE-directive", l) } + st = zExpectDirInclude case zExpectDirInclude: if l.value != zString { - t <- &Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}} - return + return zp.setParseError("expecting $INCLUDE value, not this...", l) } - neworigin := origin // There may be optionally a new origin set after the filename, if not use current one - switch l := <-c; l.value { + + neworigin := zp.origin // There may be optionally a new origin set after the filename, if not use current one + switch l, _ := zp.c.Next(); l.value { case zBlank: - l := <-c + l, _ := zp.c.Next() if l.value == zString { - name, ok := toAbsoluteName(l.token, origin) + name, ok := toAbsoluteName(l.token, zp.origin) if !ok { - t <- &Token{Error: &ParseError{f, "bad origin name", l}} - return + return zp.setParseError("bad origin name", l) } + neworigin = name } case zNewline, zEOF: // Ok default: - t <- &Token{Error: &ParseError{f, "garbage after $INCLUDE", l}} - return + return zp.setParseError("garbage after $INCLUDE", l) } + + if !zp.includeAllowed { + return zp.setParseError("$INCLUDE directive not allowed", l) + } + if zp.includeDepth >= maxIncludeDepth { + return zp.setParseError("too deeply nested $INCLUDE", l) + } + // Start with the new file includePath := l.token if !filepath.IsAbs(includePath) { - includePath = filepath.Join(filepath.Dir(f), includePath) + includePath = filepath.Join(filepath.Dir(zp.file), includePath) } + r1, e1 := os.Open(includePath) if e1 != nil { - msg := fmt.Sprintf("failed to open `%s'", l.token) + var as string if !filepath.IsAbs(l.token) { - msg += fmt.Sprintf(" as `%s'", includePath) + as = fmt.Sprintf(" as `%s'", includePath) } - t <- &Token{Error: &ParseError{f, msg, l}} - return + + msg := fmt.Sprintf("failed to open `%s'%s: %v", l.token, as, e1) + return zp.setParseError(msg, l) } - if include+1 > 7 { - t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}} - return - } - parseZone(r1, neworigin, includePath, defttl, t, include+1) - st = zExpectOwnerDir + + zp.sub = NewZoneParser(r1, neworigin, includePath) + zp.sub.defttl, zp.sub.includeDepth, zp.sub.osFile = zp.defttl, zp.includeDepth+1, r1 + zp.sub.SetIncludeAllowed(true) + return zp.subNext() case zExpectDirTTLBl: if l.value != zBlank { - t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}} - return + return zp.setParseError("no blank after $TTL-directive", l) } + st = zExpectDirTTL case zExpectDirTTL: if l.value != zString { - t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}} - return + return zp.setParseError("expecting $TTL value, not this...", l) } - if e, _ := slurpRemainder(c, f); e != nil { - t <- &Token{Error: e} - return + + if e, _ := slurpRemainder(zp.c, zp.file); e != nil { + zp.parseErr = e + return nil, false } + ttl, ok := stringToTTL(l.token) if !ok { - t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}} - return + return zp.setParseError("expecting $TTL value, not this...", l) } - defttl = &ttlState{ttl, true} + + zp.defttl = &ttlState{ttl, true} + st = zExpectOwnerDir case zExpectDirOriginBl: if l.value != zBlank { - t <- &Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}} - return + return zp.setParseError("no blank after $ORIGIN-directive", l) } + st = zExpectDirOrigin case zExpectDirOrigin: if l.value != zString { - t <- &Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}} - return + return zp.setParseError("expecting $ORIGIN value, not this...", l) } - if e, _ := slurpRemainder(c, f); e != nil { - t <- &Token{Error: e} + + if e, _ := slurpRemainder(zp.c, zp.file); e != nil { + zp.parseErr = e + return nil, false } - name, ok := toAbsoluteName(l.token, origin) + + name, ok := toAbsoluteName(l.token, zp.origin) if !ok { - t <- &Token{Error: &ParseError{f, "bad origin name", l}} - return + return zp.setParseError("bad origin name", l) } - origin = name + + zp.origin = name + st = zExpectOwnerDir case zExpectDirGenerateBl: if l.value != zBlank { - t <- &Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}} - return + return zp.setParseError("no blank after $GENERATE-directive", l) } + st = zExpectDirGenerate case zExpectDirGenerate: if l.value != zString { - t <- &Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}} - return + return zp.setParseError("expecting $GENERATE value, not this...", l) } - if errMsg := generate(l, c, t, origin); errMsg != "" { - t <- &Token{Error: &ParseError{f, errMsg, l}} - return - } - st = zExpectOwnerDir + + return zp.generate(l) case zExpectOwnerBl: if l.value != zBlank { - t <- &Token{Error: &ParseError{f, "no blank after owner", l}} - return + return zp.setParseError("no blank after owner", l) } + st = zExpectAny case zExpectAny: switch l.value { case zRrtpe: - if defttl == nil { - t <- &Token{Error: &ParseError{f, "missing TTL with no previous value", l}} - return + if zp.defttl == nil { + return zp.setParseError("missing TTL with no previous value", l) } + 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 + return zp.setParseError("not a TTL", l) } + h.Ttl = ttl - if defttl == nil || !defttl.isByDirective { - defttl = &ttlState{ttl, false} + + if zp.defttl == nil || !zp.defttl.isByDirective { + zp.defttl = &ttlState{ttl, false} } + st = zExpectAnyNoTTLBl default: - t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}} - return + return zp.setParseError("expecting RR type, TTL or class, not this...", l) } case zExpectAnyNoClassBl: if l.value != zBlank { - t <- &Token{Error: &ParseError{f, "no blank before class", l}} - return + return zp.setParseError("no blank before class", l) } + st = zExpectAnyNoClass case zExpectAnyNoTTLBl: if l.value != zBlank { - t <- &Token{Error: &ParseError{f, "no blank before TTL", l}} - return + return zp.setParseError("no blank before TTL", l) } + 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 + return zp.setParseError("expecting RR type or class, not this...", l) } case zExpectAnyNoClass: switch l.value { case zString: ttl, ok := stringToTTL(l.token) if !ok { - t <- &Token{Error: &ParseError{f, "not a TTL", l}} - return + return zp.setParseError("not a TTL", l) } + h.Ttl = ttl - if defttl == nil || !defttl.isByDirective { - defttl = &ttlState{ttl, false} + + if zp.defttl == nil || !zp.defttl.isByDirective { + zp.defttl = &ttlState{ttl, false} } + 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 + return zp.setParseError("expecting RR type or TTL, not this...", l) } case zExpectRrtypeBl: if l.value != zBlank { - t <- &Token{Error: &ParseError{f, "no blank before RR type", l}} - return + return zp.setParseError("no blank before RR type", l) } + st = zExpectRrtype case zExpectRrtype: if l.value != zRrtpe { - t <- &Token{Error: &ParseError{f, "unknown RR type", l}} - return + return zp.setParseError("unknown RR type", l) } + h.Rrtype = l.torc + st = zExpectRdata case zExpectRdata: - r, e, c1 := setRR(h, c, origin, f) + r, e, c1 := setRR(*h, zp.c, zp.origin, zp.file) 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 + + zp.parseErr = e + return nil, false } - t <- &Token{RR: r, Comment: c1} - st = zExpectOwnerDir + + zp.com = c1 + return r, true } } + // 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. + return nil, false } -// 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 { +type zlexer struct { + br io.ByteReader + + readErr error + + line int + column int + + com string + + l lex + + brace int + quote bool + space bool + commt bool + rrtype bool + owner bool + + nextL bool + + eol bool // end-of-line +} + +func newZLexer(r io.Reader) *zlexer { + br, ok := r.(io.ByteReader) + if !ok { + br = bufio.NewReaderSize(r, 1024) + } + + return &zlexer{ + br: br, + + line: 1, + + owner: true, + } +} + +func (zl *zlexer) Err() error { + if zl.readErr == io.EOF { + return nil + } + + return zl.readErr +} + +// readByte returns the next byte from the input +func (zl *zlexer) readByte() (byte, bool) { + if zl.readErr != nil { + return 0, false + } + + c, err := zl.br.ReadByte() + if err != nil { + zl.readErr = err + return 0, false + } + + // delay the newline handling until the next token is delivered, + // fixes off-by-one errors when reporting a parse error. + if zl.eol { + zl.line++ + zl.column = 0 + zl.eol = false + } + + if c == '\n' { + zl.eol = true + } else { + zl.column++ + } + + return c, true +} + +func (zl *zlexer) Next() (lex, bool) { + l := &zl.l + if zl.nextL { + zl.nextL = false + return *l, true + } + if l.err { + // Parsing errors should be sticky. + return lex{value: zEOF}, false + } + + var ( + str [maxTok]byte // Hold string text + com [maxTok]byte // Hold comment text + + stri int // Offset in str (0 means empty) + comi int // Offset in com (0 means empty) + + escape bool + ) + + if zl.com != "" { + comi = copy(com[:], zl.com) + zl.com = "" + } + + for x, ok := zl.readByte(); ok; x, ok = zl.readByte() { + l.line, l.column = zl.line, zl.column + l.comment = "" + + if stri >= len(str) { l.token = "token length insufficient for parsing" l.err = true - c <- l - return + return *l, true } - if comi >= maxTok { + if comi >= len(com) { l.token = "comment length insufficient for parsing" l.err = true - c <- l - return + return *l, true } switch x { case ' ', '\t': - if escape { + if escape || zl.quote { + // Inside quotes or escaped this is legal. + str[stri] = x + stri++ + escape = false - str[stri] = x - stri++ break } - if quote { - // Inside quotes this is legal - str[stri] = x - stri++ - break - } - if commt { + + if zl.commt { com[comi] = x comi++ break } + + var retL lex if stri == 0 { // Space directly in the beginning, handled in the grammar - } else if owner { + } else if zl.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 { + switch strings.ToUpper(l.token) { case "$TTL": l.value = zDirTTL case "$ORIGIN": @@ -556,259 +823,316 @@ func zlexer(s *scan, c chan lex) { case "$GENERATE": l.value = zDirGenerate } - c <- l + + retL = *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 { + + if !zl.rrtype { + tokenUpper := strings.ToUpper(l.token) + if t, ok := StringToType[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 - rrtype = true - l.torc = t + + zl.rrtype = true + } else if strings.HasPrefix(tokenUpper, "TYPE") { + t, ok := typeToInt(l.token) + if !ok { + l.token = "unknown RR type" + l.err = true + return *l, true } + + l.value = zRrtpe + l.torc = t + + zl.rrtype = true } - if t, ok := StringToClass[l.tokenUpper]; ok { + + if t, ok := StringToClass[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 + } else if strings.HasPrefix(tokenUpper, "CLASS") { + t, ok := classToInt(l.token) + if !ok { + l.token = "unknown class" + l.err = true + return *l, true } + + l.value = zClass + l.torc = t } } - c <- l - } - stri = 0 - if !space && !commt { + retL = *l + } + + zl.owner = false + + if !zl.space { + zl.space = true + l.value = zBlank l.token = " " - l.length = 1 - c <- l + + if retL == (lex{}) { + return *l, true + } + + zl.nextL = true + } + + if retL != (lex{}) { + return retL, true } - owner = false - space = true case ';': - if escape { + if escape || zl.quote { + // Inside quotes or escaped this is legal. + str[stri] = x + stri++ + escape = false - str[stri] = x - stri++ break } - if quote { - // Inside quotes this is legal - str[stri] = x - stri++ - break + + zl.commt = true + zl.com = "" + + if comi > 1 { + // A newline was previously seen inside a comment that + // was inside braces and we delayed adding it until now. + com[comi] = ' ' // convert newline to space + comi++ } - if stri > 0 { - l.value = zString - l.token = string(str[:stri]) - l.tokenUpper = strings.ToUpper(l.token) - l.length = stri - c <- l - stri = 0 - } - commt = true + com[comi] = ';' comi++ + + if stri > 0 { + zl.com = string(com[:comi]) + + l.value = zString + l.token = string(str[:stri]) + return *l, true + } case '\r': escape = false - if quote { + + if zl.quote { str[stri] = x stri++ - break } + // discard if outside of quotes case '\n': escape = false + // Escaped newline - if quote { + if zl.quote { str[stri] = x stri++ break } - // inside quotes this is legal - if commt { + + if zl.commt { // Reset a comment - commt = false - rrtype = false - stri = 0 + zl.commt = false + zl.rrtype = false + // If not in a brace this ends the comment AND the RR - if brace == 0 { - owner = true - owner = true + if zl.brace == 0 { + zl.owner = true + l.value = zNewline l.token = "\n" - l.tokenUpper = l.token - l.length = 1 l.comment = string(com[:comi]) - c <- l - l.comment = "" - comi = 0 - break + return *l, true } - com[comi] = ' ' // convert newline to space - comi++ + + zl.com = string(com[:comi]) break } - if brace == 0 { + if zl.brace == 0 { // If there is previous text, we should output it here + var retL lex 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 { + if !zl.rrtype { + tokenUpper := strings.ToUpper(l.token) + if t, ok := StringToType[tokenUpper]; ok { + zl.rrtype = true + l.value = zRrtpe l.torc = t - rrtype = true } } - c <- l + + retL = *l } + l.value = zNewline l.token = "\n" - l.tokenUpper = l.token - l.length = 1 - c <- l - stri = 0 - commt = false - rrtype = false - owner = true - comi = 0 + l.comment = zl.com + + zl.com = "" + zl.rrtype = false + zl.owner = true + + if retL != (lex{}) { + zl.nextL = true + return retL, true + } + + return *l, true } case '\\': // comments do not get escaped chars, everything is copied - if commt { + if zl.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 { + if zl.commt { com[comi] = x comi++ break } + if escape { str[stri] = x stri++ + escape = false break } - space = false + + zl.space = false + // send previous gathered text and the quote + var retL lex if stri != 0 { l.value = zString l.token = string(str[:stri]) - l.tokenUpper = strings.ToUpper(l.token) - l.length = stri - c <- l - stri = 0 + retL = *l } // send quote itself as separate token l.value = zQuote l.token = "\"" - l.tokenUpper = l.token - l.length = 1 - c <- l - quote = !quote + + zl.quote = !zl.quote + + if retL != (lex{}) { + zl.nextL = true + return retL, true + } + + return *l, true case '(', ')': - if commt { + if zl.commt { com[comi] = x comi++ break } - if escape { + + if escape || zl.quote { + // Inside quotes or escaped this is legal. str[stri] = x stri++ + escape = false break } - if quote { - str[stri] = x - stri++ - break - } + switch x { case ')': - brace-- - if brace < 0 { + zl.brace-- + + if zl.brace < 0 { l.token = "extra closing brace" - l.tokenUpper = l.token l.err = true - c <- l - return + return *l, true } case '(': - brace++ + zl.brace++ } default: escape = false - if commt { + + if zl.commt { com[comi] = x comi++ break } + str[stri] = x stri++ - space = false + + zl.space = false } - x, err = s.tokenText() } + + if zl.readErr != nil && zl.readErr != io.EOF { + // Don't return any tokens after a read error occurs. + return lex{value: zEOF}, false + } + + var retL lex if stri > 0 { - // Send remainder - l.token = string(str[:stri]) - l.tokenUpper = strings.ToUpper(l.token) - l.length = stri + // Send remainder of str l.value = zString - c <- l + l.token = string(str[:stri]) + retL = *l + + if comi <= 0 { + return retL, true + } } - if brace != 0 { + + if comi > 0 { + // Send remainder of com + l.value = zNewline + l.token = "\n" + l.comment = string(com[:comi]) + + if retL != (lex{}) { + zl.nextL = true + return retL, true + } + + return *l, true + } + + if zl.brace != 0 { + l.comment = "" // in case there was left over string and comment l.token = "unbalanced brace" - l.tokenUpper = l.token l.err = true - c <- l + return *l, true } + + return lex{value: zEOF}, false } // Extract the class number from CLASSxx @@ -969,12 +1293,12 @@ func locCheckEast(token string, longitude uint32) (uint32, bool) { } // "Eat" the rest of the "line". Return potential comments -func slurpRemainder(c chan lex, f string) (*ParseError, string) { - l := <-c +func slurpRemainder(c *zlexer, f string) (*ParseError, string) { + l, _ := c.Next() com := "" switch l.value { case zBlank: - l = <-c + l, _ = c.Next() com = l.comment if l.value != zNewline && l.value != zEOF { return &ParseError{f, "garbage after rdata", l}, "" diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go index f4ccc8424..935d22c3f 100644 --- a/vendor/github.com/miekg/dns/scan_rr.go +++ b/vendor/github.com/miekg/dns/scan_rr.go @@ -11,7 +11,7 @@ type parserFunc struct { // Func defines the function that parses the tokens and returns the RR // or an error. The last string contains any comments in the line as // they returned by the lexer as well. - Func func(h RR_Header, c chan lex, origin string, file string) (RR, *ParseError, string) + Func func(h RR_Header, c *zlexer, origin string, file string) (RR, *ParseError, string) // Signals if the RR ending is of variable length, like TXT or records // that have Hexadecimal or Base64 as their last element in the Rdata. Records // that have a fixed ending or for instance A, AAAA, SOA and etc. @@ -23,7 +23,7 @@ type parserFunc struct { // After the rdata there may come a zBlank and then a zNewline // or immediately a zNewline. If this is not the case we flag // an *ParseError: garbage after rdata. -func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setRR(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { parserfunc, ok := typeToparserFunc[h.Rrtype] if ok { r, e, cm := parserfunc.Func(h, c, o, f) @@ -45,9 +45,9 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { // A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces) // or an error -func endingToString(c chan lex, errstr, f string) (string, *ParseError, string) { +func endingToString(c *zlexer, errstr, f string) (string, *ParseError, string) { s := "" - l := <-c // zString + l, _ := c.Next() // zString for l.value != zNewline && l.value != zEOF { if l.err { return s, &ParseError{f, errstr, l}, "" @@ -59,16 +59,16 @@ func endingToString(c chan lex, errstr, f string) (string, *ParseError, string) default: return "", &ParseError{f, errstr, l}, "" } - l = <-c + l, _ = c.Next() } return s, nil, l.comment } // A remainder of the rdata with embedded spaces, split on unquoted whitespace // and return the parsed string slice or an error -func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, string) { +func endingToTxtSlice(c *zlexer, errstr, f string) ([]string, *ParseError, string) { // Get the remaining data until we see a zNewline - l := <-c + l, _ := c.Next() if l.err { return nil, &ParseError{f, errstr, l}, "" } @@ -117,7 +117,7 @@ func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, stri default: return nil, &ParseError{f, errstr, l}, "" } - l = <-c + l, _ = c.Next() } if quote { return nil, &ParseError{f, errstr, l}, "" @@ -125,12 +125,12 @@ func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, stri return s, nil, l.comment } -func setA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setA(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(A) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -141,12 +141,12 @@ func setA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setAAAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setAAAA(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(AAAA) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -157,13 +157,13 @@ func setAAAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setNS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNS(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NS) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Ns = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -175,13 +175,13 @@ func setNS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setPTR(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(PTR) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Ptr = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -193,13 +193,13 @@ func setPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setNSAPPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNSAPPTR(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NSAPPTR) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Ptr = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -211,13 +211,13 @@ func setNSAPPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) return rr, nil, "" } -func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setRP(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(RP) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Mbox = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -227,8 +227,8 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Mbox = mbox - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() rr.Txt = l.token txt, txtOk := toAbsoluteName(l.token, o) @@ -240,13 +240,13 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setMR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setMR(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(MR) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Mr = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -258,13 +258,13 @@ func setMR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setMB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setMB(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(MB) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Mb = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -276,13 +276,13 @@ func setMB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setMG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setMG(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(MG) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Mg = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -294,7 +294,7 @@ func setMG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setHINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setHINFO(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(HINFO) rr.Hdr = h @@ -320,13 +320,13 @@ func setHINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setMINFO(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(MINFO) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Rmail = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -336,8 +336,8 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Rmail = rmail - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() rr.Email = l.token email, emailOk := toAbsoluteName(l.token, o) @@ -349,13 +349,13 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setMF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setMF(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(MF) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Mf = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -367,13 +367,13 @@ func setMF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setMD(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setMD(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(MD) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Md = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -385,12 +385,12 @@ func setMD(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setMX(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(MX) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -400,8 +400,8 @@ func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Preference = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Mx = l.token name, nameOk := toAbsoluteName(l.token, o) @@ -413,12 +413,12 @@ func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setRT(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(RT) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -428,8 +428,8 @@ func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Preference = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Host = l.token name, nameOk := toAbsoluteName(l.token, o) @@ -441,12 +441,12 @@ func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setAFSDB(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(AFSDB) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -456,8 +456,8 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Subtype = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Hostname = l.token name, nameOk := toAbsoluteName(l.token, o) @@ -468,12 +468,12 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setX25(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setX25(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(X25) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -484,12 +484,12 @@ func setX25(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setKX(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(KX) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -499,8 +499,8 @@ func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Preference = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Exchanger = l.token name, nameOk := toAbsoluteName(l.token, o) @@ -511,13 +511,13 @@ func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setCNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setCNAME(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(CNAME) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Target = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -529,13 +529,13 @@ func setCNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setDNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setDNAME(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(DNAME) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Target = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -547,13 +547,13 @@ func setDNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setSOA(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(SOA) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.Ns = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -563,8 +563,8 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Ns = ns - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() rr.Mbox = l.token mbox, mboxOk := toAbsoluteName(l.token, o) @@ -573,14 +573,14 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Mbox = mbox - <-c // zBlank + c.Next() // zBlank var ( v uint32 ok bool ) for i := 0; i < 5; i++ { - l = <-c + l, _ = c.Next() if l.err { return nil, &ParseError{f, "bad SOA zone parameter", l}, "" } @@ -600,16 +600,16 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { switch i { case 0: rr.Serial = v - <-c // zBlank + c.Next() // zBlank case 1: rr.Refresh = v - <-c // zBlank + c.Next() // zBlank case 2: rr.Retry = v - <-c // zBlank + c.Next() // zBlank case 3: rr.Expire = v - <-c // zBlank + c.Next() // zBlank case 4: rr.Minttl = v } @@ -617,12 +617,12 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setSRV(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(SRV) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -632,24 +632,24 @@ func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Priority = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString i, e = strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return nil, &ParseError{f, "bad SRV Weight", l}, "" } rr.Weight = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString i, e = strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return nil, &ParseError{f, "bad SRV Port", l}, "" } rr.Port = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Target = l.token name, nameOk := toAbsoluteName(l.token, o) @@ -660,12 +660,12 @@ func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNAPTR(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NAPTR) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -675,8 +675,8 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Order = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString i, e = strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return nil, &ParseError{f, "bad NAPTR Preference", l}, "" @@ -684,15 +684,15 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.Preference = uint16(i) // Flags - <-c // zBlank - l = <-c // _QUOTE + c.Next() // zBlank + l, _ = c.Next() // _QUOTE if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Flags", l}, "" } - l = <-c // Either String or Quote + l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Flags = l.token - l = <-c // _QUOTE + l, _ = c.Next() // _QUOTE if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Flags", l}, "" } @@ -703,15 +703,15 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } // Service - <-c // zBlank - l = <-c // _QUOTE + c.Next() // zBlank + l, _ = c.Next() // _QUOTE if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Service", l}, "" } - l = <-c // Either String or Quote + l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Service = l.token - l = <-c // _QUOTE + l, _ = c.Next() // _QUOTE if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Service", l}, "" } @@ -722,15 +722,15 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } // Regexp - <-c // zBlank - l = <-c // _QUOTE + c.Next() // zBlank + l, _ = c.Next() // _QUOTE if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Regexp", l}, "" } - l = <-c // Either String or Quote + l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Regexp = l.token - l = <-c // _QUOTE + l, _ = c.Next() // _QUOTE if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Regexp", l}, "" } @@ -741,8 +741,8 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } // After quote no space?? - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Replacement = l.token name, nameOk := toAbsoluteName(l.token, o) @@ -753,13 +753,13 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setTALINK(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(TALINK) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.PreviousName = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -769,8 +769,8 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.PreviousName = previousName - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() rr.NextName = l.token nextName, nextNameOk := toAbsoluteName(l.token, o) @@ -782,7 +782,7 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setLOC(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(LOC) rr.Hdr = h // Non zero defaults for LOC record, see RFC 1876, Section 3. @@ -792,8 +792,8 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { ok := false // North - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } i, e := strconv.ParseUint(l.token, 10, 32) @@ -802,9 +802,9 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Latitude = 1000 * 60 * 60 * uint32(i) - <-c // zBlank + c.Next() // zBlank // Either number, 'N' or 'S' - l = <-c + l, _ = c.Next() if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } @@ -814,16 +814,16 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Latitude += 1000 * 60 * uint32(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { return nil, &ParseError{f, "bad LOC Latitude seconds", l}, "" } else { rr.Latitude += uint32(1000 * i) } - <-c // zBlank + c.Next() // zBlank // Either number, 'N' or 'S' - l = <-c + l, _ = c.Next() if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } @@ -832,16 +832,16 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { East: // East - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err { return nil, &ParseError{f, "bad LOC Longitude", l}, "" } else { rr.Longitude = 1000 * 60 * 60 * uint32(i) } - <-c // zBlank + c.Next() // zBlank // Either number, 'E' or 'W' - l = <-c + l, _ = c.Next() if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } @@ -850,16 +850,16 @@ East: } else { rr.Longitude += 1000 * 60 * uint32(i) } - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { return nil, &ParseError{f, "bad LOC Longitude seconds", l}, "" } else { rr.Longitude += uint32(1000 * i) } - <-c // zBlank + c.Next() // zBlank // Either number, 'E' or 'W' - l = <-c + l, _ = c.Next() if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } @@ -867,9 +867,9 @@ East: return nil, &ParseError{f, "bad LOC Longitude East/West", l}, "" Altitude: - <-c // zBlank - l = <-c - if l.length == 0 || l.err { + c.Next() // zBlank + l, _ = c.Next() + if len(l.token) == 0 || l.err { return nil, &ParseError{f, "bad LOC Altitude", l}, "" } if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { @@ -882,7 +882,7 @@ Altitude: } // And now optionally the other values - l = <-c + l, _ = c.Next() count := 0 for l.value != zNewline && l.value != zEOF { switch l.value { @@ -893,19 +893,19 @@ Altitude: if !ok { return nil, &ParseError{f, "bad LOC Size", l}, "" } - rr.Size = (e & 0x0f) | (m << 4 & 0xf0) + rr.Size = e&0x0f | m<<4&0xf0 case 1: // HorizPre e, m, ok := stringToCm(l.token) if !ok { return nil, &ParseError{f, "bad LOC HorizPre", l}, "" } - rr.HorizPre = (e & 0x0f) | (m << 4 & 0xf0) + rr.HorizPre = e&0x0f | m<<4&0xf0 case 2: // VertPre e, m, ok := stringToCm(l.token) if !ok { return nil, &ParseError{f, "bad LOC VertPre", l}, "" } - rr.VertPre = (e & 0x0f) | (m << 4 & 0xf0) + rr.VertPre = e&0x0f | m<<4&0xf0 } count++ case zBlank: @@ -913,18 +913,18 @@ Altitude: default: return nil, &ParseError{f, "bad LOC Size, HorizPre or VertPre", l}, "" } - l = <-c + l, _ = c.Next() } return rr, nil, "" } -func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setHIP(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(HIP) rr.Hdr = h // HitLength is not represented - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -934,24 +934,24 @@ func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.PublicKeyAlgorithm = uint8(i) - <-c // zBlank - l = <-c // zString - if l.length == 0 || l.err { + c.Next() // zBlank + l, _ = c.Next() // zString + if len(l.token) == 0 || l.err { return nil, &ParseError{f, "bad HIP Hit", l}, "" } rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. rr.HitLength = uint8(len(rr.Hit)) / 2 - <-c // zBlank - l = <-c // zString - if l.length == 0 || l.err { + c.Next() // zBlank + l, _ = c.Next() // zString + if len(l.token) == 0 || l.err { return nil, &ParseError{f, "bad HIP PublicKey", l}, "" } rr.PublicKey = l.token // This cannot contain spaces rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey))) // RendezvousServers (if any) - l = <-c + l, _ = c.Next() var xs []string for l.value != zNewline && l.value != zEOF { switch l.value { @@ -966,18 +966,18 @@ func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { default: return nil, &ParseError{f, "bad HIP RendezvousServers", l}, "" } - l = <-c + l, _ = c.Next() } rr.RendezvousServers = xs return rr, nil, l.comment } -func setCERT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setCERT(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(CERT) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -988,15 +988,15 @@ func setCERT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } else { rr.Type = uint16(i) } - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return nil, &ParseError{f, "bad CERT KeyTag", l}, "" } rr.KeyTag = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString if v, ok := StringToAlgorithm[l.token]; ok { rr.Algorithm = v } else if i, e := strconv.ParseUint(l.token, 10, 8); e != nil { @@ -1012,7 +1012,7 @@ func setCERT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setOPENPGPKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setOPENPGPKEY(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(OPENPGPKEY) rr.Hdr = h @@ -1024,12 +1024,12 @@ func setOPENPGPKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, strin return rr, nil, c1 } -func setCSYNC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setCSYNC(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(CSYNC) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } j, e := strconv.ParseUint(l.token, 10, 32) @@ -1039,9 +1039,9 @@ func setCSYNC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Serial = uint32(j) - <-c // zBlank + c.Next() // zBlank - l = <-c + l, _ = c.Next() j, e = strconv.ParseUint(l.token, 10, 16) if e != nil { // Serial must be a number @@ -1054,14 +1054,15 @@ func setCSYNC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { k uint16 ok bool ) - l = <-c + l, _ = c.Next() for l.value != zNewline && l.value != zEOF { switch l.value { case zBlank: // Ok case zString: - if k, ok = StringToType[l.tokenUpper]; !ok { - if k, ok = typeToInt(l.tokenUpper); !ok { + tokenUpper := strings.ToUpper(l.token) + if k, ok = StringToType[tokenUpper]; !ok { + if k, ok = typeToInt(l.token); !ok { return nil, &ParseError{f, "bad CSYNC TypeBitMap", l}, "" } } @@ -1069,12 +1070,12 @@ func setCSYNC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { default: return nil, &ParseError{f, "bad CSYNC TypeBitMap", l}, "" } - l = <-c + l, _ = c.Next() } return rr, nil, l.comment } -func setSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setSIG(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { r, e, s := setRRSIG(h, c, o, f) if r != nil { return &SIG{*r.(*RRSIG)}, e, s @@ -1082,18 +1083,19 @@ func setSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, e, s } -func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setRRSIG(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(RRSIG) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } - if t, ok := StringToType[l.tokenUpper]; !ok { - if strings.HasPrefix(l.tokenUpper, "TYPE") { - t, ok = typeToInt(l.tokenUpper) + tokenUpper := strings.ToUpper(l.token) + if t, ok := StringToType[tokenUpper]; !ok { + if strings.HasPrefix(tokenUpper, "TYPE") { + t, ok = typeToInt(l.token) if !ok { return nil, &ParseError{f, "bad RRSIG Typecovered", l}, "" } @@ -1105,32 +1107,32 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.TypeCovered = t } - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, err := strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return nil, &ParseError{f, "bad RRSIG Algorithm", l}, "" } rr.Algorithm = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, err = strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return nil, &ParseError{f, "bad RRSIG Labels", l}, "" } rr.Labels = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, err = strconv.ParseUint(l.token, 10, 32) if err != nil || l.err { return nil, &ParseError{f, "bad RRSIG OrigTtl", l}, "" } rr.OrigTtl = uint32(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if i, err := StringToTime(l.token); err != nil { // Try to see if all numeric and use it as epoch if i, err := strconv.ParseInt(l.token, 10, 64); err == nil { @@ -1143,8 +1145,8 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.Expiration = i } - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if i, err := StringToTime(l.token); err != nil { if i, err := strconv.ParseInt(l.token, 10, 64); err == nil { rr.Inception = uint32(i) @@ -1155,16 +1157,16 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.Inception = i } - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, err = strconv.ParseUint(l.token, 10, 16) if err != nil || l.err { return nil, &ParseError{f, "bad RRSIG KeyTag", l}, "" } rr.KeyTag = uint16(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() rr.SignerName = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { @@ -1181,13 +1183,13 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNSEC(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NSEC) rr.Hdr = h - l := <-c + l, _ := c.Next() rr.NextDomain = l.token - if l.length == 0 { // dynamic update rr. + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -1202,14 +1204,15 @@ func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { k uint16 ok bool ) - l = <-c + l, _ = c.Next() for l.value != zNewline && l.value != zEOF { switch l.value { case zBlank: // Ok case zString: - if k, ok = StringToType[l.tokenUpper]; !ok { - if k, ok = typeToInt(l.tokenUpper); !ok { + tokenUpper := strings.ToUpper(l.token) + if k, ok = StringToType[tokenUpper]; !ok { + if k, ok = typeToInt(l.token); !ok { return nil, &ParseError{f, "bad NSEC TypeBitMap", l}, "" } } @@ -1217,17 +1220,17 @@ func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { default: return nil, &ParseError{f, "bad NSEC TypeBitMap", l}, "" } - l = <-c + l, _ = c.Next() } return rr, nil, l.comment } -func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNSEC3(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NSEC3) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -1236,30 +1239,32 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad NSEC3 Hash", l}, "" } rr.Hash = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3 Flags", l}, "" } rr.Flags = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3 Iterations", l}, "" } rr.Iterations = uint16(i) - <-c - l = <-c + c.Next() + l, _ = c.Next() if len(l.token) == 0 || l.err { return nil, &ParseError{f, "bad NSEC3 Salt", l}, "" } - rr.SaltLength = uint8(len(l.token)) / 2 - rr.Salt = l.token + if l.token != "-" { + rr.SaltLength = uint8(len(l.token)) / 2 + rr.Salt = l.token + } - <-c - l = <-c + c.Next() + l, _ = c.Next() if len(l.token) == 0 || l.err { return nil, &ParseError{f, "bad NSEC3 NextDomain", l}, "" } @@ -1271,14 +1276,15 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { k uint16 ok bool ) - l = <-c + l, _ = c.Next() for l.value != zNewline && l.value != zEOF { switch l.value { case zBlank: // Ok case zString: - if k, ok = StringToType[l.tokenUpper]; !ok { - if k, ok = typeToInt(l.tokenUpper); !ok { + tokenUpper := strings.ToUpper(l.token) + if k, ok = StringToType[tokenUpper]; !ok { + if k, ok = typeToInt(l.token); !ok { return nil, &ParseError{f, "bad NSEC3 TypeBitMap", l}, "" } } @@ -1286,17 +1292,17 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { default: return nil, &ParseError{f, "bad NSEC3 TypeBitMap", l}, "" } - l = <-c + l, _ = c.Next() } return rr, nil, l.comment } -func setNSEC3PARAM(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNSEC3PARAM(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NSEC3PARAM) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1305,37 +1311,39 @@ func setNSEC3PARAM(h RR_Header, c chan lex, o, f string) (RR, *ParseError, strin return nil, &ParseError{f, "bad NSEC3PARAM Hash", l}, "" } rr.Hash = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3PARAM Flags", l}, "" } rr.Flags = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3PARAM Iterations", l}, "" } rr.Iterations = uint16(i) - <-c - l = <-c - rr.SaltLength = uint8(len(l.token)) - rr.Salt = l.token + c.Next() + l, _ = c.Next() + if l.token != "-" { + rr.SaltLength = uint8(len(l.token)) + rr.Salt = l.token + } return rr, nil, "" } -func setEUI48(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setEUI48(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(EUI48) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } - if l.length != 17 || l.err { + if len(l.token) != 17 || l.err { return nil, &ParseError{f, "bad EUI48 Address", l}, "" } addr := make([]byte, 12) @@ -1359,16 +1367,16 @@ func setEUI48(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setEUI64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setEUI64(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(EUI64) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } - if l.length != 23 || l.err { + if len(l.token) != 23 || l.err { return nil, &ParseError{f, "bad EUI64 Address", l}, "" } addr := make([]byte, 16) @@ -1392,12 +1400,12 @@ func setEUI64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setSSHFP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setSSHFP(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(SSHFP) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1406,14 +1414,14 @@ func setSSHFP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad SSHFP Algorithm", l}, "" } rr.Algorithm = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad SSHFP Type", l}, "" } rr.Type = uint8(i) - <-c // zBlank + c.Next() // zBlank s, e1, c1 := endingToString(c, "bad SSHFP Fingerprint", f) if e1 != nil { return nil, e1, c1 @@ -1422,12 +1430,12 @@ func setSSHFP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setDNSKEYs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string) { +func setDNSKEYs(h RR_Header, c *zlexer, o, f, typ string) (RR, *ParseError, string) { rr := new(DNSKEY) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -1436,15 +1444,15 @@ func setDNSKEYs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, str return nil, &ParseError{f, "bad " + typ + " Flags", l}, "" } rr.Flags = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad " + typ + " Protocol", l}, "" } rr.Protocol = uint8(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, "" @@ -1458,7 +1466,7 @@ func setDNSKEYs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, str return rr, nil, c1 } -func setKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setKEY(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { r, e, s := setDNSKEYs(h, c, o, f, "KEY") if r != nil { return &KEY{*r.(*DNSKEY)}, e, s @@ -1466,12 +1474,12 @@ func setKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, e, s } -func setDNSKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setDNSKEY(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { r, e, s := setDNSKEYs(h, c, o, f, "DNSKEY") return r, e, s } -func setCDNSKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setCDNSKEY(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { r, e, s := setDNSKEYs(h, c, o, f, "CDNSKEY") if r != nil { return &CDNSKEY{*r.(*DNSKEY)}, e, s @@ -1479,12 +1487,12 @@ func setCDNSKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) return nil, e, s } -func setRKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setRKEY(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(RKEY) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -1493,15 +1501,15 @@ func setRKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad RKEY Flags", l}, "" } rr.Flags = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad RKEY Protocol", l}, "" } rr.Protocol = uint8(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad RKEY Algorithm", l}, "" @@ -1515,7 +1523,7 @@ func setRKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setEID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setEID(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(EID) rr.Hdr = h s, e, c1 := endingToString(c, "bad EID Endpoint", f) @@ -1526,7 +1534,7 @@ func setEID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setNIMLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNIMLOC(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NIMLOC) rr.Hdr = h s, e, c1 := endingToString(c, "bad NIMLOC Locator", f) @@ -1537,12 +1545,12 @@ func setNIMLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setGPOS(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(GPOS) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1551,15 +1559,15 @@ func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad GPOS Longitude", l}, "" } rr.Longitude = l.token - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() _, e = strconv.ParseFloat(l.token, 64) if e != nil || l.err { return nil, &ParseError{f, "bad GPOS Latitude", l}, "" } rr.Latitude = l.token - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() _, e = strconv.ParseFloat(l.token, 64) if e != nil || l.err { return nil, &ParseError{f, "bad GPOS Altitude", l}, "" @@ -1568,12 +1576,12 @@ func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string) { +func setDSs(h RR_Header, c *zlexer, o, f, typ string) (RR, *ParseError, string) { rr := new(DS) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -1582,10 +1590,11 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string) return nil, &ParseError{f, "bad " + typ + " KeyTag", l}, "" } rr.KeyTag = uint16(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if i, e = strconv.ParseUint(l.token, 10, 8); e != nil { - i, ok := StringToAlgorithm[l.tokenUpper] + tokenUpper := strings.ToUpper(l.token) + i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, "" } @@ -1593,8 +1602,8 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string) } else { rr.Algorithm = uint8(i) } - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad " + typ + " DigestType", l}, "" @@ -1608,12 +1617,12 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string) return rr, nil, c1 } -func setDS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setDS(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { r, e, s := setDSs(h, c, o, f, "DS") return r, e, s } -func setDLV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setDLV(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { r, e, s := setDSs(h, c, o, f, "DLV") if r != nil { return &DLV{*r.(*DS)}, e, s @@ -1621,7 +1630,7 @@ func setDLV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, e, s } -func setCDS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setCDS(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { r, e, s := setDSs(h, c, o, f, "CDS") if r != nil { return &CDS{*r.(*DS)}, e, s @@ -1629,12 +1638,12 @@ func setCDS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, e, s } -func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setTA(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(TA) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -1643,10 +1652,11 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad TA KeyTag", l}, "" } rr.KeyTag = uint16(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if i, e := strconv.ParseUint(l.token, 10, 8); e != nil { - i, ok := StringToAlgorithm[l.tokenUpper] + tokenUpper := strings.ToUpper(l.token) + i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { return nil, &ParseError{f, "bad TA Algorithm", l}, "" } @@ -1654,27 +1664,27 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } else { rr.Algorithm = uint8(i) } - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad TA DigestType", l}, "" } rr.DigestType = uint8(i) - s, e, c1 := endingToString(c, "bad TA Digest", f) - if e != nil { - return nil, e.(*ParseError), c1 + s, err, c1 := endingToString(c, "bad TA Digest", f) + if err != nil { + return nil, err, c1 } rr.Digest = s return rr, nil, c1 } -func setTLSA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setTLSA(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(TLSA) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -1683,15 +1693,15 @@ func setTLSA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad TLSA Usage", l}, "" } rr.Usage = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad TLSA Selector", l}, "" } rr.Selector = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad TLSA MatchingType", l}, "" @@ -1706,12 +1716,12 @@ func setTLSA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setSMIMEA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setSMIMEA(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(SMIMEA) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -1720,15 +1730,15 @@ func setSMIMEA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad SMIMEA Usage", l}, "" } rr.Usage = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad SMIMEA Selector", l}, "" } rr.Selector = uint8(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { return nil, &ParseError{f, "bad SMIMEA MatchingType", l}, "" @@ -1743,17 +1753,17 @@ func setSMIMEA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setRFC3597(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setRFC3597(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(RFC3597) rr.Hdr = h - l := <-c + l, _ := c.Next() if l.token != "\\#" { return nil, &ParseError{f, "bad RFC3597 Rdata", l}, "" } - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() rdlength, e := strconv.Atoi(l.token) if e != nil || l.err { return nil, &ParseError{f, "bad RFC3597 Rdata ", l}, "" @@ -1770,7 +1780,7 @@ func setRFC3597(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) return rr, nil, c1 } -func setSPF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setSPF(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(SPF) rr.Hdr = h @@ -1782,7 +1792,7 @@ func setSPF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setAVC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setAVC(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(AVC) rr.Hdr = h @@ -1794,7 +1804,7 @@ func setAVC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setTXT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setTXT(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(TXT) rr.Hdr = h @@ -1808,7 +1818,7 @@ func setTXT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } // identical to setTXT -func setNINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNINFO(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NINFO) rr.Hdr = h @@ -1820,12 +1830,12 @@ func setNINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setURI(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(URI) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1834,15 +1844,15 @@ func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad URI Priority", l}, "" } rr.Priority = uint16(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() i, e = strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { return nil, &ParseError{f, "bad URI Weight", l}, "" } rr.Weight = uint16(i) - <-c // zBlank + c.Next() // zBlank s, err, c1 := endingToTxtSlice(c, "bad URI Target", f) if err != nil { return nil, err, "" @@ -1854,7 +1864,7 @@ func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setDHCID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setDHCID(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { // awesome record to parse! rr := new(DHCID) rr.Hdr = h @@ -1867,12 +1877,12 @@ func setDHCID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setNID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setNID(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(NID) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1881,8 +1891,8 @@ func setNID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad NID Preference", l}, "" } rr.Preference = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString u, err := stringToNodeID(l) if err != nil || l.err { return nil, err, "" @@ -1891,12 +1901,12 @@ func setNID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setL32(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setL32(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(L32) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1905,8 +1915,8 @@ func setL32(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad L32 Preference", l}, "" } rr.Preference = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Locator32 = net.ParseIP(l.token) if rr.Locator32 == nil || l.err { return nil, &ParseError{f, "bad L32 Locator", l}, "" @@ -1914,12 +1924,12 @@ func setL32(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setLP(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(LP) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1929,8 +1939,8 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Preference = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Fqdn = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { @@ -1941,12 +1951,12 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setL64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setL64(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(L64) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1955,8 +1965,8 @@ func setL64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return nil, &ParseError{f, "bad L64 Preference", l}, "" } rr.Preference = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString u, err := stringToNodeID(l) if err != nil || l.err { return nil, err, "" @@ -1965,12 +1975,12 @@ func setL64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setUID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setUID(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(UID) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1982,12 +1992,12 @@ func setUID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setGID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setGID(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(GID) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -1999,7 +2009,7 @@ func setGID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setUINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setUINFO(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(UINFO) rr.Hdr = h @@ -2014,12 +2024,12 @@ func setUINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setPX(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(PX) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, "" } @@ -2029,8 +2039,8 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Preference = uint16(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Map822 = l.token map822, map822Ok := toAbsoluteName(l.token, o) if l.err || !map822Ok { @@ -2038,8 +2048,8 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Map822 = map822 - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString rr.Mapx400 = l.token mapx400, mapx400Ok := toAbsoluteName(l.token, o) if l.err || !mapx400Ok { @@ -2050,12 +2060,12 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setCAA(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(CAA) rr.Hdr = h - l := <-c - if l.length == 0 { // dynamic update rr. + l, _ := c.Next() + if len(l.token) == 0 { // dynamic update rr. return rr, nil, l.comment } @@ -2065,14 +2075,14 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } rr.Flag = uint8(i) - <-c // zBlank - l = <-c // zString + c.Next() // zBlank + l, _ = c.Next() // zString if l.value != zString { return nil, &ParseError{f, "bad CAA Tag", l}, "" } rr.Tag = l.token - <-c // zBlank + c.Next() // zBlank s, e, c1 := endingToTxtSlice(c, "bad CAA Value", f) if e != nil { return nil, e, "" @@ -2084,43 +2094,43 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setTKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { +func setTKEY(h RR_Header, c *zlexer, o, f string) (RR, *ParseError, string) { rr := new(TKEY) rr.Hdr = h - l := <-c + l, _ := c.Next() // Algorithm if l.value != zString { return nil, &ParseError{f, "bad TKEY algorithm", l}, "" } rr.Algorithm = l.token - <-c // zBlank + c.Next() // zBlank // Get the key length and key values - l = <-c + l, _ = c.Next() i, err := strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return nil, &ParseError{f, "bad TKEY key length", l}, "" } rr.KeySize = uint16(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if l.value != zString { return nil, &ParseError{f, "bad TKEY key", l}, "" } rr.Key = l.token - <-c // zBlank + c.Next() // zBlank // Get the otherdata length and string data - l = <-c + l, _ = c.Next() i, err = strconv.ParseUint(l.token, 10, 8) if err != nil || l.err { return nil, &ParseError{f, "bad TKEY otherdata length", l}, "" } rr.OtherLen = uint16(i) - <-c // zBlank - l = <-c + c.Next() // zBlank + l, _ = c.Next() if l.value != zString { return nil, &ParseError{f, "bad TKEY otherday", l}, "" } diff --git a/vendor/github.com/miekg/dns/scanner.go b/vendor/github.com/miekg/dns/scanner.go deleted file mode 100644 index 424e5af9f..000000000 --- a/vendor/github.com/miekg/dns/scanner.go +++ /dev/null @@ -1,56 +0,0 @@ -package dns - -// Implement a simple scanner, return a byte stream from an io reader. - -import ( - "bufio" - "context" - "io" - "text/scanner" -) - -type scan struct { - src *bufio.Reader - position scanner.Position - eof bool // Have we just seen a eof - ctx context.Context -} - -func scanInit(r io.Reader) (*scan, context.CancelFunc) { - s := new(scan) - s.src = bufio.NewReader(r) - s.position.Line = 1 - - ctx, cancel := context.WithCancel(context.Background()) - s.ctx = ctx - - return s, cancel -} - -// 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 - } - select { - case <-s.ctx.Done(): - return c, context.Canceled - default: - break - } - - // 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 -} diff --git a/vendor/github.com/miekg/dns/serve_mux.go b/vendor/github.com/miekg/dns/serve_mux.go new file mode 100644 index 000000000..ae304db53 --- /dev/null +++ b/vendor/github.com/miekg/dns/serve_mux.go @@ -0,0 +1,147 @@ +package dns + +import ( + "strings" + "sync" +) + +// 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. +// +// The zero ServeMux is empty and ready for use. +type ServeMux struct { + z map[string]Handler + m sync.RWMutex +} + +// NewServeMux allocates and returns a new ServeMux. +func NewServeMux() *ServeMux { + return new(ServeMux) +} + +// DefaultServeMux is the default ServeMux used by Serve. +var DefaultServeMux = NewServeMux() + +func (mux *ServeMux) match(q string, t uint16) Handler { + mux.m.RLock() + defer mux.m.RUnlock() + if mux.z == nil { + return nil + } + + var handler Handler + + // TODO(tmthrgd): Once https://go-review.googlesource.com/c/go/+/137575 + // lands in a go release, replace the following with strings.ToLower. + var sb strings.Builder + for i := 0; i < len(q); i++ { + c := q[i] + if !(c >= 'A' && c <= 'Z') { + continue + } + + sb.Grow(len(q)) + sb.WriteString(q[:i]) + + for ; i < len(q); i++ { + c := q[i] + if c >= 'A' && c <= 'Z' { + c += 'a' - 'A' + } + + sb.WriteByte(c) + } + + q = sb.String() + break + } + + for off, end := 0, false; !end; off, end = NextLabel(q, off) { + if h, ok := mux.z[q[off:]]; ok { + if t != TypeDS { + return h + } + // Continue for DS to see if we have a parent too, if so delegate to the parent + handler = h + } + } + + // 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() + if mux.z == nil { + mux.z = make(map[string]Handler) + } + 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 deregisters 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. +// +// ServeDNS 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. +// +// If no handler is found, or there is no question, a standard SERVFAIL +// message is returned +func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) { + var h Handler + if len(req.Question) >= 1 { // allow more than one question + h = mux.match(req.Question[0].Name, req.Question[0].Qtype) + } + + if h != nil { + h.ServeDNS(w, req) + } else { + HandleFailed(w, req) + } +} + +// 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) +} diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index 685753f43..6abbed512 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -4,22 +4,54 @@ package dns import ( "bytes" + "context" "crypto/tls" "encoding/binary" + "errors" "io" "net" + "strings" "sync" + "sync/atomic" "time" ) -// Maximum number of TCP queries before we close the socket. +// Default maximum number of TCP queries before we close the socket. const maxTCPQueries = 128 +// The maximum number of idle workers. +// +// This controls the maximum number of workers that are allowed to stay +// idle waiting for incoming requests before being torn down. +// +// If this limit is reached, the server will just keep spawning new +// workers (goroutines) for each incoming request. In this case, each +// worker will only be used for a single request. +const maxIdleWorkersCount = 10000 + +// The maximum length of time a worker may idle for before being destroyed. +const idleWorkerTimeout = 10 * time.Second + +// aLongTimeAgo is a non-zero time, far in the past, used for +// immediate cancelation of network operations. +var aLongTimeAgo = time.Unix(1, 0) + // Handler is implemented by any value that implements ServeDNS. type Handler interface { ServeDNS(w ResponseWriter, r *Msg) } +// 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) +} + // A ResponseWriter interface is used by an DNS handler to // construct an DNS response. type ResponseWriter interface { @@ -42,46 +74,25 @@ type ResponseWriter interface { Hijack() } +// A ConnectionStater interface is used by a DNS Handler to access TLS connection state +// when available. +type ConnectionStater interface { + ConnectionState() *tls.ConnectionState +} + type response struct { + msg []byte + closed bool // connection has been closed hijacked bool // connection has been hijacked by handler - tsigStatus error tsigTimersOnly bool + tsigStatus error 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) + wg *sync.WaitGroup // for gracefull shutdown } // HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets. @@ -92,8 +103,6 @@ func HandleFailed(w ResponseWriter, r *Msg) { 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 { @@ -132,99 +141,6 @@ func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error { 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 @@ -287,87 +203,170 @@ type Server struct { IdleTimeout func() time.Duration // Secret(s) for Tsig map[]. The zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2). 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 + // Maximum number of TCP queries before we close the socket. Default is maxTCPQueries (unlimited if -1). + MaxTCPQueries int + // Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address. + // It is only supported on go1.11+ and when using ListenAndServe. + ReusePort bool + // AcceptMsgFunc will check the incoming message and will reject it early in the process. + // By default DefaultMsgAcceptFunc will be used. + MsgAcceptFunc MsgAcceptFunc + + // UDP packet or TCP connection queue + queue chan *response + // Workers count + workersCount int32 // Shutdown handling - lock sync.RWMutex - started bool + lock sync.RWMutex + started bool + shutdown chan struct{} + conns map[net.Conn]struct{} + + // A pool for UDP message buffers. + udpPool sync.Pool +} + +func (srv *Server) isStarted() bool { + srv.lock.RLock() + started := srv.started + srv.lock.RUnlock() + return started +} + +func (srv *Server) worker(w *response) { + srv.serve(w) + + for { + count := atomic.LoadInt32(&srv.workersCount) + if count > maxIdleWorkersCount { + return + } + if atomic.CompareAndSwapInt32(&srv.workersCount, count, count+1) { + break + } + } + + defer atomic.AddInt32(&srv.workersCount, -1) + + inUse := false + timeout := time.NewTimer(idleWorkerTimeout) + defer timeout.Stop() +LOOP: + for { + select { + case w, ok := <-srv.queue: + if !ok { + break LOOP + } + inUse = true + srv.serve(w) + case <-timeout.C: + if !inUse { + break LOOP + } + inUse = false + timeout.Reset(idleWorkerTimeout) + } + } +} + +func (srv *Server) spawnWorker(w *response) { + select { + case srv.queue <- w: + default: + go srv.worker(w) + } +} + +func makeUDPBuffer(size int) func() interface{} { + return func() interface{} { + return make([]byte, size) + } +} + +func (srv *Server) init() { + srv.queue = make(chan *response) + + srv.shutdown = make(chan struct{}) + srv.conns = make(map[net.Conn]struct{}) + + if srv.UDPSize == 0 { + srv.UDPSize = MinMsgSize + } + if srv.MsgAcceptFunc == nil { + srv.MsgAcceptFunc = defaultMsgAcceptFunc + } + + srv.udpPool.New = makeUDPBuffer(srv.UDPSize) +} + +func unlockOnce(l sync.Locker) func() { + var once sync.Once + return func() { once.Do(l.Unlock) } } // ListenAndServe starts a nameserver on the configured address in *Server. func (srv *Server) ListenAndServe() error { + unlock := unlockOnce(&srv.lock) srv.lock.Lock() - defer srv.lock.Unlock() + defer unlock() + if srv.started { return &Error{err: "server already started"} } + addr := srv.Addr if addr == "" { addr = ":domain" } - if srv.UDPSize == 0 { - srv.UDPSize = MinMsgSize - } + + srv.init() + defer close(srv.queue) + 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) + l, err := listenTCP(srv.Net, addr, srv.ReusePort) 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 + unlock() + return srv.serveTCP(l) case "tcp-tls", "tcp4-tls", "tcp6-tls": - network := "tcp" - if srv.Net == "tcp4-tls" { - network = "tcp4" - } else if srv.Net == "tcp6-tls" { - network = "tcp6" + if srv.TLSConfig == nil || (len(srv.TLSConfig.Certificates) == 0 && srv.TLSConfig.GetCertificate == nil) { + return errors.New("dns: neither Certificates nor GetCertificate set in Config") } - - l, err := tls.Listen(network, addr, srv.TLSConfig) + network := strings.TrimSuffix(srv.Net, "-tls") + l, err := listenTCP(network, addr, srv.ReusePort) if err != nil { return err } + l = tls.NewListener(l, srv.TLSConfig) 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 + unlock() + return srv.serveTCP(l) case "udp", "udp4", "udp6": - a, err := net.ResolveUDPAddr(srv.Net, addr) + l, err := listenUDP(srv.Net, addr, srv.ReusePort) if err != nil { return err } - l, err := net.ListenUDP(srv.Net, a) - if err != nil { - return err - } - if e := setUDPSocketOptions(l); e != nil { + u := l.(*net.UDPConn) + if e := setUDPSocketOptions(u); 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 + unlock() + return srv.serveUDP(u) } return &Error{err: "bad network"} } @@ -375,17 +374,20 @@ func (srv *Server) ListenAndServe() error { // 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 { + unlock := unlockOnce(&srv.lock) srv.lock.Lock() - defer srv.lock.Unlock() + defer unlock() + if srv.started { return &Error{err: "server already started"} } + + srv.init() + defer close(srv.queue) + 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 { @@ -393,18 +395,14 @@ func (srv *Server) ActivateAndServe() error { return e } srv.started = true - srv.lock.Unlock() - e := srv.serveUDP(t) - srv.lock.Lock() // to satisfy the defer at the top - return e + unlock() + return srv.serveUDP(t) } } 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 + unlock() + return srv.serveTCP(l) } return &Error{err: "bad listeners"} } @@ -412,23 +410,57 @@ func (srv *Server) ActivateAndServe() error { // Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and // ActivateAndServe will return. func (srv *Server) Shutdown() error { + return srv.ShutdownContext(context.Background()) +} + +// ShutdownContext shuts down a server. After a call to ShutdownContext, +// ListenAndServe and ActivateAndServe will return. +// +// A context.Context may be passed to limit how long to wait for connections +// to terminate. +func (srv *Server) ShutdownContext(ctx context.Context) error { srv.lock.Lock() if !srv.started { srv.lock.Unlock() return &Error{err: "server not started"} } + srv.started = false + + if srv.PacketConn != nil { + srv.PacketConn.SetReadDeadline(aLongTimeAgo) // Unblock reads + } + + if srv.Listener != nil { + srv.Listener.Close() + } + + for rw := range srv.conns { + rw.SetReadDeadline(aLongTimeAgo) // Unblock reads + } + srv.lock.Unlock() + if testShutdownNotify != nil { + testShutdownNotify.Broadcast() + } + + var ctxErr error + select { + case <-srv.shutdown: + case <-ctx.Done(): + ctxErr = ctx.Err() + } + if srv.PacketConn != nil { srv.PacketConn.Close() } - if srv.Listener != nil { - srv.Listener.Close() - } - return nil + + return ctxErr } +var testShutdownNotify *sync.Cond + // 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 @@ -439,7 +471,6 @@ func (srv *Server) getReadTimeout() time.Duration { } // 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() @@ -447,44 +478,39 @@ func (srv *Server) serveTCP(l net.Listener) error { srv.NotifyStartedFunc() } - reader := Reader(&defaultReader{srv}) - if srv.DecorateReader != nil { - reader = srv.DecorateReader(reader) - } + var wg sync.WaitGroup + defer func() { + wg.Wait() + close(srv.shutdown) + }() - handler := srv.Handler - if handler == nil { - handler = DefaultServeMux - } - rtimeout := srv.getReadTimeout() - // deadline is not used here - for { + for srv.isStarted() { rw, err := l.Accept() - srv.lock.RLock() - if !srv.started { - srv.lock.RUnlock() - return nil - } - srv.lock.RUnlock() if err != nil { + if !srv.isStarted() { + return nil + } if neterr, ok := err.(net.Error); ok && neterr.Temporary() { continue } return err } - go func() { - m, err := reader.ReadTCP(rw, rtimeout) - if err != nil { - rw.Close() - return - } - srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw) - }() + srv.lock.Lock() + // Track the connection to allow unblocking reads on shutdown. + srv.conns[rw] = struct{}{} + srv.lock.Unlock() + wg.Add(1) + srv.spawnWorker(&response{ + tsigSecret: srv.TsigSecret, + tcp: rw, + wg: &wg, + }) } + + return nil } // 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() @@ -497,107 +523,182 @@ func (srv *Server) serveUDP(l *net.UDPConn) error { reader = srv.DecorateReader(reader) } - handler := srv.Handler - if handler == nil { - handler = DefaultServeMux - } + var wg sync.WaitGroup + defer func() { + wg.Wait() + close(srv.shutdown) + }() + rtimeout := srv.getReadTimeout() // deadline is not used here - for { + for srv.isStarted() { m, s, err := reader.ReadUDP(l, rtimeout) - srv.lock.RLock() - if !srv.started { - srv.lock.RUnlock() - return nil - } - srv.lock.RUnlock() if err != nil { + if !srv.isStarted() { + return nil + } if netErr, ok := err.(net.Error); ok && netErr.Temporary() { continue } return err } if len(m) < headerSize { + if cap(m) == srv.UDPSize { + srv.udpPool.Put(m[:srv.UDPSize]) + } continue } - go srv.serve(s.RemoteAddr(), handler, m, l, s, nil) + wg.Add(1) + srv.spawnWorker(&response{ + msg: m, + tsigSecret: srv.TsigSecret, + udp: l, + udpSession: s, + wg: &wg, + }) } + + return nil } -// Serve a new connection. -func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t net.Conn) { - w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s} +func (srv *Server) serve(w *response) { if srv.DecorateWriter != nil { w.writer = srv.DecorateWriter(w) } else { w.writer = w } - q := 0 // counter for the amount of TCP queries we get + if w.udp != nil { + // serve UDP + srv.serveDNS(w) + + w.wg.Done() + return + } + + defer func() { + if !w.hijacked { + w.Close() + } + + srv.lock.Lock() + delete(srv.conns, w.tcp) + srv.lock.Unlock() + + w.wg.Done() + }() reader := Reader(&defaultReader{srv}) 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 + + idleTimeout := tcpIdleTimeout + if srv.IdleTimeout != nil { + idleTimeout = srv.IdleTimeout() } - if !srv.Unsafe && req.Response { - goto Exit + + timeout := srv.getReadTimeout() + + limit := srv.MaxTCPQueries + if limit == 0 { + limit = maxTCPQueries + } + + for q := 0; (q < limit || limit == -1) && srv.isStarted(); q++ { + var err error + w.msg, err = reader.ReadTCP(w.tcp, timeout) + if err != nil { + // TODO(tmthrgd): handle error + break + } + srv.serveDNS(w) + if w.tcp == nil { + break // Close() was called + } + if w.hijacked { + break // client will call Close() themselves + } + // The first read uses the read timeout, the rest use the + // idle timeout. + timeout = idleTimeout + } +} + +func (srv *Server) disposeBuffer(w *response) { + if w.udp != nil && cap(w.msg) == srv.UDPSize { + srv.udpPool.Put(w.msg[:srv.UDPSize]) + } + w.msg = nil +} + +func (srv *Server) serveDNS(w *response) { + dh, off, err := unpackMsgHdr(w.msg, 0) + if err != nil { + // Let client hang, they are sending crap; any reply can be used to amplify. + return + } + + req := new(Msg) + req.setHdr(dh) + + switch srv.MsgAcceptFunc(dh) { + case MsgAccept: + case MsgIgnore: + return + case MsgReject: + req.SetRcodeFormatError(req) + // Are we allowed to delete any OPT records here? + req.Ns, req.Answer, req.Extra = nil, nil, nil + + w.WriteMsg(req) + srv.disposeBuffer(w) + return + } + + if err := req.unpack(dh, w.msg, off); err != nil { + req.SetRcodeFormatError(req) + req.Ns, req.Answer, req.Extra = nil, nil, nil + + w.WriteMsg(req) + srv.disposeBuffer(w) + return } 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 + if secret, ok := w.tsigSecret[t.Hdr.Name]; ok { + w.tsigStatus = TsigVerify(w.msg, secret, "", false) + } else { + w.tsigStatus = ErrSecret } - 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 + srv.disposeBuffer(w) + + handler := srv.Handler + if handler == nil { + handler = DefaultServeMux } - 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 + handler.ServeDNS(w, req) // Writes back to the client } func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { - conn.SetReadDeadline(time.Now().Add(timeout)) + // If we race with ShutdownContext, the read deadline may + // have been set in the distant past to unblock the read + // below. We must not override it, otherwise we may block + // ShutdownContext. + srv.lock.RLock() + if srv.started { + conn.SetReadDeadline(time.Now().Add(timeout)) + } + srv.lock.RUnlock() + l := make([]byte, 2) n, err := conn.Read(l) if err != nil || n != 2 { @@ -632,10 +733,17 @@ func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) } func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) { - conn.SetReadDeadline(time.Now().Add(timeout)) - m := make([]byte, srv.UDPSize) + srv.lock.RLock() + if srv.started { + // See the comment in readTCP above. + conn.SetReadDeadline(time.Now().Add(timeout)) + } + srv.lock.RUnlock() + + m := srv.udpPool.Get().([]byte) n, s, err := ReadFromSessionUDP(conn, m) if err != nil { + srv.udpPool.Put(m) return nil, nil, err } m = m[:n] @@ -644,6 +752,10 @@ func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *S // WriteMsg implements the ResponseWriter.WriteMsg method. func (w *response) WriteMsg(m *Msg) (err error) { + if w.closed { + return &Error{err: "WriteMsg called after Close"} + } + 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 { @@ -665,6 +777,10 @@ func (w *response) WriteMsg(m *Msg) (err error) { // Write implements the ResponseWriter.Write method. func (w *response) Write(m []byte) (int, error) { + if w.closed { + return 0, &Error{err: "Write called after Close"} + } + switch { case w.udp != nil: n, err := WriteToSessionUDP(w.udp, m, w.udpSession) @@ -683,20 +799,34 @@ func (w *response) Write(m []byte) (int, error) { n, err := io.Copy(w.tcp, bytes.NewReader(m)) return int(n), err + default: + panic("dns: internal error: udp and tcp both nil") } - panic("not reached") } // LocalAddr implements the ResponseWriter.LocalAddr method. func (w *response) LocalAddr() net.Addr { - if w.tcp != nil { + switch { + case w.udp != nil: + return w.udp.LocalAddr() + case w.tcp != nil: return w.tcp.LocalAddr() + default: + panic("dns: internal error: udp and tcp both nil") } - return w.udp.LocalAddr() } // RemoteAddr implements the ResponseWriter.RemoteAddr method. -func (w *response) RemoteAddr() net.Addr { return w.remoteAddr } +func (w *response) RemoteAddr() net.Addr { + switch { + case w.udpSession != nil: + return w.udpSession.RemoteAddr() + case w.tcp != nil: + return w.tcp.RemoteAddr() + default: + panic("dns: internal error: udpSession and tcp both nil") + } +} // TsigStatus implements the ResponseWriter.TsigStatus method. func (w *response) TsigStatus() error { return w.tsigStatus } @@ -709,11 +839,30 @@ 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 + if w.closed { + return &Error{err: "connection already closed"} + } + w.closed = true + + switch { + case w.udp != nil: + // Can't close the udp conn, as that is actually the listener. + return nil + case w.tcp != nil: + return w.tcp.Close() + default: + panic("dns: internal error: udp and tcp both nil") + } +} + +// ConnectionState() implements the ConnectionStater.ConnectionState() interface. +func (w *response) ConnectionState() *tls.ConnectionState { + type tlsConnectionStater interface { + ConnectionState() tls.ConnectionState + } + if v, ok := w.tcp.(tlsConnectionStater); ok { + t := v.ConnectionState() + return &t } return nil } diff --git a/vendor/github.com/miekg/dns/sig0.go b/vendor/github.com/miekg/dns/sig0.go index f31e9e684..e97f63968 100644 --- a/vendor/github.com/miekg/dns/sig0.go +++ b/vendor/github.com/miekg/dns/sig0.go @@ -29,7 +29,7 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) { rr.TypeCovered = 0 rr.Labels = 0 - buf := make([]byte, m.Len()+rr.len()) + buf := make([]byte, m.Len()+Len(rr)) mbuf, err := m.PackBuffer(buf) if err != nil { return nil, err @@ -127,8 +127,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error { if offset+1 >= buflen { continue } - var rdlen uint16 - rdlen = binary.BigEndian.Uint16(buf[offset:]) + rdlen := binary.BigEndian.Uint16(buf[offset:]) offset += 2 offset += int(rdlen) } @@ -168,7 +167,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error { } // 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) { + if !strings.EqualFold(signername, k.Header().Name) { return &Error{err: "signer name doesn't match key name"} } sigend := offset diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go index 4837b4ab1..91b69d582 100644 --- a/vendor/github.com/miekg/dns/tsig.go +++ b/vendor/github.com/miekg/dns/tsig.go @@ -133,7 +133,7 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s t.Algorithm = rr.Algorithm t.OrigId = m.Id - tbuf := make([]byte, t.len()) + tbuf := make([]byte, Len(t)) if off, err := PackRR(t, tbuf, 0, nil, false); err == nil { tbuf = tbuf[:off] // reset to actual size used } else { diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index a779ca8ab..c8658b343 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -218,8 +218,10 @@ type Question struct { Qclass uint16 } -func (q *Question) len() int { - return len(q.Name) + 1 + 2 + 2 +func (q *Question) len(off int, compression map[string]struct{}) int { + l := domainNameLen(q.Name, off, compression, true) + l += 2 + 2 + return l } func (q *Question) String() (s string) { @@ -330,7 +332,7 @@ func (rr *MX) String() string { type AFSDB struct { Hdr RR_Header Subtype uint16 - Hostname string `dns:"cdomain-name"` + Hostname string `dns:"domain-name"` } func (rr *AFSDB) String() string { @@ -351,7 +353,7 @@ func (rr *X25) String() string { type RT struct { Hdr RR_Header Preference uint16 - Host string `dns:"cdomain-name"` + Host string `dns:"domain-name"` // RFC 3597 prohibits compressing records not defined in RFC 1035. } func (rr *RT) String() string { @@ -419,128 +421,154 @@ type TXT struct { func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } func sprintName(s string) string { - src := []byte(s) - dst := make([]byte, 0, len(src)) - for i := 0; i < len(src); { - if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' { - dst = append(dst, src[i:i+2]...) + var dst strings.Builder + dst.Grow(len(s)) + for i := 0; i < len(s); { + if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' { + dst.WriteString(s[i : i+2]) i += 2 - } else { - b, n := nextByte(src, i) - if n == 0 { - i++ // dangling back slash - } else if b == '.' { - dst = append(dst, b) - } else { - dst = appendDomainNameByte(dst, b) - } - i += n + continue } + + b, n := nextByte(s, i) + switch { + case n == 0: + i++ // dangling back slash + case b == '.': + dst.WriteByte('.') + default: + writeDomainNameByte(&dst, b) + } + i += n } - return string(dst) + return dst.String() } func sprintTxtOctet(s string) string { - src := []byte(s) - dst := make([]byte, 0, len(src)) - dst = append(dst, '"') - for i := 0; i < len(src); { - if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' { - dst = append(dst, src[i:i+2]...) + var dst strings.Builder + dst.Grow(2 + len(s)) + dst.WriteByte('"') + for i := 0; i < len(s); { + if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' { + dst.WriteString(s[i : i+2]) i += 2 - } else { - b, n := nextByte(src, i) - if n == 0 { - i++ // dangling back slash - } else if b == '.' { - dst = append(dst, b) - } else { - if b < ' ' || b > '~' { - dst = appendByte(dst, b) - } else { - dst = append(dst, b) - } - } - i += n + continue } + + b, n := nextByte(s, i) + switch { + case n == 0: + i++ // dangling back slash + case b == '.': + dst.WriteByte('.') + case b < ' ' || b > '~': + dst.WriteString(escapeByte(b)) + default: + dst.WriteByte(b) + } + i += n } - dst = append(dst, '"') - return string(dst) + dst.WriteByte('"') + return dst.String() } func sprintTxt(txt []string) string { - var out []byte + var out strings.Builder for i, s := range txt { + out.Grow(3 + len(s)) if i > 0 { - out = append(out, ` "`...) + out.WriteString(` "`) } else { - out = append(out, '"') + out.WriteByte('"') } - bs := []byte(s) - for j := 0; j < len(bs); { - b, n := nextByte(bs, j) + for j := 0; j < len(s); { + b, n := nextByte(s, j) if n == 0 { break } - out = appendTXTStringByte(out, b) + writeTXTStringByte(&out, b) j += n } - out = append(out, '"') + out.WriteByte('"') } - return string(out) + return out.String() } -func appendDomainNameByte(s []byte, b byte) []byte { +func writeDomainNameByte(s *strings.Builder, b byte) { switch b { case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape - return append(s, '\\', b) + s.WriteByte('\\') + s.WriteByte(b) + default: + writeTXTStringByte(s, b) } - return appendTXTStringByte(s, b) } -func appendTXTStringByte(s []byte, b byte) []byte { - switch b { - case '"', '\\': - return append(s, '\\', b) +func writeTXTStringByte(s *strings.Builder, b byte) { + switch { + case b == '"' || b == '\\': + s.WriteByte('\\') + s.WriteByte(b) + case b < ' ' || b > '~': + s.WriteString(escapeByte(b)) + default: + s.WriteByte(b) } - if b < ' ' || b > '~' { - return appendByte(s, b) - } - return append(s, b) } -func appendByte(s []byte, b byte) []byte { - 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') +const ( + escapedByteSmall = "" + + `\000\001\002\003\004\005\006\007\008\009` + + `\010\011\012\013\014\015\016\017\018\019` + + `\020\021\022\023\024\025\026\027\028\029` + + `\030\031` + escapedByteLarge = `\127\128\129` + + `\130\131\132\133\134\135\136\137\138\139` + + `\140\141\142\143\144\145\146\147\148\149` + + `\150\151\152\153\154\155\156\157\158\159` + + `\160\161\162\163\164\165\166\167\168\169` + + `\170\171\172\173\174\175\176\177\178\179` + + `\180\181\182\183\184\185\186\187\188\189` + + `\190\191\192\193\194\195\196\197\198\199` + + `\200\201\202\203\204\205\206\207\208\209` + + `\210\211\212\213\214\215\216\217\218\219` + + `\220\221\222\223\224\225\226\227\228\229` + + `\230\231\232\233\234\235\236\237\238\239` + + `\240\241\242\243\244\245\246\247\248\249` + + `\250\251\252\253\254\255` +) + +// escapeByte returns the \DDD escaping of b which must +// satisfy b < ' ' || b > '~'. +func escapeByte(b byte) string { + if b < ' ' { + return escapedByteSmall[b*4 : b*4+4] } - for _, r := range bufs { - s = append(s, r) - } - return s + + b -= '~' + 1 + // The cast here is needed as b*4 may overflow byte. + return escapedByteLarge[int(b)*4 : int(b)*4+4] } -func nextByte(b []byte, offset int) (byte, int) { - if offset >= len(b) { +func nextByte(s string, offset int) (byte, int) { + if offset >= len(s) { return 0, 0 } - if b[offset] != '\\' { + if s[offset] != '\\' { // not an escape sequence - return b[offset], 1 + return s[offset], 1 } - switch len(b) - offset { + switch len(s) - offset { case 1: // dangling escape return 0, 0 case 2, 3: // too short to be \ddd default: // maybe \ddd - if isDigit(b[offset+1]) && isDigit(b[offset+2]) && isDigit(b[offset+3]) { - return dddToByte(b[offset+1:]), 4 + if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) { + return dddStringToByte(s[offset+1:]), 4 } } // not \ddd, just an RFC 1035 "quoted" character - return b[offset+1], 2 + return s[offset+1], 2 } // SPF RR. See RFC 4408, Section 3.1.1. @@ -728,7 +756,7 @@ func (rr *LOC) String() string { lat = lat % LOC_DEGREES m := lat / LOC_HOURS lat = lat % LOC_HOURS - s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float64(lat) / 1000), ns) + s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lat)/1000, ns) lon := rr.Longitude ew := "E" @@ -742,7 +770,7 @@ func (rr *LOC) String() string { lon = lon % LOC_DEGREES m = lon / LOC_HOURS lon = lon % LOC_HOURS - s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float64(lon) / 1000), ew) + s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lon)/1000, ew) var alt = float64(rr.Altitude) / 100 alt -= LOC_ALTITUDEBASE @@ -752,9 +780,9 @@ func (rr *LOC) String() string { s += fmt.Sprintf("%.0fm ", alt) } - s += cmToM((rr.Size&0xf0)>>4, rr.Size&0x0f) + "m " - s += cmToM((rr.HorizPre&0xf0)>>4, rr.HorizPre&0x0f) + "m " - s += cmToM((rr.VertPre&0xf0)>>4, rr.VertPre&0x0f) + "m" + s += cmToM(rr.Size&0xf0>>4, rr.Size&0x0f) + "m " + s += cmToM(rr.HorizPre&0xf0>>4, rr.HorizPre&0x0f) + "m " + s += cmToM(rr.VertPre&0xf0>>4, rr.VertPre&0x0f) + "m" return s } @@ -807,8 +835,9 @@ func (rr *NSEC) String() string { return s } -func (rr *NSEC) len() int { - l := rr.Hdr.len() + len(rr.NextDomain) + 1 +func (rr *NSEC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.NextDomain, off+l, compression, false) lastwindow := uint32(2 ^ 32 + 1) for _, t := range rr.TypeBitMap { window := t / 256 @@ -972,8 +1001,9 @@ func (rr *NSEC3) String() string { return s } -func (rr *NSEC3) len() int { - l := rr.Hdr.len() + 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1 +func (rr *NSEC3) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1 lastwindow := uint32(2 ^ 32 + 1) for _, t := range rr.TypeBitMap { window := t / 256 @@ -1289,8 +1319,9 @@ func (rr *CSYNC) String() string { return s } -func (rr *CSYNC) len() int { - l := rr.Hdr.len() + 4 + 2 +func (rr *CSYNC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += 4 + 2 lastwindow := uint32(2 ^ 32 + 1) for _, t := range rr.TypeBitMap { window := t / 256 @@ -1306,11 +1337,11 @@ func (rr *CSYNC) len() int { // string representation used when printing the record. // It takes serial arithmetic (RFC 1982) into account. func TimeToString(t uint32) string { - mod := ((int64(t) - time.Now().Unix()) / year68) - 1 + mod := (int64(t)-time.Now().Unix())/year68 - 1 if mod < 0 { mod = 0 } - ti := time.Unix(int64(t)-(mod*year68), 0).UTC() + ti := time.Unix(int64(t)-mod*year68, 0).UTC() return ti.Format("20060102150405") } @@ -1322,11 +1353,11 @@ func StringToTime(s string) (uint32, error) { if err != nil { return 0, err } - mod := (t.Unix() / year68) - 1 + mod := t.Unix()/year68 - 1 if mod < 0 { mod = 0 } - return uint32(t.Unix() - (mod * year68)), nil + return uint32(t.Unix() - mod*year68), nil } // saltToString converts a NSECX salt to uppercase and returns "-" when it is empty. diff --git a/vendor/github.com/miekg/dns/types_generate.go b/vendor/github.com/miekg/dns/types_generate.go index 8703cce64..8c897ec11 100644 --- a/vendor/github.com/miekg/dns/types_generate.go +++ b/vendor/github.com/miekg/dns/types_generate.go @@ -153,8 +153,8 @@ func main() { if isEmbedded { continue } - fmt.Fprintf(b, "func (rr *%s) len() int {\n", name) - fmt.Fprintf(b, "l := rr.Hdr.len()\n") + fmt.Fprintf(b, "func (rr *%s) len(off int, compression map[string]struct{}) int {\n", name) + fmt.Fprintf(b, "l := rr.Hdr.len(off, compression)\n") for i := 1; i < st.NumFields(); i++ { o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) } @@ -162,7 +162,11 @@ func main() { switch st.Tag(i) { case `dns:"-"`: // ignored - case `dns:"cdomain-name"`, `dns:"domain-name"`, `dns:"txt"`: + case `dns:"cdomain-name"`: + o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, true) }\n") + case `dns:"domain-name"`: + o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, false) }\n") + case `dns:"txt"`: o("for _, x := range rr.%s { l += len(x) + 1 }\n") default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) @@ -173,8 +177,10 @@ func main() { switch { case st.Tag(i) == `dns:"-"`: // ignored - case st.Tag(i) == `dns:"cdomain-name"`, st.Tag(i) == `dns:"domain-name"`: - o("l += len(rr.%s) + 1\n") + case st.Tag(i) == `dns:"cdomain-name"`: + o("l += domainNameLen(rr.%s, off+l, compression, true)\n") + case st.Tag(i) == `dns:"domain-name"`: + o("l += domainNameLen(rr.%s, off+l, compression, false)\n") case st.Tag(i) == `dns:"octet"`: o("l += len(rr.%s)\n") case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): @@ -226,7 +232,7 @@ func main() { continue } fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name) - fields := []string{"*rr.Hdr.copyHeader()"} + fields := []string{"rr.Hdr"} for i := 1; i < st.NumFields(); i++ { f := st.Field(i).Name() if sl, ok := st.Field(i).Type().(*types.Slice); ok { diff --git a/vendor/github.com/miekg/dns/udp.go b/vendor/github.com/miekg/dns/udp.go index f3f31a7ac..a4826ee2f 100644 --- a/vendor/github.com/miekg/dns/udp.go +++ b/vendor/github.com/miekg/dns/udp.go @@ -9,6 +9,22 @@ import ( "golang.org/x/net/ipv6" ) +// This is the required size of the OOB buffer to pass to ReadMsgUDP. +var udpOOBSize = func() int { + // We can't know whether we'll get an IPv4 control message or an + // IPv6 control message ahead of time. To get around this, we size + // the buffer equal to the largest of the two. + + oob4 := ipv4.NewControlMessage(ipv4.FlagDst | ipv4.FlagInterface) + oob6 := ipv6.NewControlMessage(ipv6.FlagDst | ipv6.FlagInterface) + + if len(oob4) > len(oob6) { + return len(oob4) + } + + return len(oob6) +}() + // SessionUDP holds the remote address and the associated // out-of-band data. type SessionUDP struct { @@ -22,7 +38,7 @@ 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) + oob := make([]byte, udpOOBSize) n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) if err != nil { return n, nil, err @@ -53,18 +69,15 @@ func parseDstFromOOB(oob []byte) net.IP { // Start with IPv6 and then fallback to IPv4 // TODO(fastest963): Figure out a way to prefer one or the other. Looking at // the lvl of the header for a 0 or 41 isn't cross-platform. - var dst net.IP cm6 := new(ipv6.ControlMessage) - if cm6.Parse(oob) == nil { - dst = cm6.Dst + if cm6.Parse(oob) == nil && cm6.Dst != nil { + return cm6.Dst } - if dst == nil { - cm4 := new(ipv4.ControlMessage) - if cm4.Parse(oob) == nil { - dst = cm4.Dst - } + cm4 := new(ipv4.ControlMessage) + if cm4.Parse(oob) == nil && cm4.Dst != nil { + return cm4.Dst } - return dst + return nil } // correctSource takes oob data and returns new oob data with the Src equal to the Dst diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index e41d2b3ca..257dee4d1 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = V{1, 0, 4} +var Version = V{1, 1, 1} // V holds the version of this library. type V struct { diff --git a/vendor/github.com/miekg/dns/zcompress.go b/vendor/github.com/miekg/dns/zcompress.go deleted file mode 100644 index c2503204d..000000000 --- a/vendor/github.com/miekg/dns/zcompress.go +++ /dev/null @@ -1,118 +0,0 @@ -// Code generated by "go run compress_generate.go"; DO NOT EDIT. - -package dns - -func compressionLenHelperType(c map[string]int, r RR) { - switch x := r.(type) { - case *AFSDB: - compressionLenHelper(c, x.Hostname) - case *CNAME: - compressionLenHelper(c, x.Target) - case *DNAME: - compressionLenHelper(c, x.Target) - case *HIP: - for i := range x.RendezvousServers { - compressionLenHelper(c, x.RendezvousServers[i]) - } - case *KX: - compressionLenHelper(c, x.Exchanger) - case *LP: - compressionLenHelper(c, x.Fqdn) - case *MB: - compressionLenHelper(c, x.Mb) - case *MD: - compressionLenHelper(c, x.Md) - case *MF: - compressionLenHelper(c, x.Mf) - case *MG: - compressionLenHelper(c, x.Mg) - case *MINFO: - compressionLenHelper(c, x.Rmail) - compressionLenHelper(c, x.Email) - case *MR: - compressionLenHelper(c, x.Mr) - case *MX: - compressionLenHelper(c, x.Mx) - case *NAPTR: - compressionLenHelper(c, x.Replacement) - case *NS: - compressionLenHelper(c, x.Ns) - case *NSAPPTR: - compressionLenHelper(c, x.Ptr) - case *NSEC: - compressionLenHelper(c, x.NextDomain) - case *PTR: - compressionLenHelper(c, x.Ptr) - case *PX: - compressionLenHelper(c, x.Map822) - compressionLenHelper(c, x.Mapx400) - case *RP: - compressionLenHelper(c, x.Mbox) - compressionLenHelper(c, x.Txt) - case *RRSIG: - compressionLenHelper(c, x.SignerName) - case *RT: - compressionLenHelper(c, x.Host) - case *SIG: - compressionLenHelper(c, x.SignerName) - case *SOA: - compressionLenHelper(c, x.Ns) - compressionLenHelper(c, x.Mbox) - case *SRV: - compressionLenHelper(c, x.Target) - case *TALINK: - compressionLenHelper(c, x.PreviousName) - compressionLenHelper(c, x.NextName) - case *TKEY: - compressionLenHelper(c, x.Algorithm) - case *TSIG: - compressionLenHelper(c, x.Algorithm) - } -} - -func compressionLenSearchType(c map[string]int, r RR) (int, bool) { - switch x := r.(type) { - case *AFSDB: - k1, ok1 := compressionLenSearch(c, x.Hostname) - return k1, ok1 - case *CNAME: - k1, ok1 := compressionLenSearch(c, x.Target) - return k1, ok1 - case *MB: - k1, ok1 := compressionLenSearch(c, x.Mb) - return k1, ok1 - case *MD: - k1, ok1 := compressionLenSearch(c, x.Md) - return k1, ok1 - case *MF: - k1, ok1 := compressionLenSearch(c, x.Mf) - return k1, ok1 - case *MG: - k1, ok1 := compressionLenSearch(c, x.Mg) - return k1, ok1 - case *MINFO: - k1, ok1 := compressionLenSearch(c, x.Rmail) - k2, ok2 := compressionLenSearch(c, x.Email) - return k1 + k2, ok1 && ok2 - case *MR: - k1, ok1 := compressionLenSearch(c, x.Mr) - return k1, ok1 - case *MX: - k1, ok1 := compressionLenSearch(c, x.Mx) - return k1, ok1 - case *NS: - k1, ok1 := compressionLenSearch(c, x.Ns) - return k1, ok1 - case *PTR: - k1, ok1 := compressionLenSearch(c, x.Ptr) - return k1, ok1 - case *RT: - k1, ok1 := compressionLenSearch(c, x.Host) - return k1, ok1 - case *SOA: - k1, ok1 := compressionLenSearch(c, x.Ns) - k2, ok2 := compressionLenSearch(c, x.Mbox) - return k1 + k2, ok1 && ok2 - } - return 0, false -} diff --git a/vendor/github.com/miekg/dns/zduplicate.go b/vendor/github.com/miekg/dns/zduplicate.go new file mode 100644 index 000000000..ba9863b23 --- /dev/null +++ b/vendor/github.com/miekg/dns/zduplicate.go @@ -0,0 +1,943 @@ +// Code generated by "go run duplicate_generate.go"; DO NOT EDIT. + +package dns + +// isDuplicateRdata calls the rdata specific functions +func isDuplicateRdata(r1, r2 RR) bool { + switch r1.Header().Rrtype { + case TypeA: + return isDuplicateA(r1.(*A), r2.(*A)) + case TypeAAAA: + return isDuplicateAAAA(r1.(*AAAA), r2.(*AAAA)) + case TypeAFSDB: + return isDuplicateAFSDB(r1.(*AFSDB), r2.(*AFSDB)) + case TypeAVC: + return isDuplicateAVC(r1.(*AVC), r2.(*AVC)) + case TypeCAA: + return isDuplicateCAA(r1.(*CAA), r2.(*CAA)) + case TypeCERT: + return isDuplicateCERT(r1.(*CERT), r2.(*CERT)) + case TypeCNAME: + return isDuplicateCNAME(r1.(*CNAME), r2.(*CNAME)) + case TypeCSYNC: + return isDuplicateCSYNC(r1.(*CSYNC), r2.(*CSYNC)) + case TypeDHCID: + return isDuplicateDHCID(r1.(*DHCID), r2.(*DHCID)) + case TypeDNAME: + return isDuplicateDNAME(r1.(*DNAME), r2.(*DNAME)) + case TypeDNSKEY: + return isDuplicateDNSKEY(r1.(*DNSKEY), r2.(*DNSKEY)) + case TypeDS: + return isDuplicateDS(r1.(*DS), r2.(*DS)) + case TypeEID: + return isDuplicateEID(r1.(*EID), r2.(*EID)) + case TypeEUI48: + return isDuplicateEUI48(r1.(*EUI48), r2.(*EUI48)) + case TypeEUI64: + return isDuplicateEUI64(r1.(*EUI64), r2.(*EUI64)) + case TypeGID: + return isDuplicateGID(r1.(*GID), r2.(*GID)) + case TypeGPOS: + return isDuplicateGPOS(r1.(*GPOS), r2.(*GPOS)) + case TypeHINFO: + return isDuplicateHINFO(r1.(*HINFO), r2.(*HINFO)) + case TypeHIP: + return isDuplicateHIP(r1.(*HIP), r2.(*HIP)) + case TypeKX: + return isDuplicateKX(r1.(*KX), r2.(*KX)) + case TypeL32: + return isDuplicateL32(r1.(*L32), r2.(*L32)) + case TypeL64: + return isDuplicateL64(r1.(*L64), r2.(*L64)) + case TypeLOC: + return isDuplicateLOC(r1.(*LOC), r2.(*LOC)) + case TypeLP: + return isDuplicateLP(r1.(*LP), r2.(*LP)) + case TypeMB: + return isDuplicateMB(r1.(*MB), r2.(*MB)) + case TypeMD: + return isDuplicateMD(r1.(*MD), r2.(*MD)) + case TypeMF: + return isDuplicateMF(r1.(*MF), r2.(*MF)) + case TypeMG: + return isDuplicateMG(r1.(*MG), r2.(*MG)) + case TypeMINFO: + return isDuplicateMINFO(r1.(*MINFO), r2.(*MINFO)) + case TypeMR: + return isDuplicateMR(r1.(*MR), r2.(*MR)) + case TypeMX: + return isDuplicateMX(r1.(*MX), r2.(*MX)) + case TypeNAPTR: + return isDuplicateNAPTR(r1.(*NAPTR), r2.(*NAPTR)) + case TypeNID: + return isDuplicateNID(r1.(*NID), r2.(*NID)) + case TypeNIMLOC: + return isDuplicateNIMLOC(r1.(*NIMLOC), r2.(*NIMLOC)) + case TypeNINFO: + return isDuplicateNINFO(r1.(*NINFO), r2.(*NINFO)) + case TypeNS: + return isDuplicateNS(r1.(*NS), r2.(*NS)) + case TypeNSAPPTR: + return isDuplicateNSAPPTR(r1.(*NSAPPTR), r2.(*NSAPPTR)) + case TypeNSEC: + return isDuplicateNSEC(r1.(*NSEC), r2.(*NSEC)) + case TypeNSEC3: + return isDuplicateNSEC3(r1.(*NSEC3), r2.(*NSEC3)) + case TypeNSEC3PARAM: + return isDuplicateNSEC3PARAM(r1.(*NSEC3PARAM), r2.(*NSEC3PARAM)) + case TypeOPENPGPKEY: + return isDuplicateOPENPGPKEY(r1.(*OPENPGPKEY), r2.(*OPENPGPKEY)) + case TypePTR: + return isDuplicatePTR(r1.(*PTR), r2.(*PTR)) + case TypePX: + return isDuplicatePX(r1.(*PX), r2.(*PX)) + case TypeRKEY: + return isDuplicateRKEY(r1.(*RKEY), r2.(*RKEY)) + case TypeRP: + return isDuplicateRP(r1.(*RP), r2.(*RP)) + case TypeRRSIG: + return isDuplicateRRSIG(r1.(*RRSIG), r2.(*RRSIG)) + case TypeRT: + return isDuplicateRT(r1.(*RT), r2.(*RT)) + case TypeSMIMEA: + return isDuplicateSMIMEA(r1.(*SMIMEA), r2.(*SMIMEA)) + case TypeSOA: + return isDuplicateSOA(r1.(*SOA), r2.(*SOA)) + case TypeSPF: + return isDuplicateSPF(r1.(*SPF), r2.(*SPF)) + case TypeSRV: + return isDuplicateSRV(r1.(*SRV), r2.(*SRV)) + case TypeSSHFP: + return isDuplicateSSHFP(r1.(*SSHFP), r2.(*SSHFP)) + case TypeTA: + return isDuplicateTA(r1.(*TA), r2.(*TA)) + case TypeTALINK: + return isDuplicateTALINK(r1.(*TALINK), r2.(*TALINK)) + case TypeTKEY: + return isDuplicateTKEY(r1.(*TKEY), r2.(*TKEY)) + case TypeTLSA: + return isDuplicateTLSA(r1.(*TLSA), r2.(*TLSA)) + case TypeTSIG: + return isDuplicateTSIG(r1.(*TSIG), r2.(*TSIG)) + case TypeTXT: + return isDuplicateTXT(r1.(*TXT), r2.(*TXT)) + case TypeUID: + return isDuplicateUID(r1.(*UID), r2.(*UID)) + case TypeUINFO: + return isDuplicateUINFO(r1.(*UINFO), r2.(*UINFO)) + case TypeURI: + return isDuplicateURI(r1.(*URI), r2.(*URI)) + case TypeX25: + return isDuplicateX25(r1.(*X25), r2.(*X25)) + } + return false +} + +// isDuplicate() functions + +func isDuplicateA(r1, r2 *A) bool { + if len(r1.A) != len(r2.A) { + return false + } + for i := 0; i < len(r1.A); i++ { + if r1.A[i] != r2.A[i] { + return false + } + } + return true +} + +func isDuplicateAAAA(r1, r2 *AAAA) bool { + if len(r1.AAAA) != len(r2.AAAA) { + return false + } + for i := 0; i < len(r1.AAAA); i++ { + if r1.AAAA[i] != r2.AAAA[i] { + return false + } + } + return true +} + +func isDuplicateAFSDB(r1, r2 *AFSDB) bool { + if r1.Subtype != r2.Subtype { + return false + } + if !isDulicateName(r1.Hostname, r2.Hostname) { + return false + } + return true +} + +func isDuplicateAVC(r1, r2 *AVC) bool { + if len(r1.Txt) != len(r2.Txt) { + return false + } + for i := 0; i < len(r1.Txt); i++ { + if r1.Txt[i] != r2.Txt[i] { + return false + } + } + return true +} + +func isDuplicateCAA(r1, r2 *CAA) bool { + if r1.Flag != r2.Flag { + return false + } + if r1.Tag != r2.Tag { + return false + } + if r1.Value != r2.Value { + return false + } + return true +} + +func isDuplicateCERT(r1, r2 *CERT) bool { + if r1.Type != r2.Type { + return false + } + if r1.KeyTag != r2.KeyTag { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.Certificate != r2.Certificate { + return false + } + return true +} + +func isDuplicateCNAME(r1, r2 *CNAME) bool { + if !isDulicateName(r1.Target, r2.Target) { + return false + } + return true +} + +func isDuplicateCSYNC(r1, r2 *CSYNC) bool { + if r1.Serial != r2.Serial { + return false + } + if r1.Flags != r2.Flags { + return false + } + if len(r1.TypeBitMap) != len(r2.TypeBitMap) { + return false + } + for i := 0; i < len(r1.TypeBitMap); i++ { + if r1.TypeBitMap[i] != r2.TypeBitMap[i] { + return false + } + } + return true +} + +func isDuplicateDHCID(r1, r2 *DHCID) bool { + if r1.Digest != r2.Digest { + return false + } + return true +} + +func isDuplicateDNAME(r1, r2 *DNAME) bool { + if !isDulicateName(r1.Target, r2.Target) { + return false + } + return true +} + +func isDuplicateDNSKEY(r1, r2 *DNSKEY) bool { + if r1.Flags != r2.Flags { + return false + } + if r1.Protocol != r2.Protocol { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.PublicKey != r2.PublicKey { + return false + } + return true +} + +func isDuplicateDS(r1, r2 *DS) bool { + if r1.KeyTag != r2.KeyTag { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.DigestType != r2.DigestType { + return false + } + if r1.Digest != r2.Digest { + return false + } + return true +} + +func isDuplicateEID(r1, r2 *EID) bool { + if r1.Endpoint != r2.Endpoint { + return false + } + return true +} + +func isDuplicateEUI48(r1, r2 *EUI48) bool { + if r1.Address != r2.Address { + return false + } + return true +} + +func isDuplicateEUI64(r1, r2 *EUI64) bool { + if r1.Address != r2.Address { + return false + } + return true +} + +func isDuplicateGID(r1, r2 *GID) bool { + if r1.Gid != r2.Gid { + return false + } + return true +} + +func isDuplicateGPOS(r1, r2 *GPOS) bool { + if r1.Longitude != r2.Longitude { + return false + } + if r1.Latitude != r2.Latitude { + return false + } + if r1.Altitude != r2.Altitude { + return false + } + return true +} + +func isDuplicateHINFO(r1, r2 *HINFO) bool { + if r1.Cpu != r2.Cpu { + return false + } + if r1.Os != r2.Os { + return false + } + return true +} + +func isDuplicateHIP(r1, r2 *HIP) bool { + if r1.HitLength != r2.HitLength { + return false + } + if r1.PublicKeyAlgorithm != r2.PublicKeyAlgorithm { + return false + } + if r1.PublicKeyLength != r2.PublicKeyLength { + return false + } + if r1.Hit != r2.Hit { + return false + } + if r1.PublicKey != r2.PublicKey { + return false + } + if len(r1.RendezvousServers) != len(r2.RendezvousServers) { + return false + } + for i := 0; i < len(r1.RendezvousServers); i++ { + if !isDulicateName(r1.RendezvousServers[i], r2.RendezvousServers[i]) { + return false + } + } + return true +} + +func isDuplicateKX(r1, r2 *KX) bool { + if r1.Preference != r2.Preference { + return false + } + if !isDulicateName(r1.Exchanger, r2.Exchanger) { + return false + } + return true +} + +func isDuplicateL32(r1, r2 *L32) bool { + if r1.Preference != r2.Preference { + return false + } + if len(r1.Locator32) != len(r2.Locator32) { + return false + } + for i := 0; i < len(r1.Locator32); i++ { + if r1.Locator32[i] != r2.Locator32[i] { + return false + } + } + return true +} + +func isDuplicateL64(r1, r2 *L64) bool { + if r1.Preference != r2.Preference { + return false + } + if r1.Locator64 != r2.Locator64 { + return false + } + return true +} + +func isDuplicateLOC(r1, r2 *LOC) bool { + if r1.Version != r2.Version { + return false + } + if r1.Size != r2.Size { + return false + } + if r1.HorizPre != r2.HorizPre { + return false + } + if r1.VertPre != r2.VertPre { + return false + } + if r1.Latitude != r2.Latitude { + return false + } + if r1.Longitude != r2.Longitude { + return false + } + if r1.Altitude != r2.Altitude { + return false + } + return true +} + +func isDuplicateLP(r1, r2 *LP) bool { + if r1.Preference != r2.Preference { + return false + } + if !isDulicateName(r1.Fqdn, r2.Fqdn) { + return false + } + return true +} + +func isDuplicateMB(r1, r2 *MB) bool { + if !isDulicateName(r1.Mb, r2.Mb) { + return false + } + return true +} + +func isDuplicateMD(r1, r2 *MD) bool { + if !isDulicateName(r1.Md, r2.Md) { + return false + } + return true +} + +func isDuplicateMF(r1, r2 *MF) bool { + if !isDulicateName(r1.Mf, r2.Mf) { + return false + } + return true +} + +func isDuplicateMG(r1, r2 *MG) bool { + if !isDulicateName(r1.Mg, r2.Mg) { + return false + } + return true +} + +func isDuplicateMINFO(r1, r2 *MINFO) bool { + if !isDulicateName(r1.Rmail, r2.Rmail) { + return false + } + if !isDulicateName(r1.Email, r2.Email) { + return false + } + return true +} + +func isDuplicateMR(r1, r2 *MR) bool { + if !isDulicateName(r1.Mr, r2.Mr) { + return false + } + return true +} + +func isDuplicateMX(r1, r2 *MX) bool { + if r1.Preference != r2.Preference { + return false + } + if !isDulicateName(r1.Mx, r2.Mx) { + return false + } + return true +} + +func isDuplicateNAPTR(r1, r2 *NAPTR) bool { + if r1.Order != r2.Order { + return false + } + if r1.Preference != r2.Preference { + return false + } + if r1.Flags != r2.Flags { + return false + } + if r1.Service != r2.Service { + return false + } + if r1.Regexp != r2.Regexp { + return false + } + if !isDulicateName(r1.Replacement, r2.Replacement) { + return false + } + return true +} + +func isDuplicateNID(r1, r2 *NID) bool { + if r1.Preference != r2.Preference { + return false + } + if r1.NodeID != r2.NodeID { + return false + } + return true +} + +func isDuplicateNIMLOC(r1, r2 *NIMLOC) bool { + if r1.Locator != r2.Locator { + return false + } + return true +} + +func isDuplicateNINFO(r1, r2 *NINFO) bool { + if len(r1.ZSData) != len(r2.ZSData) { + return false + } + for i := 0; i < len(r1.ZSData); i++ { + if r1.ZSData[i] != r2.ZSData[i] { + return false + } + } + return true +} + +func isDuplicateNS(r1, r2 *NS) bool { + if !isDulicateName(r1.Ns, r2.Ns) { + return false + } + return true +} + +func isDuplicateNSAPPTR(r1, r2 *NSAPPTR) bool { + if !isDulicateName(r1.Ptr, r2.Ptr) { + return false + } + return true +} + +func isDuplicateNSEC(r1, r2 *NSEC) bool { + if !isDulicateName(r1.NextDomain, r2.NextDomain) { + return false + } + if len(r1.TypeBitMap) != len(r2.TypeBitMap) { + return false + } + for i := 0; i < len(r1.TypeBitMap); i++ { + if r1.TypeBitMap[i] != r2.TypeBitMap[i] { + return false + } + } + return true +} + +func isDuplicateNSEC3(r1, r2 *NSEC3) bool { + if r1.Hash != r2.Hash { + return false + } + if r1.Flags != r2.Flags { + return false + } + if r1.Iterations != r2.Iterations { + return false + } + if r1.SaltLength != r2.SaltLength { + return false + } + if r1.Salt != r2.Salt { + return false + } + if r1.HashLength != r2.HashLength { + return false + } + if r1.NextDomain != r2.NextDomain { + return false + } + if len(r1.TypeBitMap) != len(r2.TypeBitMap) { + return false + } + for i := 0; i < len(r1.TypeBitMap); i++ { + if r1.TypeBitMap[i] != r2.TypeBitMap[i] { + return false + } + } + return true +} + +func isDuplicateNSEC3PARAM(r1, r2 *NSEC3PARAM) bool { + if r1.Hash != r2.Hash { + return false + } + if r1.Flags != r2.Flags { + return false + } + if r1.Iterations != r2.Iterations { + return false + } + if r1.SaltLength != r2.SaltLength { + return false + } + if r1.Salt != r2.Salt { + return false + } + return true +} + +func isDuplicateOPENPGPKEY(r1, r2 *OPENPGPKEY) bool { + if r1.PublicKey != r2.PublicKey { + return false + } + return true +} + +func isDuplicatePTR(r1, r2 *PTR) bool { + if !isDulicateName(r1.Ptr, r2.Ptr) { + return false + } + return true +} + +func isDuplicatePX(r1, r2 *PX) bool { + if r1.Preference != r2.Preference { + return false + } + if !isDulicateName(r1.Map822, r2.Map822) { + return false + } + if !isDulicateName(r1.Mapx400, r2.Mapx400) { + return false + } + return true +} + +func isDuplicateRKEY(r1, r2 *RKEY) bool { + if r1.Flags != r2.Flags { + return false + } + if r1.Protocol != r2.Protocol { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.PublicKey != r2.PublicKey { + return false + } + return true +} + +func isDuplicateRP(r1, r2 *RP) bool { + if !isDulicateName(r1.Mbox, r2.Mbox) { + return false + } + if !isDulicateName(r1.Txt, r2.Txt) { + return false + } + return true +} + +func isDuplicateRRSIG(r1, r2 *RRSIG) bool { + if r1.TypeCovered != r2.TypeCovered { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.Labels != r2.Labels { + return false + } + if r1.OrigTtl != r2.OrigTtl { + return false + } + if r1.Expiration != r2.Expiration { + return false + } + if r1.Inception != r2.Inception { + return false + } + if r1.KeyTag != r2.KeyTag { + return false + } + if !isDulicateName(r1.SignerName, r2.SignerName) { + return false + } + if r1.Signature != r2.Signature { + return false + } + return true +} + +func isDuplicateRT(r1, r2 *RT) bool { + if r1.Preference != r2.Preference { + return false + } + if !isDulicateName(r1.Host, r2.Host) { + return false + } + return true +} + +func isDuplicateSMIMEA(r1, r2 *SMIMEA) bool { + if r1.Usage != r2.Usage { + return false + } + if r1.Selector != r2.Selector { + return false + } + if r1.MatchingType != r2.MatchingType { + return false + } + if r1.Certificate != r2.Certificate { + return false + } + return true +} + +func isDuplicateSOA(r1, r2 *SOA) bool { + if !isDulicateName(r1.Ns, r2.Ns) { + return false + } + if !isDulicateName(r1.Mbox, r2.Mbox) { + return false + } + if r1.Serial != r2.Serial { + return false + } + if r1.Refresh != r2.Refresh { + return false + } + if r1.Retry != r2.Retry { + return false + } + if r1.Expire != r2.Expire { + return false + } + if r1.Minttl != r2.Minttl { + return false + } + return true +} + +func isDuplicateSPF(r1, r2 *SPF) bool { + if len(r1.Txt) != len(r2.Txt) { + return false + } + for i := 0; i < len(r1.Txt); i++ { + if r1.Txt[i] != r2.Txt[i] { + return false + } + } + return true +} + +func isDuplicateSRV(r1, r2 *SRV) bool { + if r1.Priority != r2.Priority { + return false + } + if r1.Weight != r2.Weight { + return false + } + if r1.Port != r2.Port { + return false + } + if !isDulicateName(r1.Target, r2.Target) { + return false + } + return true +} + +func isDuplicateSSHFP(r1, r2 *SSHFP) bool { + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.Type != r2.Type { + return false + } + if r1.FingerPrint != r2.FingerPrint { + return false + } + return true +} + +func isDuplicateTA(r1, r2 *TA) bool { + if r1.KeyTag != r2.KeyTag { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.DigestType != r2.DigestType { + return false + } + if r1.Digest != r2.Digest { + return false + } + return true +} + +func isDuplicateTALINK(r1, r2 *TALINK) bool { + if !isDulicateName(r1.PreviousName, r2.PreviousName) { + return false + } + if !isDulicateName(r1.NextName, r2.NextName) { + return false + } + return true +} + +func isDuplicateTKEY(r1, r2 *TKEY) bool { + if !isDulicateName(r1.Algorithm, r2.Algorithm) { + return false + } + if r1.Inception != r2.Inception { + return false + } + if r1.Expiration != r2.Expiration { + return false + } + if r1.Mode != r2.Mode { + return false + } + if r1.Error != r2.Error { + return false + } + if r1.KeySize != r2.KeySize { + return false + } + if r1.Key != r2.Key { + return false + } + if r1.OtherLen != r2.OtherLen { + return false + } + if r1.OtherData != r2.OtherData { + return false + } + return true +} + +func isDuplicateTLSA(r1, r2 *TLSA) bool { + if r1.Usage != r2.Usage { + return false + } + if r1.Selector != r2.Selector { + return false + } + if r1.MatchingType != r2.MatchingType { + return false + } + if r1.Certificate != r2.Certificate { + return false + } + return true +} + +func isDuplicateTSIG(r1, r2 *TSIG) bool { + if !isDulicateName(r1.Algorithm, r2.Algorithm) { + return false + } + if r1.TimeSigned != r2.TimeSigned { + return false + } + if r1.Fudge != r2.Fudge { + return false + } + if r1.MACSize != r2.MACSize { + return false + } + if r1.MAC != r2.MAC { + return false + } + if r1.OrigId != r2.OrigId { + return false + } + if r1.Error != r2.Error { + return false + } + if r1.OtherLen != r2.OtherLen { + return false + } + if r1.OtherData != r2.OtherData { + return false + } + return true +} + +func isDuplicateTXT(r1, r2 *TXT) bool { + if len(r1.Txt) != len(r2.Txt) { + return false + } + for i := 0; i < len(r1.Txt); i++ { + if r1.Txt[i] != r2.Txt[i] { + return false + } + } + return true +} + +func isDuplicateUID(r1, r2 *UID) bool { + if r1.Uid != r2.Uid { + return false + } + return true +} + +func isDuplicateUINFO(r1, r2 *UINFO) bool { + if r1.Uinfo != r2.Uinfo { + return false + } + return true +} + +func isDuplicateURI(r1, r2 *URI) bool { + if r1.Priority != r2.Priority { + return false + } + if r1.Weight != r2.Weight { + return false + } + if r1.Target != r2.Target { + return false + } + return true +} + +func isDuplicateX25(r1, r2 *X25) bool { + if r1.PSDNAddress != r2.PSDNAddress { + return false + } + return true +} diff --git a/vendor/github.com/miekg/dns/zmsg.go b/vendor/github.com/miekg/dns/zmsg.go index 0d1f6f4da..3e93a782f 100644 --- a/vendor/github.com/miekg/dns/zmsg.go +++ b/vendor/github.com/miekg/dns/zmsg.go @@ -4,1490 +4,1350 @@ package dns // pack*() functions -func (rr *A) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *A) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packDataA(rr.A, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *AAAA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *AAAA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packDataAAAA(rr.AAAA, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *AFSDB) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *AFSDB) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Subtype, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Hostname, msg, off, compression, compress) + off, _, err = packDomainName(rr.Hostname, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *ANY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *AVC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringTxt(rr.Txt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CAA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CAA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Flag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Tag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringOctet(rr.Value, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CDNSKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CDNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CDS) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CDS) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CERT) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CERT) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Type, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.Certificate, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CNAME) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Target, msg, off, compression, compress) + off, _, err = packDomainName(rr.Target, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CSYNC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CSYNC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint32(rr.Serial, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DHCID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DHCID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringBase64(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DLV) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DLV) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DNAME) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Target, msg, off, compression, false) + off, _, err = packDomainName(rr.Target, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DNSKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DS) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DS) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *EID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *EID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringHex(rr.Endpoint, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *EUI48) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *EUI48) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint48(rr.Address, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *EUI64) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *EUI64) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint64(rr.Address, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *GID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *GID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint32(rr.Gid, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *GPOS) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *GPOS) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packString(rr.Longitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Latitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Altitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *HINFO) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *HINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packString(rr.Cpu, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Os, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *HIP) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *HIP) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.HitLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.PublicKeyAlgorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.PublicKeyLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Hit, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = packDataDomainNames(rr.RendezvousServers, msg, off, compression, compress) + off, err = packDataDomainNames(rr.RendezvousServers, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *KEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *KX) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *KX) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Exchanger, msg, off, compression, false) + off, _, err = packDomainName(rr.Exchanger, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *L32) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *L32) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packDataA(rr.Locator32, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *L64) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *L64) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint64(rr.Locator64, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *LOC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *LOC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Version, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Size, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.HorizPre, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.VertPre, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Latitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Longitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Altitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *LP) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *LP) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Fqdn, msg, off, compression, false) + off, _, err = packDomainName(rr.Fqdn, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MB) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MB) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mb, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mb, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MD) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MD) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Md, msg, off, compression, compress) + off, _, err = packDomainName(rr.Md, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MF) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MF) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mf, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mf, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MG) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MG) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mg, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mg, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MINFO) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Rmail, msg, off, compression, compress) + off, _, err = packDomainName(rr.Rmail, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Email, msg, off, compression, compress) + off, _, err = packDomainName(rr.Email, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mr, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mr, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MX) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MX) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Mx, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mx, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NAPTR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NAPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Order, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Service, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Regexp, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Replacement, msg, off, compression, false) + off, _, err = packDomainName(rr.Replacement, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint64(rr.NodeID, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NIMLOC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NIMLOC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringHex(rr.Locator, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NINFO) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringTxt(rr.ZSData, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NS) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NS) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Ns, msg, off, compression, compress) + off, _, err = packDomainName(rr.Ns, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NSAPPTR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NSAPPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Ptr, msg, off, compression, false) + off, _, err = packDomainName(rr.Ptr, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NSEC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NSEC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.NextDomain, msg, off, compression, false) + off, _, err = packDomainName(rr.NextDomain, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NSEC3) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NSEC3) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Hash, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Iterations, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.SaltLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } // Only pack salt if value is not "-", i.e. empty if rr.Salt != "-" { off, err = packStringHex(rr.Salt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } } off, err = packUint8(rr.HashLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase32(rr.NextDomain, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NSEC3PARAM) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NSEC3PARAM) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Hash, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Iterations, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.SaltLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } // Only pack salt if value is not "-", i.e. empty if rr.Salt != "-" { off, err = packStringHex(rr.Salt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *OPENPGPKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *OPT) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *OPT) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packDataOpt(rr.Option, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *PTR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *PTR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Ptr, msg, off, compression, compress) + off, _, err = packDomainName(rr.Ptr, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *PX) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *PX) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Map822, msg, off, compression, false) + off, _, err = packDomainName(rr.Map822, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Mapx400, msg, off, compression, false) + off, _, err = packDomainName(rr.Mapx400, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RFC3597) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RFC3597) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringHex(rr.Rdata, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RP) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RP) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mbox, msg, off, compression, false) + off, _, err = packDomainName(rr.Mbox, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Txt, msg, off, compression, false) + off, _, err = packDomainName(rr.Txt, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RRSIG) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RRSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.TypeCovered, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Labels, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.OrigTtl, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.SignerName, msg, off, compression, false) + off, _, err = packDomainName(rr.SignerName, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.Signature, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RT) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RT) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Host, msg, off, compression, compress) + off, _, err = packDomainName(rr.Host, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SIG) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SIG) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.TypeCovered, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Labels, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.OrigTtl, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.SignerName, msg, off, compression, false) + off, _, err = packDomainName(rr.SignerName, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.Signature, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SMIMEA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SMIMEA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Usage, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Selector, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.MatchingType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Certificate, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SOA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SOA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Ns, msg, off, compression, compress) + off, _, err = packDomainName(rr.Ns, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Mbox, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mbox, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Serial, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Refresh, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Retry, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Expire, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Minttl, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SPF) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SPF) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringTxt(rr.Txt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SRV) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SRV) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Priority, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Weight, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Port, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Target, msg, off, compression, false) + off, _, err = packDomainName(rr.Target, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SSHFP) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SSHFP) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Type, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.FingerPrint, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TALINK) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TALINK) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.PreviousName, msg, off, compression, false) + off, _, err = packDomainName(rr.PreviousName, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.NextName, msg, off, compression, false) + off, _, err = packDomainName(rr.NextName, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Algorithm, msg, off, compression, false) + off, _, err = packDomainName(rr.Algorithm, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Mode, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Error, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.KeySize, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Key, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.OtherLen, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.OtherData, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TLSA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TLSA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Usage, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Selector, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.MatchingType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Certificate, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TSIG) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Algorithm, msg, off, compression, false) + off, _, err = packDomainName(rr.Algorithm, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint48(rr.TimeSigned, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Fudge, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.MACSize, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.MAC, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.OrigId, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Error, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.OtherLen, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.OtherData, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TXT) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TXT) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringTxt(rr.Txt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *UID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *UID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint32(rr.Uid, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *UINFO) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *UINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packString(rr.Uinfo, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *URI) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *URI) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Priority, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Weight, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringOctet(rr.Target, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *X25) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packString(rr.PSDNAddress, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } // unpack*() functions diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go index abd75dd91..71662b7b7 100644 --- a/vendor/github.com/miekg/dns/ztypes.go +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -236,144 +236,144 @@ 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() +func (rr *A) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += net.IPv4len // A return l } -func (rr *AAAA) len() int { - l := rr.Hdr.len() +func (rr *AAAA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += net.IPv6len // AAAA return l } -func (rr *AFSDB) len() int { - l := rr.Hdr.len() +func (rr *AFSDB) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Subtype - l += len(rr.Hostname) + 1 + l += domainNameLen(rr.Hostname, off+l, compression, false) return l } -func (rr *ANY) len() int { - l := rr.Hdr.len() +func (rr *ANY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) return l } -func (rr *AVC) len() int { - l := rr.Hdr.len() +func (rr *AVC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } -func (rr *CAA) len() int { - l := rr.Hdr.len() +func (rr *CAA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Flag l += len(rr.Tag) + 1 l += len(rr.Value) return l } -func (rr *CERT) len() int { - l := rr.Hdr.len() +func (rr *CERT) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Type l += 2 // KeyTag l++ // Algorithm l += base64.StdEncoding.DecodedLen(len(rr.Certificate)) return l } -func (rr *CNAME) len() int { - l := rr.Hdr.len() - l += len(rr.Target) + 1 +func (rr *CNAME) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Target, off+l, compression, true) return l } -func (rr *DHCID) len() int { - l := rr.Hdr.len() +func (rr *DHCID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.Digest)) return l } -func (rr *DNAME) len() int { - l := rr.Hdr.len() - l += len(rr.Target) + 1 +func (rr *DNAME) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Target, off+l, compression, false) return l } -func (rr *DNSKEY) len() int { - l := rr.Hdr.len() +func (rr *DNSKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Flags l++ // Protocol l++ // Algorithm l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } -func (rr *DS) len() int { - l := rr.Hdr.len() +func (rr *DS) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // KeyTag l++ // Algorithm l++ // DigestType l += len(rr.Digest)/2 + 1 return l } -func (rr *EID) len() int { - l := rr.Hdr.len() +func (rr *EID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Endpoint)/2 + 1 return l } -func (rr *EUI48) len() int { - l := rr.Hdr.len() +func (rr *EUI48) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 6 // Address return l } -func (rr *EUI64) len() int { - l := rr.Hdr.len() +func (rr *EUI64) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 8 // Address return l } -func (rr *GID) len() int { - l := rr.Hdr.len() +func (rr *GID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 4 // Gid return l } -func (rr *GPOS) len() int { - l := rr.Hdr.len() +func (rr *GPOS) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) 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() +func (rr *HINFO) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Cpu) + 1 l += len(rr.Os) + 1 return l } -func (rr *HIP) len() int { - l := rr.Hdr.len() +func (rr *HIP) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // HitLength l++ // PublicKeyAlgorithm l += 2 // PublicKeyLength l += len(rr.Hit) / 2 l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) for _, x := range rr.RendezvousServers { - l += len(x) + 1 + l += domainNameLen(x, off+l, compression, false) } return l } -func (rr *KX) len() int { - l := rr.Hdr.len() +func (rr *KX) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Exchanger) + 1 + l += domainNameLen(rr.Exchanger, off+l, compression, false) return l } -func (rr *L32) len() int { - l := rr.Hdr.len() +func (rr *L32) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference l += net.IPv4len // Locator32 return l } -func (rr *L64) len() int { - l := rr.Hdr.len() +func (rr *L64) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // Locator64 return l } -func (rr *LOC) len() int { - l := rr.Hdr.len() +func (rr *LOC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Version l++ // Size l++ // HorizPre @@ -383,89 +383,89 @@ func (rr *LOC) len() int { l += 4 // Altitude return l } -func (rr *LP) len() int { - l := rr.Hdr.len() +func (rr *LP) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Fqdn) + 1 + l += domainNameLen(rr.Fqdn, off+l, compression, false) return l } -func (rr *MB) len() int { - l := rr.Hdr.len() - l += len(rr.Mb) + 1 +func (rr *MB) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mb, off+l, compression, true) return l } -func (rr *MD) len() int { - l := rr.Hdr.len() - l += len(rr.Md) + 1 +func (rr *MD) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Md, off+l, compression, true) return l } -func (rr *MF) len() int { - l := rr.Hdr.len() - l += len(rr.Mf) + 1 +func (rr *MF) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mf, off+l, compression, true) return l } -func (rr *MG) len() int { - l := rr.Hdr.len() - l += len(rr.Mg) + 1 +func (rr *MG) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mg, off+l, compression, true) return l } -func (rr *MINFO) len() int { - l := rr.Hdr.len() - l += len(rr.Rmail) + 1 - l += len(rr.Email) + 1 +func (rr *MINFO) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Rmail, off+l, compression, true) + l += domainNameLen(rr.Email, off+l, compression, true) return l } -func (rr *MR) len() int { - l := rr.Hdr.len() - l += len(rr.Mr) + 1 +func (rr *MR) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mr, off+l, compression, true) return l } -func (rr *MX) len() int { - l := rr.Hdr.len() +func (rr *MX) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Mx) + 1 + l += domainNameLen(rr.Mx, off+l, compression, true) return l } -func (rr *NAPTR) len() int { - l := rr.Hdr.len() +func (rr *NAPTR) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) 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 + l += domainNameLen(rr.Replacement, off+l, compression, false) return l } -func (rr *NID) len() int { - l := rr.Hdr.len() +func (rr *NID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // NodeID return l } -func (rr *NIMLOC) len() int { - l := rr.Hdr.len() +func (rr *NIMLOC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Locator)/2 + 1 return l } -func (rr *NINFO) len() int { - l := rr.Hdr.len() +func (rr *NINFO) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) 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 +func (rr *NS) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Ns, off+l, compression, true) return l } -func (rr *NSAPPTR) len() int { - l := rr.Hdr.len() - l += len(rr.Ptr) + 1 +func (rr *NSAPPTR) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Ptr, off+l, compression, false) return l } -func (rr *NSEC3PARAM) len() int { - l := rr.Hdr.len() +func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Hash l++ // Flags l += 2 // Iterations @@ -473,44 +473,44 @@ func (rr *NSEC3PARAM) len() int { l += len(rr.Salt) / 2 return l } -func (rr *OPENPGPKEY) len() int { - l := rr.Hdr.len() +func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } -func (rr *PTR) len() int { - l := rr.Hdr.len() - l += len(rr.Ptr) + 1 +func (rr *PTR) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Ptr, off+l, compression, true) return l } -func (rr *PX) len() int { - l := rr.Hdr.len() +func (rr *PX) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Map822) + 1 - l += len(rr.Mapx400) + 1 + l += domainNameLen(rr.Map822, off+l, compression, false) + l += domainNameLen(rr.Mapx400, off+l, compression, false) return l } -func (rr *RFC3597) len() int { - l := rr.Hdr.len() +func (rr *RFC3597) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Rdata)/2 + 1 return l } -func (rr *RKEY) len() int { - l := rr.Hdr.len() +func (rr *RKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Flags l++ // Protocol l++ // 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 +func (rr *RP) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mbox, off+l, compression, false) + l += domainNameLen(rr.Txt, off+l, compression, false) return l } -func (rr *RRSIG) len() int { - l := rr.Hdr.len() +func (rr *RRSIG) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // TypeCovered l++ // Algorithm l++ // Labels @@ -518,28 +518,28 @@ func (rr *RRSIG) len() int { l += 4 // Expiration l += 4 // Inception l += 2 // KeyTag - l += len(rr.SignerName) + 1 + l += domainNameLen(rr.SignerName, off+l, compression, false) l += base64.StdEncoding.DecodedLen(len(rr.Signature)) return l } -func (rr *RT) len() int { - l := rr.Hdr.len() +func (rr *RT) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Host) + 1 + l += domainNameLen(rr.Host, off+l, compression, false) return l } -func (rr *SMIMEA) len() int { - l := rr.Hdr.len() +func (rr *SMIMEA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Usage l++ // Selector l++ // 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 +func (rr *SOA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Ns, off+l, compression, true) + l += domainNameLen(rr.Mbox, off+l, compression, true) l += 4 // Serial l += 4 // Refresh l += 4 // Retry @@ -547,45 +547,45 @@ func (rr *SOA) len() int { l += 4 // Minttl return l } -func (rr *SPF) len() int { - l := rr.Hdr.len() +func (rr *SPF) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } -func (rr *SRV) len() int { - l := rr.Hdr.len() +func (rr *SRV) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Priority l += 2 // Weight l += 2 // Port - l += len(rr.Target) + 1 + l += domainNameLen(rr.Target, off+l, compression, false) return l } -func (rr *SSHFP) len() int { - l := rr.Hdr.len() +func (rr *SSHFP) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Algorithm l++ // Type l += len(rr.FingerPrint)/2 + 1 return l } -func (rr *TA) len() int { - l := rr.Hdr.len() +func (rr *TA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // KeyTag l++ // Algorithm l++ // 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 +func (rr *TALINK) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.PreviousName, off+l, compression, false) + l += domainNameLen(rr.NextName, off+l, compression, false) return l } -func (rr *TKEY) len() int { - l := rr.Hdr.len() - l += len(rr.Algorithm) + 1 +func (rr *TKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Algorithm, off+l, compression, false) l += 4 // Inception l += 4 // Expiration l += 2 // Mode @@ -596,17 +596,17 @@ func (rr *TKEY) len() int { l += len(rr.OtherData) / 2 return l } -func (rr *TLSA) len() int { - l := rr.Hdr.len() +func (rr *TLSA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Usage l++ // Selector l++ // MatchingType l += len(rr.Certificate)/2 + 1 return l } -func (rr *TSIG) len() int { - l := rr.Hdr.len() - l += len(rr.Algorithm) + 1 +func (rr *TSIG) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Algorithm, off+l, compression, false) l += 6 // TimeSigned l += 2 // Fudge l += 2 // MACSize @@ -617,247 +617,247 @@ func (rr *TSIG) len() int { l += len(rr.OtherData) / 2 return l } -func (rr *TXT) len() int { - l := rr.Hdr.len() +func (rr *TXT) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } -func (rr *UID) len() int { - l := rr.Hdr.len() +func (rr *UID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 4 // Uid return l } -func (rr *UINFO) len() int { - l := rr.Hdr.len() +func (rr *UINFO) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Uinfo) + 1 return l } -func (rr *URI) len() int { - l := rr.Hdr.len() +func (rr *URI) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Priority l += 2 // Weight l += len(rr.Target) return l } -func (rr *X25) len() int { - l := rr.Hdr.len() +func (rr *X25) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.PSDNAddress) + 1 return l } // copy() functions func (rr *A) copy() RR { - return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)} + return &A{rr.Hdr, copyIP(rr.A)} } func (rr *AAAA) copy() RR { - return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)} + return &AAAA{rr.Hdr, copyIP(rr.AAAA)} } func (rr *AFSDB) copy() RR { - return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname} + return &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname} } func (rr *ANY) copy() RR { - return &ANY{*rr.Hdr.copyHeader()} + return &ANY{rr.Hdr} } func (rr *AVC) copy() RR { Txt := make([]string, len(rr.Txt)) copy(Txt, rr.Txt) - return &AVC{*rr.Hdr.copyHeader(), Txt} + return &AVC{rr.Hdr, Txt} } func (rr *CAA) copy() RR { - return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value} + return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value} } func (rr *CERT) copy() RR { - return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate} + return &CERT{rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate} } func (rr *CNAME) copy() RR { - return &CNAME{*rr.Hdr.copyHeader(), rr.Target} + return &CNAME{rr.Hdr, rr.Target} } func (rr *CSYNC) copy() RR { TypeBitMap := make([]uint16, len(rr.TypeBitMap)) copy(TypeBitMap, rr.TypeBitMap) - return &CSYNC{*rr.Hdr.copyHeader(), rr.Serial, rr.Flags, TypeBitMap} + return &CSYNC{rr.Hdr, rr.Serial, rr.Flags, TypeBitMap} } func (rr *DHCID) copy() RR { - return &DHCID{*rr.Hdr.copyHeader(), rr.Digest} + return &DHCID{rr.Hdr, rr.Digest} } func (rr *DNAME) copy() RR { - return &DNAME{*rr.Hdr.copyHeader(), rr.Target} + return &DNAME{rr.Hdr, rr.Target} } func (rr *DNSKEY) copy() RR { - return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} + return &DNSKEY{rr.Hdr, 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} + return &DS{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} } func (rr *EID) copy() RR { - return &EID{*rr.Hdr.copyHeader(), rr.Endpoint} + return &EID{rr.Hdr, rr.Endpoint} } func (rr *EUI48) copy() RR { - return &EUI48{*rr.Hdr.copyHeader(), rr.Address} + return &EUI48{rr.Hdr, rr.Address} } func (rr *EUI64) copy() RR { - return &EUI64{*rr.Hdr.copyHeader(), rr.Address} + return &EUI64{rr.Hdr, rr.Address} } func (rr *GID) copy() RR { - return &GID{*rr.Hdr.copyHeader(), rr.Gid} + return &GID{rr.Hdr, rr.Gid} } func (rr *GPOS) copy() RR { - return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude} + return &GPOS{rr.Hdr, rr.Longitude, rr.Latitude, rr.Altitude} } func (rr *HINFO) copy() RR { - return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os} + return &HINFO{rr.Hdr, 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} + return &HIP{rr.Hdr, 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} + return &KX{rr.Hdr, rr.Preference, rr.Exchanger} } func (rr *L32) copy() RR { - return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)} + return &L32{rr.Hdr, rr.Preference, copyIP(rr.Locator32)} } func (rr *L64) copy() RR { - return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64} + return &L64{rr.Hdr, 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} + return &LOC{rr.Hdr, 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} + return &LP{rr.Hdr, rr.Preference, rr.Fqdn} } func (rr *MB) copy() RR { - return &MB{*rr.Hdr.copyHeader(), rr.Mb} + return &MB{rr.Hdr, rr.Mb} } func (rr *MD) copy() RR { - return &MD{*rr.Hdr.copyHeader(), rr.Md} + return &MD{rr.Hdr, rr.Md} } func (rr *MF) copy() RR { - return &MF{*rr.Hdr.copyHeader(), rr.Mf} + return &MF{rr.Hdr, rr.Mf} } func (rr *MG) copy() RR { - return &MG{*rr.Hdr.copyHeader(), rr.Mg} + return &MG{rr.Hdr, rr.Mg} } func (rr *MINFO) copy() RR { - return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email} + return &MINFO{rr.Hdr, rr.Rmail, rr.Email} } func (rr *MR) copy() RR { - return &MR{*rr.Hdr.copyHeader(), rr.Mr} + return &MR{rr.Hdr, rr.Mr} } func (rr *MX) copy() RR { - return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx} + return &MX{rr.Hdr, 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} + return &NAPTR{rr.Hdr, 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} + return &NID{rr.Hdr, rr.Preference, rr.NodeID} } func (rr *NIMLOC) copy() RR { - return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator} + return &NIMLOC{rr.Hdr, rr.Locator} } func (rr *NINFO) copy() RR { ZSData := make([]string, len(rr.ZSData)) copy(ZSData, rr.ZSData) - return &NINFO{*rr.Hdr.copyHeader(), ZSData} + return &NINFO{rr.Hdr, ZSData} } func (rr *NS) copy() RR { - return &NS{*rr.Hdr.copyHeader(), rr.Ns} + return &NS{rr.Hdr, rr.Ns} } func (rr *NSAPPTR) copy() RR { - return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr} + return &NSAPPTR{rr.Hdr, 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} + return &NSEC{rr.Hdr, 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} + return &NSEC3{rr.Hdr, 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} + return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt} } func (rr *OPENPGPKEY) copy() RR { - return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey} + return &OPENPGPKEY{rr.Hdr, rr.PublicKey} } func (rr *OPT) copy() RR { Option := make([]EDNS0, len(rr.Option)) copy(Option, rr.Option) - return &OPT{*rr.Hdr.copyHeader(), Option} + return &OPT{rr.Hdr, Option} } func (rr *PTR) copy() RR { - return &PTR{*rr.Hdr.copyHeader(), rr.Ptr} + return &PTR{rr.Hdr, rr.Ptr} } func (rr *PX) copy() RR { - return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400} + return &PX{rr.Hdr, rr.Preference, rr.Map822, rr.Mapx400} } func (rr *RFC3597) copy() RR { - return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata} + return &RFC3597{rr.Hdr, rr.Rdata} } func (rr *RKEY) copy() RR { - return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} + return &RKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} } func (rr *RP) copy() RR { - return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt} + return &RP{rr.Hdr, 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} + return &RRSIG{rr.Hdr, 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} + return &RT{rr.Hdr, rr.Preference, rr.Host} } func (rr *SMIMEA) copy() RR { - return &SMIMEA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} + return &SMIMEA{rr.Hdr, 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} + return &SOA{rr.Hdr, 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} + return &SPF{rr.Hdr, Txt} } func (rr *SRV) copy() RR { - return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target} + return &SRV{rr.Hdr, rr.Priority, rr.Weight, rr.Port, rr.Target} } func (rr *SSHFP) copy() RR { - return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint} + return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint} } func (rr *TA) copy() RR { - return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} + return &TA{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} } func (rr *TALINK) copy() RR { - return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName} + return &TALINK{rr.Hdr, 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} + return &TKEY{rr.Hdr, 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} + return &TLSA{rr.Hdr, 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} + return &TSIG{rr.Hdr, 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} + return &TXT{rr.Hdr, Txt} } func (rr *UID) copy() RR { - return &UID{*rr.Hdr.copyHeader(), rr.Uid} + return &UID{rr.Hdr, rr.Uid} } func (rr *UINFO) copy() RR { - return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo} + return &UINFO{rr.Hdr, rr.Uinfo} } func (rr *URI) copy() RR { - return &URI{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Target} + return &URI{rr.Hdr, rr.Priority, rr.Weight, rr.Target} } func (rr *X25) copy() RR { - return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress} + return &X25{rr.Hdr, rr.PSDNAddress} } diff --git a/vendor/github.com/xenolf/lego/acme/api/account.go b/vendor/github.com/xenolf/lego/acme/api/account.go new file mode 100644 index 000000000..489be4207 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/account.go @@ -0,0 +1,69 @@ +package api + +import ( + "encoding/base64" + "errors" + "fmt" + + "github.com/xenolf/lego/acme" +) + +type AccountService service + +// New Creates a new account. +func (a *AccountService) New(req acme.Account) (acme.ExtendedAccount, error) { + var account acme.Account + resp, err := a.core.post(a.core.GetDirectory().NewAccountURL, req, &account) + location := getLocation(resp) + + if len(location) > 0 { + a.core.jws.SetKid(location) + } + + if err != nil { + return acme.ExtendedAccount{Location: location}, err + } + + return acme.ExtendedAccount{Account: account, Location: location}, nil +} + +// NewEAB Creates a new account with an External Account Binding. +func (a *AccountService) NewEAB(accMsg acme.Account, kid string, hmacEncoded string) (acme.ExtendedAccount, error) { + hmac, err := base64.RawURLEncoding.DecodeString(hmacEncoded) + if err != nil { + return acme.ExtendedAccount{}, fmt.Errorf("acme: could not decode hmac key: %v", err) + } + + eabJWS, err := a.core.signEABContent(a.core.GetDirectory().NewAccountURL, kid, hmac) + if err != nil { + return acme.ExtendedAccount{}, fmt.Errorf("acme: error signing eab content: %v", err) + } + accMsg.ExternalAccountBinding = eabJWS + + return a.New(accMsg) +} + +// Get Retrieves an account. +func (a *AccountService) Get(accountURL string) (acme.Account, error) { + if len(accountURL) == 0 { + return acme.Account{}, errors.New("account[get]: empty URL") + } + + var account acme.Account + _, err := a.core.post(accountURL, acme.Account{}, &account) + if err != nil { + return acme.Account{}, err + } + return account, nil +} + +// Deactivate Deactivates an account. +func (a *AccountService) Deactivate(accountURL string) error { + if len(accountURL) == 0 { + return errors.New("account[deactivate]: empty URL") + } + + req := acme.Account{Status: acme.StatusDeactivated} + _, err := a.core.post(accountURL, req, nil) + return err +} diff --git a/vendor/github.com/xenolf/lego/acme/api/api.go b/vendor/github.com/xenolf/lego/acme/api/api.go new file mode 100644 index 000000000..e14cd993a --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/api.go @@ -0,0 +1,151 @@ +package api + +import ( + "bytes" + "crypto" + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/acme/api/internal/nonces" + "github.com/xenolf/lego/acme/api/internal/secure" + "github.com/xenolf/lego/acme/api/internal/sender" + "github.com/xenolf/lego/log" +) + +// Core ACME/LE core API. +type Core struct { + doer *sender.Doer + nonceManager *nonces.Manager + jws *secure.JWS + directory acme.Directory + HTTPClient *http.Client + + common service // Reuse a single struct instead of allocating one for each service on the heap. + Accounts *AccountService + Authorizations *AuthorizationService + Certificates *CertificateService + Challenges *ChallengeService + Orders *OrderService +} + +// New Creates a new Core. +func New(httpClient *http.Client, userAgent string, caDirURL, kid string, privateKey crypto.PrivateKey) (*Core, error) { + doer := sender.NewDoer(httpClient, userAgent) + + dir, err := getDirectory(doer, caDirURL) + if err != nil { + return nil, err + } + + nonceManager := nonces.NewManager(doer, dir.NewNonceURL) + + jws := secure.NewJWS(privateKey, kid, nonceManager) + + c := &Core{doer: doer, nonceManager: nonceManager, jws: jws, directory: dir} + + c.common.core = c + c.Accounts = (*AccountService)(&c.common) + c.Authorizations = (*AuthorizationService)(&c.common) + c.Certificates = (*CertificateService)(&c.common) + c.Challenges = (*ChallengeService)(&c.common) + c.Orders = (*OrderService)(&c.common) + + return c, nil +} + +// post performs an HTTP POST request and parses the response body as JSON, +// into the provided respBody object. +func (a *Core) post(uri string, reqBody, response interface{}) (*http.Response, error) { + content, err := json.Marshal(reqBody) + if err != nil { + return nil, errors.New("failed to marshal message") + } + + return a.retrievablePost(uri, content, response, 0) +} + +// postAsGet performs an HTTP POST ("POST-as-GET") request. +// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.3 +func (a *Core) postAsGet(uri string, response interface{}) (*http.Response, error) { + return a.retrievablePost(uri, []byte{}, response, 0) +} + +func (a *Core) retrievablePost(uri string, content []byte, response interface{}, retry int) (*http.Response, error) { + resp, err := a.signedPost(uri, content, response) + if err != nil { + // during tests, 5 retries allow to support ~50% of bad nonce. + if retry >= 5 { + log.Infof("too many retry on a nonce error, retry count: %d", retry) + return resp, err + } + switch err.(type) { + // Retry once if the nonce was invalidated + case *acme.NonceError: + log.Infof("nonce error retry: %s", err) + resp, err = a.retrievablePost(uri, content, response, retry+1) + if err != nil { + return resp, err + } + default: + return resp, err + } + } + + return resp, nil +} + +func (a *Core) signedPost(uri string, content []byte, response interface{}) (*http.Response, error) { + signedContent, err := a.jws.SignContent(uri, content) + if err != nil { + return nil, fmt.Errorf("failed to post JWS message -> failed to sign content -> %v", err) + } + + signedBody := bytes.NewBuffer([]byte(signedContent.FullSerialize())) + + resp, err := a.doer.Post(uri, signedBody, "application/jose+json", response) + + // nonceErr is ignored to keep the root error. + nonce, nonceErr := nonces.GetFromResponse(resp) + if nonceErr == nil { + a.nonceManager.Push(nonce) + } + + return resp, err +} + +func (a *Core) signEABContent(newAccountURL, kid string, hmac []byte) ([]byte, error) { + eabJWS, err := a.jws.SignEABContent(newAccountURL, kid, hmac) + if err != nil { + return nil, err + } + + return []byte(eabJWS.FullSerialize()), nil +} + +// GetKeyAuthorization Gets the key authorization +func (a *Core) GetKeyAuthorization(token string) (string, error) { + return a.jws.GetKeyAuthorization(token) +} + +func (a *Core) GetDirectory() acme.Directory { + return a.directory +} + +func getDirectory(do *sender.Doer, caDirURL string) (acme.Directory, error) { + var dir acme.Directory + if _, err := do.Get(caDirURL, &dir); err != nil { + return dir, fmt.Errorf("get directory at '%s': %v", caDirURL, err) + } + + if dir.NewAccountURL == "" { + return dir, errors.New("directory missing new registration URL") + } + if dir.NewOrderURL == "" { + return dir, errors.New("directory missing new order URL") + } + + return dir, nil +} diff --git a/vendor/github.com/xenolf/lego/acme/api/authorization.go b/vendor/github.com/xenolf/lego/acme/api/authorization.go new file mode 100644 index 000000000..ed4a48672 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/authorization.go @@ -0,0 +1,34 @@ +package api + +import ( + "errors" + + "github.com/xenolf/lego/acme" +) + +type AuthorizationService service + +// Get Gets an authorization. +func (c *AuthorizationService) Get(authzURL string) (acme.Authorization, error) { + if len(authzURL) == 0 { + return acme.Authorization{}, errors.New("authorization[get]: empty URL") + } + + var authz acme.Authorization + _, err := c.core.postAsGet(authzURL, &authz) + if err != nil { + return acme.Authorization{}, err + } + return authz, nil +} + +// Deactivate Deactivates an authorization. +func (c *AuthorizationService) Deactivate(authzURL string) error { + if len(authzURL) == 0 { + return errors.New("authorization[deactivate]: empty URL") + } + + var disabledAuth acme.Authorization + _, err := c.core.post(authzURL, acme.Authorization{Status: acme.StatusDeactivated}, &disabledAuth) + return err +} diff --git a/vendor/github.com/xenolf/lego/acme/api/certificate.go b/vendor/github.com/xenolf/lego/acme/api/certificate.go new file mode 100644 index 000000000..db939ae42 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/certificate.go @@ -0,0 +1,99 @@ +package api + +import ( + "crypto/x509" + "encoding/pem" + "errors" + "io/ioutil" + "net/http" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/certcrypto" + "github.com/xenolf/lego/log" +) + +// maxBodySize is the maximum size of body that we will read. +const maxBodySize = 1024 * 1024 + +type CertificateService service + +// Get Returns the certificate and the issuer certificate. +// 'bundle' is only applied if the issuer is provided by the 'up' link. +func (c *CertificateService) Get(certURL string, bundle bool) ([]byte, []byte, error) { + cert, up, err := c.get(certURL) + if err != nil { + return nil, nil, err + } + + // Get issuerCert from bundled response from Let's Encrypt + // See https://community.letsencrypt.org/t/acme-v2-no-up-link-in-response/64962 + _, issuer := pem.Decode(cert) + if issuer != nil { + return cert, issuer, nil + } + + issuer, err = c.getIssuerFromLink(up) + if err != nil { + // If we fail to acquire the issuer cert, return the issued certificate - do not fail. + log.Warnf("acme: Could not bundle issuer certificate [%s]: %v", certURL, err) + } else if len(issuer) > 0 { + // If bundle is true, we want to return a certificate bundle. + // To do this, we append the issuer cert to the issued cert. + if bundle { + cert = append(cert, issuer...) + } + } + + return cert, issuer, nil +} + +// Revoke Revokes a certificate. +func (c *CertificateService) Revoke(req acme.RevokeCertMessage) error { + _, err := c.core.post(c.core.GetDirectory().RevokeCertURL, req, nil) + return err +} + +// get Returns the certificate and the "up" link. +func (c *CertificateService) get(certURL string) ([]byte, string, error) { + if len(certURL) == 0 { + return nil, "", errors.New("certificate[get]: empty URL") + } + + resp, err := c.core.postAsGet(certURL, nil) + if err != nil { + return nil, "", err + } + + cert, err := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize)) + if err != nil { + return nil, "", err + } + + // The issuer certificate link may be supplied via an "up" link + // in the response headers of a new certificate. + // See https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4.2 + up := getLink(resp.Header, "up") + + return cert, up, err +} + +// getIssuerFromLink requests the issuer certificate +func (c *CertificateService) getIssuerFromLink(up string) ([]byte, error) { + if len(up) == 0 { + return nil, nil + } + + log.Infof("acme: Requesting issuer cert from %s", up) + + cert, _, err := c.get(up) + if err != nil { + return nil, err + } + + _, err = x509.ParseCertificate(cert) + if err != nil { + return nil, err + } + + return certcrypto.PEMEncode(certcrypto.DERCertificateBytes(cert)), nil +} diff --git a/vendor/github.com/xenolf/lego/acme/api/challenge.go b/vendor/github.com/xenolf/lego/acme/api/challenge.go new file mode 100644 index 000000000..afcd0aa23 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/challenge.go @@ -0,0 +1,45 @@ +package api + +import ( + "errors" + + "github.com/xenolf/lego/acme" +) + +type ChallengeService service + +// New Creates a challenge. +func (c *ChallengeService) New(chlgURL string) (acme.ExtendedChallenge, error) { + if len(chlgURL) == 0 { + return acme.ExtendedChallenge{}, errors.New("challenge[new]: empty URL") + } + + // Challenge initiation is done by sending a JWS payload containing the trivial JSON object `{}`. + // We use an empty struct instance as the postJSON payload here to achieve this result. + var chlng acme.ExtendedChallenge + resp, err := c.core.post(chlgURL, struct{}{}, &chlng) + if err != nil { + return acme.ExtendedChallenge{}, err + } + + chlng.AuthorizationURL = getLink(resp.Header, "up") + chlng.RetryAfter = getRetryAfter(resp) + return chlng, nil +} + +// Get Gets a challenge. +func (c *ChallengeService) Get(chlgURL string) (acme.ExtendedChallenge, error) { + if len(chlgURL) == 0 { + return acme.ExtendedChallenge{}, errors.New("challenge[get]: empty URL") + } + + var chlng acme.ExtendedChallenge + resp, err := c.core.postAsGet(chlgURL, &chlng) + if err != nil { + return acme.ExtendedChallenge{}, err + } + + chlng.AuthorizationURL = getLink(resp.Header, "up") + chlng.RetryAfter = getRetryAfter(resp) + return chlng, nil +} diff --git a/vendor/github.com/xenolf/lego/acme/api/internal/nonces/nonce_manager.go b/vendor/github.com/xenolf/lego/acme/api/internal/nonces/nonce_manager.go new file mode 100644 index 000000000..20a530100 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/internal/nonces/nonce_manager.go @@ -0,0 +1,78 @@ +package nonces + +import ( + "errors" + "fmt" + "net/http" + "sync" + + "github.com/xenolf/lego/acme/api/internal/sender" +) + +// Manager Manages nonces. +type Manager struct { + do *sender.Doer + nonceURL string + nonces []string + sync.Mutex +} + +// NewManager Creates a new Manager. +func NewManager(do *sender.Doer, nonceURL string) *Manager { + return &Manager{ + do: do, + nonceURL: nonceURL, + } +} + +// Pop Pops a nonce. +func (n *Manager) Pop() (string, bool) { + n.Lock() + defer n.Unlock() + + if len(n.nonces) == 0 { + return "", false + } + + nonce := n.nonces[len(n.nonces)-1] + n.nonces = n.nonces[:len(n.nonces)-1] + return nonce, true +} + +// Push Pushes a nonce. +func (n *Manager) Push(nonce string) { + n.Lock() + defer n.Unlock() + n.nonces = append(n.nonces, nonce) +} + +// Nonce implement jose.NonceSource +func (n *Manager) Nonce() (string, error) { + if nonce, ok := n.Pop(); ok { + return nonce, nil + } + return n.getNonce() +} + +func (n *Manager) getNonce() (string, error) { + resp, err := n.do.Head(n.nonceURL) + if err != nil { + return "", fmt.Errorf("failed to get nonce from HTTP HEAD -> %v", err) + } + + return GetFromResponse(resp) +} + +// GetFromResponse Extracts a nonce from a HTTP response. +func GetFromResponse(resp *http.Response) (string, error) { + if resp == nil { + return "", errors.New("nil response") + } + + nonce := resp.Header.Get("Replay-Nonce") + if nonce == "" { + return "", fmt.Errorf("server did not respond with a proper nonce header") + } + + return nonce, nil +} diff --git a/vendor/github.com/xenolf/lego/acme/api/internal/secure/jws.go b/vendor/github.com/xenolf/lego/acme/api/internal/secure/jws.go new file mode 100644 index 000000000..7645b6e99 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/internal/secure/jws.go @@ -0,0 +1,134 @@ +package secure + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "encoding/base64" + "errors" + "fmt" + + "github.com/xenolf/lego/acme/api/internal/nonces" + "gopkg.in/square/go-jose.v2" +) + +// JWS Represents a JWS. +type JWS struct { + privKey crypto.PrivateKey + kid string // Key identifier + nonces *nonces.Manager +} + +// NewJWS Create a new JWS. +func NewJWS(privateKey crypto.PrivateKey, kid string, nonceManager *nonces.Manager) *JWS { + return &JWS{ + privKey: privateKey, + nonces: nonceManager, + kid: kid, + } +} + +// SetKid Sets a key identifier. +func (j *JWS) SetKid(kid string) { + j.kid = kid +} + +// SignContent Signs a content with the JWS. +func (j *JWS) SignContent(url string, 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 + } + } + + signKey := jose.SigningKey{ + Algorithm: alg, + Key: jose.JSONWebKey{Key: j.privKey, KeyID: j.kid}, + } + + options := jose.SignerOptions{ + NonceSource: j.nonces, + ExtraHeaders: map[jose.HeaderKey]interface{}{ + "url": url, + }, + } + + if j.kid == "" { + options.EmbedJWK = true + } + + signer, err := jose.NewSigner(signKey, &options) + if err != nil { + return nil, fmt.Errorf("failed to create jose signer -> %v", err) + } + + signed, err := signer.Sign(content) + if err != nil { + return nil, fmt.Errorf("failed to sign content -> %v", err) + } + return signed, nil +} + +// SignEABContent Signs an external account binding content with the JWS. +func (j *JWS) SignEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignature, error) { + jwk := jose.JSONWebKey{Key: j.privKey} + jwkJSON, err := jwk.Public().MarshalJSON() + if err != nil { + return nil, fmt.Errorf("acme: error encoding eab jwk key: %v", err) + } + + signer, err := jose.NewSigner( + jose.SigningKey{Algorithm: jose.HS256, Key: hmac}, + &jose.SignerOptions{ + EmbedJWK: false, + ExtraHeaders: map[jose.HeaderKey]interface{}{ + "kid": kid, + "url": url, + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to create External Account Binding jose signer -> %v", err) + } + + signed, err := signer.Sign(jwkJSON) + if err != nil { + return nil, fmt.Errorf("failed to External Account Binding sign content -> %v", err) + } + + return signed, nil +} + +// GetKeyAuthorization Gets the key authorization for a token. +func (j *JWS) GetKeyAuthorization(token string) (string, error) { + var publicKey crypto.PublicKey + switch k := j.privKey.(type) { + case *ecdsa.PrivateKey: + publicKey = k.Public() + case *rsa.PrivateKey: + publicKey = k.Public() + } + + // Generate the Key Authorization for the challenge + jwk := &jose.JSONWebKey{Key: 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.RawURLEncoding.EncodeToString(thumbBytes) + + return token + "." + keyThumb, nil +} diff --git a/vendor/github.com/xenolf/lego/acme/api/internal/sender/sender.go b/vendor/github.com/xenolf/lego/acme/api/internal/sender/sender.go new file mode 100644 index 000000000..5e74e65f1 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/internal/sender/sender.go @@ -0,0 +1,146 @@ +package sender + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "runtime" + "strings" + + "github.com/xenolf/lego/acme" +) + +type RequestOption func(*http.Request) error + +func contentType(ct string) RequestOption { + return func(req *http.Request) error { + req.Header.Set("Content-Type", ct) + return nil + } +} + +type Doer struct { + httpClient *http.Client + userAgent string +} + +// NewDoer Creates a new Doer. +func NewDoer(client *http.Client, userAgent string) *Doer { + return &Doer{ + httpClient: client, + userAgent: userAgent, + } +} + +// Get performs a GET request with a proper User-Agent string. +// If "response" is not provided, callers should close resp.Body when done reading from it. +func (d *Doer) Get(url string, response interface{}) (*http.Response, error) { + req, err := d.newRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + + return d.do(req, response) +} + +// Head performs a HEAD request with a proper User-Agent string. +// The response body (resp.Body) is already closed when this function returns. +func (d *Doer) Head(url string) (*http.Response, error) { + req, err := d.newRequest(http.MethodHead, url, nil) + if err != nil { + return nil, err + } + + return d.do(req, nil) +} + +// Post performs a POST request with a proper User-Agent string. +// If "response" is not provided, callers should close resp.Body when done reading from it. +func (d *Doer) Post(url string, body io.Reader, bodyType string, response interface{}) (*http.Response, error) { + req, err := d.newRequest(http.MethodPost, url, body, contentType(bodyType)) + if err != nil { + return nil, err + } + + return d.do(req, response) +} + +func (d *Doer) newRequest(method, uri string, body io.Reader, opts ...RequestOption) (*http.Request, error) { + req, err := http.NewRequest(method, uri, body) + if err != nil { + return nil, fmt.Errorf("failed to create request: %v", err) + } + + req.Header.Set("User-Agent", d.formatUserAgent()) + + for _, opt := range opts { + err = opt(req) + if err != nil { + return nil, fmt.Errorf("failed to create request: %v", err) + } + } + + return req, nil +} + +func (d *Doer) do(req *http.Request, response interface{}) (*http.Response, error) { + resp, err := d.httpClient.Do(req) + if err != nil { + return nil, err + } + + if err = checkError(req, resp); err != nil { + return resp, err + } + + if response != nil { + raw, err := ioutil.ReadAll(resp.Body) + if err != nil { + return resp, err + } + + defer resp.Body.Close() + + err = json.Unmarshal(raw, response) + if err != nil { + return resp, fmt.Errorf("failed to unmarshal %q to type %T: %v", raw, response, err) + } + } + + return resp, nil +} + +// formatUserAgent builds and returns the User-Agent string to use in requests. +func (d *Doer) formatUserAgent() string { + ua := fmt.Sprintf("%s %s (%s; %s; %s)", d.userAgent, ourUserAgent, ourUserAgentComment, runtime.GOOS, runtime.GOARCH) + return strings.TrimSpace(ua) +} + +func checkError(req *http.Request, resp *http.Response) error { + if resp.StatusCode >= http.StatusBadRequest { + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("%d :: %s :: %s :: %v", resp.StatusCode, req.Method, req.URL, err) + } + + var errorDetails *acme.ProblemDetails + err = json.Unmarshal(body, &errorDetails) + if err != nil { + return fmt.Errorf("%d ::%s :: %s :: %v :: %s", resp.StatusCode, req.Method, req.URL, err, string(body)) + } + + errorDetails.Method = req.Method + errorDetails.URL = req.URL.String() + + // Check for errors we handle specifically + if errorDetails.HTTPStatus == http.StatusBadRequest && errorDetails.Type == acme.BadNonceErr { + return &acme.NonceError{ProblemDetails: errorDetails} + } + + return errorDetails + } + return nil +} diff --git a/vendor/github.com/xenolf/lego/acme/api/internal/sender/useragent.go b/vendor/github.com/xenolf/lego/acme/api/internal/sender/useragent.go new file mode 100644 index 000000000..4b129b6a0 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/internal/sender/useragent.go @@ -0,0 +1,14 @@ +package sender + +// CODE GENERATED AUTOMATICALLY +// THIS FILE MUST NOT BE EDITED BY HAND + +const ( + // ourUserAgent is the User-Agent of this underlying library package. + ourUserAgent = "xenolf-acme/1.2.1" + + // ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package. + // values: detach|release + // NOTE: Update this with each tagged release. + ourUserAgentComment = "detach" +) diff --git a/vendor/github.com/xenolf/lego/acme/api/order.go b/vendor/github.com/xenolf/lego/acme/api/order.go new file mode 100644 index 000000000..40e05a258 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/order.go @@ -0,0 +1,65 @@ +package api + +import ( + "encoding/base64" + "errors" + + "github.com/xenolf/lego/acme" +) + +type OrderService service + +// New Creates a new order. +func (o *OrderService) New(domains []string) (acme.ExtendedOrder, error) { + var identifiers []acme.Identifier + for _, domain := range domains { + identifiers = append(identifiers, acme.Identifier{Type: "dns", Value: domain}) + } + + orderReq := acme.Order{Identifiers: identifiers} + + var order acme.Order + resp, err := o.core.post(o.core.GetDirectory().NewOrderURL, orderReq, &order) + if err != nil { + return acme.ExtendedOrder{}, err + } + + return acme.ExtendedOrder{ + Location: resp.Header.Get("Location"), + Order: order, + }, nil +} + +// Get Gets an order. +func (o *OrderService) Get(orderURL string) (acme.Order, error) { + if len(orderURL) == 0 { + return acme.Order{}, errors.New("order[get]: empty URL") + } + + var order acme.Order + _, err := o.core.postAsGet(orderURL, &order) + if err != nil { + return acme.Order{}, err + } + + return order, nil +} + +// UpdateForCSR Updates an order for a CSR. +func (o *OrderService) UpdateForCSR(orderURL string, csr []byte) (acme.Order, error) { + csrMsg := acme.CSRMessage{ + Csr: base64.RawURLEncoding.EncodeToString(csr), + } + + var order acme.Order + _, err := o.core.post(orderURL, csrMsg, &order) + if err != nil { + return acme.Order{}, err + } + + if order.Status == acme.StatusInvalid { + return acme.Order{}, order.Error + } + + return order, nil +} diff --git a/vendor/github.com/xenolf/lego/acme/api/service.go b/vendor/github.com/xenolf/lego/acme/api/service.go new file mode 100644 index 000000000..ff043bc7b --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/api/service.go @@ -0,0 +1,45 @@ +package api + +import ( + "net/http" + "regexp" +) + +type service struct { + core *Core +} + +// getLink get a rel into the Link header +func getLink(header http.Header, rel string) string { + var linkExpr = regexp.MustCompile(`<(.+?)>;\s*rel="(.+?)"`) + + for _, link := range header["Link"] { + for _, m := range linkExpr.FindAllStringSubmatch(link, -1) { + if len(m) != 3 { + continue + } + if m[2] == rel { + return m[1] + } + } + } + return "" +} + +// getLocation get the value of the header Location +func getLocation(resp *http.Response) string { + if resp == nil { + return "" + } + + return resp.Header.Get("Location") +} + +// getRetryAfter get the value of the header Retry-After +func getRetryAfter(resp *http.Response) string { + if resp == nil { + return "" + } + + return resp.Header.Get("Retry-After") +} diff --git a/vendor/github.com/xenolf/lego/acme/challenges.go b/vendor/github.com/xenolf/lego/acme/challenges.go deleted file mode 100644 index d10f82b87..000000000 --- a/vendor/github.com/xenolf/lego/acme/challenges.go +++ /dev/null @@ -1,17 +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") - - // 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") - - // TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01 - TLSALPN01 = Challenge("tls-alpn-01") -) diff --git a/vendor/github.com/xenolf/lego/acme/client.go b/vendor/github.com/xenolf/lego/acme/client.go deleted file mode 100644 index f9857ebd0..000000000 --- a/vendor/github.com/xenolf/lego/acme/client.go +++ /dev/null @@ -1,957 +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/pem" - "errors" - "fmt" - "io/ioutil" - "net" - "regexp" - "strconv" - "strings" - "time" - - "github.com/xenolf/lego/log" -) - -const ( - // maxBodySize is the maximum size of body that we will read. - maxBodySize = 1024 * 1024 - - // overallRequestLimit is the overall number of request per second limited on the - // “new-reg”, “new-authz” and “new-cert” endpoints. From the documentation the - // limitation is 20 requests per second, but using 20 as value doesn't work but 18 do - overallRequestLimit = 18 - - statusValid = "valid" - statusInvalid = "invalid" -) - -// 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 -} - -// Interface for challenges like dns, where we can set a record in advance for ALL challenges. -// This saves quite a bit of time vs creating the records and solving them serially. -type preSolver interface { - PreSolve(challenge challenge, domain string) error -} - -// Interface for challenges like dns, where we can solve all the challenges before to delete them. -type cleanup interface { - CleanUp(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 - 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. A private -// key of type keyType (see KeyType contants) will be generated when requesting a new -// certificate if one isn't provided. -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.NewAccountURL == "" { - return nil, errors.New("directory missing new registration URL") - } - if dir.NewOrderURL == "" { - return nil, errors.New("directory missing new order URL") - } - - jws := &jws{privKey: privKey, getNonceURL: dir.NewNonceURL} - if reg := user.GetRegistration(); reg != nil { - jws.kid = reg.URI - } - - // 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 := map[Challenge]solver{ - HTTP01: &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}, - TLSALPN01: &tlsALPNChallenge{jws: jws, validate: validate, provider: &TLSALPNProviderServer{}}, - } - - return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil -} - -// SetChallengeProvider specifies a custom provider p that can solve the given challenge type. -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 DNS01: - c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, provider: p} - case TLSALPN01: - c.solvers[challenge] = &tlsALPNChallenge{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. -// -// NOTE: This REPLACES any custom HTTP provider previously set by calling -// c.SetChallengeProvider with the default HTTP challenge provider. -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. -// -// NOTE: This REPLACES any custom TLS-ALPN provider previously set by calling -// c.SetChallengeProvider with the default TLS-ALPN challenge provider. -func (c *Client) SetTLSAddress(iface string) error { - host, port, err := net.SplitHostPort(iface) - if err != nil { - return err - } - - if chlng, ok := c.solvers[TLSALPN01]; ok { - chlng.(*tlsALPNChallenge).provider = NewTLSALPNProviderServer(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) - } -} - -// GetToSURL returns the current ToS URL from the Directory -func (c *Client) GetToSURL() string { - return c.directory.Meta.TermsOfService -} - -// GetExternalAccountRequired returns the External Account Binding requirement of the Directory -func (c *Client) GetExternalAccountRequired() bool { - return c.directory.Meta.ExternalAccountRequired -} - -// Register the current account to the ACME server. -func (c *Client) Register(tosAgreed bool) (*RegistrationResource, error) { - if c == nil || c.user == nil { - return nil, errors.New("acme: cannot register a nil client or user") - } - log.Infof("acme: Registering account for %s", c.user.GetEmail()) - - accMsg := accountMessage{} - if c.user.GetEmail() != "" { - accMsg.Contact = []string{"mailto:" + c.user.GetEmail()} - } else { - accMsg.Contact = []string{} - } - accMsg.TermsOfServiceAgreed = tosAgreed - - var serverReg accountMessage - hdr, err := postJSON(c.jws, c.directory.NewAccountURL, accMsg, &serverReg) - if err != nil { - remoteErr, ok := err.(RemoteError) - if ok && remoteErr.StatusCode == 409 { - } else { - return nil, err - } - } - - reg := &RegistrationResource{ - URI: hdr.Get("Location"), - Body: serverReg, - } - c.jws.kid = reg.URI - - return reg, nil -} - -// RegisterWithExternalAccountBinding Register the current account to the ACME server. -func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string, hmacEncoded string) (*RegistrationResource, error) { - if c == nil || c.user == nil { - return nil, errors.New("acme: cannot register a nil client or user") - } - log.Infof("acme: Registering account (EAB) for %s", c.user.GetEmail()) - - accMsg := accountMessage{} - if c.user.GetEmail() != "" { - accMsg.Contact = []string{"mailto:" + c.user.GetEmail()} - } else { - accMsg.Contact = []string{} - } - accMsg.TermsOfServiceAgreed = tosAgreed - - hmac, err := base64.RawURLEncoding.DecodeString(hmacEncoded) - if err != nil { - return nil, fmt.Errorf("acme: could not decode hmac key: %s", err.Error()) - } - - eabJWS, err := c.jws.signEABContent(c.directory.NewAccountURL, kid, hmac) - if err != nil { - return nil, fmt.Errorf("acme: error signing eab content: %s", err.Error()) - } - - eabPayload := eabJWS.FullSerialize() - - accMsg.ExternalAccountBinding = []byte(eabPayload) - - var serverReg accountMessage - hdr, err := postJSON(c.jws, c.directory.NewAccountURL, accMsg, &serverReg) - if err != nil { - remoteErr, ok := err.(RemoteError) - if ok && remoteErr.StatusCode == 409 { - } else { - return nil, err - } - } - - reg := &RegistrationResource{ - URI: hdr.Get("Location"), - Body: serverReg, - } - c.jws.kid = reg.URI - - return reg, nil -} - -// ResolveAccountByKey will attempt to look up an account using the given account key -// and return its registration resource. -func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) { - log.Infof("acme: Trying to resolve account by key") - - acc := accountMessage{OnlyReturnExisting: true} - hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil) - if err != nil { - return nil, err - } - - accountLink := hdr.Get("Location") - if accountLink == "" { - return nil, errors.New("Server did not return the account link") - } - - var retAccount accountMessage - c.jws.kid = accountLink - _, err = postJSON(c.jws, accountLink, accountMessage{}, &retAccount) - if err != nil { - return nil, err - } - - return &RegistrationResource{URI: accountLink, Body: retAccount}, 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") - } - log.Infof("acme: Deleting account for %s", c.user.GetEmail()) - - accMsg := accountMessage{ - Status: "deactivated", - } - - _, err := postJSON(c.jws, c.user.GetRegistration().URI, accMsg, nil) - return err -} - -// 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 - log.Infof("acme: Querying account for %s", c.user.GetRegistration().URI) - - accMsg := accountMessage{} - - var serverReg accountMessage - _, err := postJSON(c.jws, c.user.GetRegistration().URI, accMsg, &serverReg) - if err != nil { - return nil, err - } - - reg := &RegistrationResource{Body: serverReg} - - // Location: header is not returned so this needs to be populated off of - // existing URI - reg.URI = c.user.GetRegistration().URI - - return reg, nil -} - -// ObtainCertificateForCSR tries to obtain a certificate matching the CSR passed into it. -// The domains are inferred from the CommonName and SubjectAltNames, if any. The private key -// for this CSR is not required. -// 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) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (*CertificateResource, error) { - // figure out what domains it concerns - // start with the common name - domains := []string{csr.Subject.CommonName} - - // loop over the SubjectAltName DNS names -DNSNames: - for _, sanName := range csr.DNSNames { - for _, existingName := range domains { - if existingName == sanName { - // duplicate; skip this name - continue DNSNames - } - } - - // name is unique - domains = append(domains, sanName) - } - - if bundle { - log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) - } else { - log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) - } - - order, err := c.createOrderForIdentifiers(domains) - if err != nil { - return nil, err - } - authz, err := c.getAuthzForOrder(order) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - /*for _, auth := range authz { - c.disableAuthz(auth) - }*/ - return nil, err - } - - err = c.solveChallengeForAuthz(authz) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - return nil, err - } - - log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - - failures := make(ObtainError) - cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil) - if err != nil { - for _, chln := range authz { - failures[chln.Identifier.Value] = err - } - } - - if cert != nil { - // Add the CSR to the certificate so that it can be used for renewals. - cert.CSR = pemEncode(&csr) - } - - // do not return an empty failures map, because - // it would still be a non-nil error value - if len(failures) > 0 { - return cert, failures - } - return cert, nil -} - -// 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, mustStaple bool) (*CertificateResource, error) { - if len(domains) == 0 { - return nil, errors.New("no domains to obtain a certificate for") - } - - if bundle { - log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) - } else { - log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) - } - - order, err := c.createOrderForIdentifiers(domains) - if err != nil { - return nil, err - } - authz, err := c.getAuthzForOrder(order) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - /*for _, auth := range authz { - c.disableAuthz(auth) - }*/ - return nil, err - } - - err = c.solveChallengeForAuthz(authz) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - return nil, err - } - - log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - - failures := make(ObtainError) - cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple) - if err != nil { - for _, auth := range authz { - failures[auth.Identifier.Value] = err - } - } - - // do not return an empty failures map, because - // it would still be a non-nil error value - if len(failures) > 0 { - return cert, failures - } - return cert, nil -} - -// 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{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, mustStaple 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 nil, err - } - - x509Cert := certificates[0] - if x509Cert.IsCA { - return nil, 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()) - log.Infof("[%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours())) - - // We always need to request a new certificate to renew. - // Start by checking to see if the certificate was based off a CSR, and - // use that if it's defined. - if len(cert.CSR) > 0 { - csr, errP := pemDecodeTox509CSR(cert.CSR) - if errP != nil { - return nil, errP - } - newCert, failures := c.ObtainCertificateForCSR(*csr, bundle) - return newCert, failures - } - - var privKey crypto.PrivateKey - if cert.PrivateKey != nil { - privKey, err = parsePEMPrivateKey(cert.PrivateKey) - if err != nil { - return nil, err - } - } - - var domains []string - // 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, err := c.ObtainCertificate(domains, bundle, privKey, mustStaple) - return newCert, err -} - -func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, error) { - var identifiers []identifier - for _, domain := range domains { - identifiers = append(identifiers, identifier{Type: "dns", Value: domain}) - } - - order := orderMessage{ - Identifiers: identifiers, - } - - var response orderMessage - hdr, err := postJSON(c.jws, c.directory.NewOrderURL, order, &response) - if err != nil { - return orderResource{}, err - } - - orderRes := orderResource{ - URL: hdr.Get("Location"), - Domains: domains, - orderMessage: response, - } - return orderRes, nil -} - -// an authz with the solver we have chosen and the index of the challenge associated with it -type selectedAuthSolver struct { - authz authorization - challengeIndex int - solver solver -} - -// Looks through the challenge combinations to find a solvable match. -// Then solves the challenges in series and returns. -func (c *Client) solveChallengeForAuthz(authorizations []authorization) error { - failures := make(ObtainError) - - authSolvers := []*selectedAuthSolver{} - - // loop through the resources, basically through the domains. First pass just selects a solver for each authz. - for _, authz := range authorizations { - if authz.Status == statusValid { - // Boulder might recycle recent validated authz (see issue #267) - log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value) - continue - } - if i, solvr := c.chooseSolver(authz, authz.Identifier.Value); solvr != nil { - authSolvers = append(authSolvers, &selectedAuthSolver{ - authz: authz, - challengeIndex: i, - solver: solvr, - }) - } else { - failures[authz.Identifier.Value] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Identifier.Value) - } - } - - // for all valid presolvers, first submit the challenges so they have max time to propagate - for _, item := range authSolvers { - authz := item.authz - i := item.challengeIndex - if presolver, ok := item.solver.(preSolver); ok { - if err := presolver.PreSolve(authz.Challenges[i], authz.Identifier.Value); err != nil { - failures[authz.Identifier.Value] = err - } - } - } - - defer func() { - // clean all created TXT records - for _, item := range authSolvers { - if clean, ok := item.solver.(cleanup); ok { - if failures[item.authz.Identifier.Value] != nil { - // already failed in previous loop - continue - } - err := clean.CleanUp(item.authz.Challenges[item.challengeIndex], item.authz.Identifier.Value) - if err != nil { - log.Warnf("Error cleaning up %s: %v ", item.authz.Identifier.Value, err) - } - } - } - }() - - // finally solve all challenges for real - for _, item := range authSolvers { - authz := item.authz - i := item.challengeIndex - if failures[authz.Identifier.Value] != nil { - // already failed in previous loop - continue - } - if err := item.solver.Solve(authz.Challenges[i], authz.Identifier.Value); err != nil { - failures[authz.Identifier.Value] = err - } - } - - // be careful not to return an empty failures map, for - // even an empty ObtainError is a non-nil error value - if len(failures) > 0 { - return failures - } - return nil -} - -// Checks all challenges from the server in order and returns the first matching solver. -func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) { - for i, challenge := range auth.Challenges { - if solver, ok := c.solvers[Challenge(challenge.Type)]; ok { - return i, solver - } - log.Infof("[%s] acme: Could not find solver for: %s", domain, challenge.Type) - } - return 0, nil -} - -// Get the challenges needed to proof our identifier to the ACME server. -func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error) { - resc, errc := make(chan authorization), make(chan domainError) - - delay := time.Second / overallRequestLimit - - for _, authzURL := range order.Authorizations { - time.Sleep(delay) - - go func(authzURL string) { - var authz authorization - _, err := postAsGet(c.jws, authzURL, &authz) - if err != nil { - errc <- domainError{Domain: authz.Identifier.Value, Error: err} - return - } - - resc <- authz - }(authzURL) - } - - var responses []authorization - failures := make(ObtainError) - for i := 0; i < len(order.Authorizations); i++ { - select { - case res := <-resc: - responses = append(responses, res) - case err := <-errc: - failures[err.Domain] = err.Error - } - } - - logAuthz(order) - - close(resc) - close(errc) - - // be careful to not return an empty failures map; - // even if empty, they become non-nil error values - if len(failures) > 0 { - return responses, failures - } - return responses, nil -} - -func logAuthz(order orderResource) { - for i, auth := range order.Authorizations { - log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth) - } -} - -// cleanAuthz loops through the passed in slice and disables any auths which are not "valid" -func (c *Client) disableAuthz(authURL string) error { - var disabledAuth authorization - _, err := postJSON(c.jws, authURL, deactivateAuthMessage{Status: "deactivated"}, &disabledAuth) - return err -} - -func (c *Client) requestCertificateForOrder(order orderResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (*CertificateResource, error) { - - var err error - if privKey == nil { - privKey, err = generatePrivateKey(c.keyType) - if err != nil { - return nil, err - } - } - - // determine certificate name(s) based on the authorization resources - commonName := order.Domains[0] - - // ACME draft Section 7.4 "Applying for Certificate Issuance" - // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4 - // says: - // Clients SHOULD NOT make any assumptions about the sort order of - // "identifiers" or "authorizations" elements in the returned order - // object. - san := []string{commonName} - for _, auth := range order.Identifiers { - if auth.Value != commonName { - san = append(san, auth.Value) - } - } - - // TODO: should the CSR be customizable? - csr, err := generateCsr(privKey, commonName, san, mustStaple) - if err != nil { - return nil, err - } - - return c.requestCertificateForCsr(order, bundle, csr, pemEncode(privKey)) -} - -func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr []byte, privateKeyPem []byte) (*CertificateResource, error) { - commonName := order.Domains[0] - - csrString := base64.RawURLEncoding.EncodeToString(csr) - var retOrder orderMessage - _, err := postJSON(c.jws, order.Finalize, csrMessage{Csr: csrString}, &retOrder) - if err != nil { - return nil, err - } - - if retOrder.Status == statusInvalid { - return nil, err - } - - certRes := CertificateResource{ - Domain: commonName, - CertURL: retOrder.Certificate, - PrivateKey: privateKeyPem, - } - - if retOrder.Status == statusValid { - // if the certificate is available right away, short cut! - ok, err := c.checkCertResponse(retOrder, &certRes, bundle) - if err != nil { - return nil, err - } - - if ok { - return &certRes, nil - } - } - - stopTimer := time.NewTimer(30 * time.Second) - defer stopTimer.Stop() - retryTick := time.NewTicker(500 * time.Millisecond) - defer retryTick.Stop() - - for { - select { - case <-stopTimer.C: - return nil, errors.New("certificate polling timed out") - case <-retryTick.C: - _, err := postAsGet(c.jws, order.URL, &retOrder) - if err != nil { - return nil, err - } - - done, err := c.checkCertResponse(retOrder, &certRes, bundle) - if err != nil { - return nil, err - } - if done { - return &certRes, nil - } - } - } -} - -// checkCertResponse checks to see if the certificate is ready and a link is contained in the -// response. if so, loads it into certRes and returns true. If the cert -// is not yet ready, it returns false. The certRes input -// should already have the Domain (common name) field populated. If bundle is -// true, the certificate will be bundled with the issuer's cert. -func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResource, bundle bool) (bool, error) { - switch order.Status { - case statusValid: - resp, err := postAsGet(c.jws, order.Certificate, nil) - if err != nil { - return false, err - } - - cert, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize)) - if err != nil { - return false, err - } - - // The issuer certificate link may be supplied via an "up" link - // in the response headers of a new certificate. See - // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4.2 - links := parseLinks(resp.Header["Link"]) - if link, ok := links["up"]; ok { - issuerCert, err := c.getIssuerCertificate(link) - - if err != nil { - // If we fail to acquire the issuer cert, return the issued certificate - do not fail. - log.Warnf("[%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err) - } else { - issuerCert = pemEncode(derCertificateBytes(issuerCert)) - - // If bundle is true, we want to return a certificate bundle. - // To do this, we append the issuer cert to the issued cert. - if bundle { - cert = append(cert, issuerCert...) - } - - certRes.IssuerCertificate = issuerCert - } - } else { - // Get issuerCert from bundled response from Let's Encrypt - // See https://community.letsencrypt.org/t/acme-v2-no-up-link-in-response/64962 - _, rest := pem.Decode(cert) - if rest != nil { - certRes.IssuerCertificate = rest - } - } - - certRes.Certificate = cert - certRes.CertURL = order.Certificate - certRes.CertStableURL = order.Certificate - log.Infof("[%s] Server responded with a certificate.", certRes.Domain) - return true, nil - - case "processing": - return false, nil - case statusInvalid: - return false, errors.New("order has invalid state: invalid") - default: - return false, nil - } -} - -// getIssuerCertificate requests the issuer certificate -func (c *Client) getIssuerCertificate(url string) ([]byte, error) { - log.Infof("acme: Requesting issuer cert from %s", url) - resp, err := postAsGet(c.jws, url, nil) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize)) - if err != nil { - return nil, err - } - - _, err = x509.ParseCertificate(issuerBytes) - if err != nil { - return nil, err - } - - 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, c challenge) error { - var chlng challenge - - // Challenge initiation is done by sending a JWS payload containing the - // trivial JSON object `{}`. We use an empty struct instance as the postJSON - // payload here to achieve this result. - hdr, err := postJSON(j, uri, struct{}{}, &chlng) - 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 chlng.Status { - case statusValid: - log.Infof("[%s] The server validated our request", domain) - return nil - case "pending": - case "processing": - case statusInvalid: - return handleChallengeError(chlng) - 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 = 5 - } - - time.Sleep(time.Duration(ra) * time.Second) - - resp, err := postAsGet(j, uri, &chlng) - if err != nil { - return err - } - if resp != nil { - hdr = resp.Header - } - } -} diff --git a/vendor/github.com/xenolf/lego/acme/commons.go b/vendor/github.com/xenolf/lego/acme/commons.go new file mode 100644 index 000000000..c4493696a --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/commons.go @@ -0,0 +1,284 @@ +// Package acme contains all objects related the ACME endpoints. +// https://tools.ietf.org/html/draft-ietf-acme-acme-16 +package acme + +import ( + "encoding/json" + "time" +) + +// Challenge statuses +// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.6 +const ( + StatusPending = "pending" + StatusInvalid = "invalid" + StatusValid = "valid" + StatusProcessing = "processing" + StatusDeactivated = "deactivated" + StatusExpired = "expired" + StatusRevoked = "revoked" +) + +// Directory the ACME directory object. +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.1 +type Directory struct { + NewNonceURL string `json:"newNonce"` + NewAccountURL string `json:"newAccount"` + NewOrderURL string `json:"newOrder"` + NewAuthzURL string `json:"newAuthz"` + RevokeCertURL string `json:"revokeCert"` + KeyChangeURL string `json:"keyChange"` + Meta Meta `json:"meta"` +} + +// Meta the ACME meta object (related to Directory). +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.1 +type Meta struct { + // termsOfService (optional, string): + // A URL identifying the current terms of service. + TermsOfService string `json:"termsOfService"` + + // website (optional, string): + // An HTTP or HTTPS URL locating a website providing more information about the ACME server. + Website string `json:"website"` + + // caaIdentities (optional, array of string): + // The hostnames that the ACME server recognizes as referring to itself + // for the purposes of CAA record validation as defined in [RFC6844]. + // Each string MUST represent the same sequence of ASCII code points + // that the server will expect to see as the "Issuer Domain Name" in a CAA issue or issuewild property tag. + // This allows clients to determine the correct issuer domain name to use when configuring CAA records. + CaaIdentities []string `json:"caaIdentities"` + + // externalAccountRequired (optional, boolean): + // If this field is present and set to "true", + // then the CA requires that all new- account requests include an "externalAccountBinding" field + // associating the new account with an external account. + ExternalAccountRequired bool `json:"externalAccountRequired"` +} + +// ExtendedAccount a extended Account. +type ExtendedAccount struct { + Account + // Contains the value of the response header `Location` + Location string `json:"-"` +} + +// Account the ACME account Object. +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.2 +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.3 +type Account struct { + // status (required, string): + // The status of this account. + // Possible values are: "valid", "deactivated", and "revoked". + // The value "deactivated" should be used to indicate client-initiated deactivation + // whereas "revoked" should be used to indicate server- initiated deactivation. (See Section 7.1.6) + Status string `json:"status,omitempty"` + + // contact (optional, array of string): + // An array of URLs that the server can use to contact the client for issues related to this account. + // For example, the server may wish to notify the client about server-initiated revocation or certificate expiration. + // For information on supported URL schemes, see Section 7.3 + Contact []string `json:"contact,omitempty"` + + // termsOfServiceAgreed (optional, boolean): + // Including this field in a new-account request, + // with a value of true, indicates the client's agreement with the terms of service. + // This field is not updateable by the client. + TermsOfServiceAgreed bool `json:"termsOfServiceAgreed,omitempty"` + + // orders (required, string): + // A URL from which a list of orders submitted by this account can be fetched via a POST-as-GET request, + // as described in Section 7.1.2.1. + Orders string `json:"orders,omitempty"` + + // onlyReturnExisting (optional, boolean): + // If this field is present with the value "true", + // then the server MUST NOT create a new account if one does not already exist. + // This allows a client to look up an account URL based on an account key (see Section 7.3.1). + OnlyReturnExisting bool `json:"onlyReturnExisting,omitempty"` + + // externalAccountBinding (optional, object): + // An optional field for binding the new account with an existing non-ACME account (see Section 7.3.4). + ExternalAccountBinding json.RawMessage `json:"externalAccountBinding,omitempty"` +} + +// ExtendedOrder a extended Order. +type ExtendedOrder struct { + Order + // The order URL, contains the value of the response header `Location` + Location string `json:"-"` +} + +// Order the ACME order Object. +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.3 +type Order struct { + // status (required, string): + // The status of this order. + // Possible values are: "pending", "ready", "processing", "valid", and "invalid". + Status string `json:"status,omitempty"` + + // expires (optional, string): + // The timestamp after which the server will consider this order invalid, + // encoded in the format specified in RFC 3339 [RFC3339]. + // This field is REQUIRED for objects with "pending" or "valid" in the status field. + Expires string `json:"expires,omitempty"` + + // identifiers (required, array of object): + // An array of identifier objects that the order pertains to. + Identifiers []Identifier `json:"identifiers"` + + // notBefore (optional, string): + // The requested value of the notBefore field in the certificate, + // in the date format defined in [RFC3339]. + NotBefore string `json:"notBefore,omitempty"` + + // notAfter (optional, string): + // The requested value of the notAfter field in the certificate, + // in the date format defined in [RFC3339]. + NotAfter string `json:"notAfter,omitempty"` + + // error (optional, object): + // The error that occurred while processing the order, if any. + // This field is structured as a problem document [RFC7807]. + Error *ProblemDetails `json:"error,omitempty"` + + // authorizations (required, array of string): + // For pending orders, + // the authorizations that the client needs to complete before the requested certificate can be issued (see Section 7.5), + // including unexpired authorizations that the client has completed in the past for identifiers specified in the order. + // The authorizations required are dictated by server policy + // and there may not be a 1:1 relationship between the order identifiers and the authorizations required. + // For final orders (in the "valid" or "invalid" state), the authorizations that were completed. + // Each entry is a URL from which an authorization can be fetched with a POST-as-GET request. + Authorizations []string `json:"authorizations,omitempty"` + + // finalize (required, string): + // A URL that a CSR must be POSTed to once all of the order's authorizations are satisfied to finalize the order. + // The result of a successful finalization will be the population of the certificate URL for the order. + Finalize string `json:"finalize,omitempty"` + + // certificate (optional, string): + // A URL for the certificate that has been issued in response to this order + Certificate string `json:"certificate,omitempty"` +} + +// Authorization the ACME authorization object. +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.4 +type Authorization struct { + // status (required, string): + // The status of this authorization. + // Possible values are: "pending", "valid", "invalid", "deactivated", "expired", and "revoked". + Status string `json:"status"` + + // expires (optional, string): + // The timestamp after which the server will consider this authorization invalid, + // encoded in the format specified in RFC 3339 [RFC3339]. + // This field is REQUIRED for objects with "valid" in the "status" field. + Expires time.Time `json:"expires,omitempty"` + + // identifier (required, object): + // The identifier that the account is authorized to represent + Identifier Identifier `json:"identifier,omitempty"` + + // challenges (required, array of objects): + // For pending authorizations, the challenges that the client can fulfill in order to prove possession of the identifier. + // For valid authorizations, the challenge that was validated. + // For invalid authorizations, the challenge that was attempted and failed. + // Each array entry is an object with parameters required to validate the challenge. + // A client should attempt to fulfill one of these challenges, + // and a server should consider any one of the challenges sufficient to make the authorization valid. + Challenges []Challenge `json:"challenges,omitempty"` + + // wildcard (optional, boolean): + // For authorizations created as a result of a newOrder request containing a DNS identifier + // with a value that contained a wildcard prefix this field MUST be present, and true. + Wildcard bool `json:"wildcard,omitempty"` +} + +// ExtendedChallenge a extended Challenge. +type ExtendedChallenge struct { + Challenge + // Contains the value of the response header `Retry-After` + RetryAfter string `json:"-"` + // Contains the value of the response header `Link` rel="up" + AuthorizationURL string `json:"-"` +} + +// Challenge the ACME challenge object. +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.5 +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8 +type Challenge struct { + // type (required, string): + // The type of challenge encoded in the object. + Type string `json:"type"` + + // url (required, string): + // The URL to which a response can be posted. + URL string `json:"url"` + + // status (required, string): + // The status of this challenge. Possible values are: "pending", "processing", "valid", and "invalid". + Status string `json:"status"` + + // validated (optional, string): + // The time at which the server validated this challenge, + // encoded in the format specified in RFC 3339 [RFC3339]. + // This field is REQUIRED if the "status" field is "valid". + Validated time.Time `json:"validated,omitempty"` + + // error (optional, object): + // Error that occurred while the server was validating the challenge, if any, + // structured as a problem document [RFC7807]. + // Multiple errors can be indicated by using subproblems Section 6.7.1. + // A challenge object with an error MUST have status equal to "invalid". + Error *ProblemDetails `json:"error,omitempty"` + + // token (required, string): + // A random value that uniquely identifies the challenge. + // This value MUST have at least 128 bits of entropy. + // It MUST NOT contain any characters outside the base64url alphabet, + // and MUST NOT include base64 padding characters ("="). + // See [RFC4086] for additional information on randomness requirements. + // https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.3 + // https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.4 + Token string `json:"token"` + + // https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.1 + KeyAuthorization string `json:"keyAuthorization"` +} + +// Identifier the ACME identifier object. +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-9.7.7 +type Identifier struct { + Type string `json:"type"` + Value string `json:"value"` +} + +// CSRMessage Certificate Signing Request +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.4 +type CSRMessage struct { + // csr (required, string): + // A CSR encoding the parameters for the certificate being requested [RFC2986]. + // The CSR is sent in the base64url-encoded version of the DER format. + // (Note: Because this field uses base64url, and does not include headers, it is different from PEM.). + Csr string `json:"csr"` +} + +// RevokeCertMessage a certificate revocation message +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.6 +// - https://tools.ietf.org/html/rfc5280#section-5.3.1 +type RevokeCertMessage struct { + // certificate (required, string): + // The certificate to be revoked, in the base64url-encoded version of the DER format. + // (Note: Because this field uses base64url, and does not include headers, it is different from PEM.) + Certificate string `json:"certificate"` + + // reason (optional, int): + // One of the revocation reasonCodes defined in Section 5.3.1 of [RFC5280] to be used when generating OCSP responses and CRLs. + // If this field is not set the server SHOULD omit the reasonCode CRL entry extension when generating OCSP responses and CRLs. + // The server MAY disallow a subset of reasonCodes from being used by the user. + // If a request contains a disallowed reasonCode the server MUST reject it with the error type "urn:ietf:params:acme:error:badRevocationReason". + // The problem document detail SHOULD indicate which reasonCodes are allowed. + Reason *uint `json:"reason,omitempty"` +} diff --git a/vendor/github.com/xenolf/lego/acme/crypto.go b/vendor/github.com/xenolf/lego/acme/crypto.go deleted file mode 100644 index 5ddeeaec2..000000000 --- a/vendor/github.com/xenolf/lego/acme/crypto.go +++ /dev/null @@ -1,334 +0,0 @@ -package acme - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/base64" - "encoding/pem" - "errors" - "fmt" - "io" - "io/ioutil" - "math/big" - "net/http" - "time" - - "golang.org/x/crypto/ocsp" - jose "gopkg.in/square/go-jose.v2" -) - -// 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 -) - -// Constants for OCSP must staple -var ( - tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24} - ocspMustStapleFeature = []byte{0x30, 0x03, 0x02, 0x01, 0x05} -) - -// 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, errC := httpGet(issuedCert.IssuingCertificateURL[0]) - if errC != nil { - return nil, nil, errC - } - defer resp.Body.Close() - - issuerBytes, errC := ioutil.ReadAll(limitReader(resp.Body, 1024*1024)) - if errC != nil { - return nil, nil, errC - } - - issuerCert, errC := x509.ParseCertificate(issuerBytes) - if errC != nil { - return nil, nil, errC - } - - // 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)) - if err != nil { - return nil, nil, err - } - - ocspRes, err := ocsp.ParseResponse(ocspResBytes, 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 := &jose.JSONWebKey{Key: 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.RawURLEncoding.EncodeToString(thumbBytes) - - 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, mustStaple bool) ([]byte, error) { - template := x509.CertificateRequest{ - Subject: pkix.Name{CommonName: domain}, - } - - if len(san) > 0 { - template.DNSNames = san - } - - if mustStaple { - template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{ - Id: tlsFeatureExtensionOID, - Value: ocspMustStapleFeature, - }) - } - - 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)} - case *x509.CertificateRequest: - pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw} - 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 pemDecodeTox509CSR(pem []byte) (*x509.CertificateRequest, error) { - pemBlock, err := pemDecode(pem) - if pemBlock == nil { - return nil, err - } - - if pemBlock.Type != "CERTIFICATE REQUEST" { - return nil, fmt.Errorf("PEM block is not a certificate request") - } - - return x509.ParseCertificateRequest(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, extensions []pkix.Extension) ([]byte, error) { - derBytes, err := generateDerCert(privKey, time.Time{}, domain, extensions) - 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, extensions []pkix.Extension) ([]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}, - ExtraExtensions: extensions, - } - - 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) -} diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go deleted file mode 100644 index d9c252e7e..000000000 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go +++ /dev/null @@ -1,343 +0,0 @@ -package acme - -import ( - "crypto/sha256" - "encoding/base64" - "errors" - "fmt" - "net" - "strings" - "sync" - "time" - - "github.com/miekg/dns" - "github.com/xenolf/lego/log" -) - -type preCheckDNSFunc func(fqdn, value string) (bool, error) - -var ( - // PreCheckDNS checks DNS propagation before notifying ACME that - // the DNS challenge is ready. - PreCheckDNS preCheckDNSFunc = checkDNSPropagation - fqdnToZone = map[string]string{} - muFqdnToZone sync.Mutex -) - -const defaultResolvConf = "/etc/resolv.conf" - -const ( - // DefaultPropagationTimeout default propagation timeout - DefaultPropagationTimeout = 60 * time.Second - - // DefaultPollingInterval default polling interval - DefaultPollingInterval = 2 * time.Second - - // DefaultTTL default TTL - DefaultTTL = 120 -) - -var defaultNameservers = []string{ - "google-public-dns-a.google.com:53", - "google-public-dns-b.google.com:53", -} - -// RecursiveNameservers are used to pre-check DNS propagation -var RecursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers) - -// DNSTimeout is used to override the default DNS timeout of 10 seconds. -var DNSTimeout = 10 * time.Second - -// getNameservers attempts to get systems nameservers before falling back to the defaults -func getNameservers(path string, defaults []string) []string { - config, err := dns.ClientConfigFromFile(path) - if err != nil || len(config.Servers) == 0 { - return defaults - } - - systemNameservers := []string{} - for _, server := range config.Servers { - // ensure all servers have a port number - if _, _, err := net.SplitHostPort(server); err != nil { - systemNameservers = append(systemNameservers, net.JoinHostPort(server, "53")) - } else { - systemNameservers = append(systemNameservers, server) - } - } - return systemNameservers -} - -// 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 - value = base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size]) - ttl = DefaultTTL - 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 -} - -// PreSolve just submits the txt record to the dns provider. It does not validate record propagation, or -// do anything at all with the acme server. -func (s *dnsChallenge) PreSolve(chlng challenge, domain string) error { - log.Infof("[%s] acme: Preparing 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) - } - - return nil -} - -func (s *dnsChallenge) Solve(chlng challenge, domain string) error { - log.Infof("[%s] acme: Trying to solve DNS-01", domain) - - // Generate the Key Authorization for the challenge - keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey) - if err != nil { - return err - } - - fqdn, value, _ := DNS01Record(domain, keyAuth) - - log.Infof("[%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers) - - var timeout, interval time.Duration - switch provider := s.provider.(type) { - case ChallengeProviderTimeout: - timeout, interval = provider.Timeout() - default: - timeout, interval = DefaultPropagationTimeout, DefaultPollingInterval - } - - err = WaitFor(timeout, interval, func() (bool, error) { - return PreCheckDNS(fqdn, value) - }) - if err != nil { - return err - } - - return s.validate(s.jws, domain, chlng.URL, challenge{Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth}) -} - -// CleanUp cleans the challenge -func (s *dnsChallenge) CleanUp(chlng challenge, domain string) error { - keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey) - if err != nil { - return err - } - return s.provider.CleanUp(domain, chlng.Token, 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 [fqdn: %s]", ns, fqdn) - } - } - - 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 succeeds, 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, fmt.Errorf("could not determine the zone: %v", 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 apex for the given fqdn by recursing up the -// domain labels until the nameserver returns a SOA record in the answer section. -func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) { - muFqdnToZone.Lock() - defer muFqdnToZone.Unlock() - - // Do we have it cached? - if zone, ok := fqdnToZone[fqdn]; ok { - return zone, nil - } - - labelIndexes := dns.Split(fqdn) - for _, index := range labelIndexes { - domain := fqdn[index:] - - in, err := dnsQuery(domain, dns.TypeSOA, nameservers, true) - if err != nil { - return "", err - } - - // Any response code other than NOERROR and NXDOMAIN is treated as error - if in.Rcode != dns.RcodeNameError && in.Rcode != dns.RcodeSuccess { - return "", fmt.Errorf("unexpected response code '%s' for %s", - dns.RcodeToString[in.Rcode], domain) - } - - // Check if we got a SOA RR in the answer section - if in.Rcode == dns.RcodeSuccess { - - // CNAME records cannot/should not exist at the root of a zone. - // So we skip a domain when a CNAME is found. - if dnsMsgContainsCNAME(in) { - continue - } - - for _, ans := range in.Answer { - if soa, ok := ans.(*dns.SOA); ok { - zone := soa.Hdr.Name - fqdnToZone[fqdn] = zone - return zone, nil - } - } - } - } - - return "", fmt.Errorf("could not find the start of authority") -} - -// dnsMsgContainsCNAME checks for a CNAME answer in msg -func dnsMsgContainsCNAME(msg *dns.Msg) bool { - for _, ans := range msg.Answer { - if _, ok := ans.(*dns.CNAME); ok { - return true - } - } - return false -} - -// 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 -} diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go b/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go deleted file mode 100644 index ca94fcac7..000000000 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go +++ /dev/null @@ -1,55 +0,0 @@ -package acme - -import ( - "bufio" - "fmt" - "os" - - "github.com/xenolf/lego/log" -) - -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 - } - - log.Infof("acme: Please create the following TXT record in your %s zone:", authZone) - log.Infof("acme: %s", dnsRecord) - log.Infof("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 - } - - log.Infof("acme: You can now remove this TXT record from your %s zone:", authZone) - log.Infof("acme: %s", dnsRecord) - return nil -} diff --git a/vendor/github.com/xenolf/lego/acme/error.go b/vendor/github.com/xenolf/lego/acme/error.go deleted file mode 100644 index 78694debe..000000000 --- a/vendor/github.com/xenolf/lego/acme/error.go +++ /dev/null @@ -1,91 +0,0 @@ -package acme - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strings" -) - -const ( - tosAgreementError = "Terms of service have changed" - invalidNonceError = "urn:ietf:params:acme:error:badNonce" -) - -// 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 -} - -// NonceError represents the error which is returned if the -// nonce sent by the client was not accepted by the server. -type NonceError struct { - RemoteError -} - -type domainError struct { - Domain string - Error error -} - -// ObtainError is returned when there are specific errors available -// per domain. For example in ObtainCertificate -type ObtainError map[string]error - -func (e ObtainError) Error() string { - buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n") - for dom, err := range e { - buffer.WriteString(fmt.Sprintf("[%s] %s\n", dom, err)) - } - return buffer.String() -} - -func handleHTTPError(resp *http.Response) error { - var errorDetail RemoteError - - contentType := resp.Header.Get("Content-Type") - if contentType == "application/json" || strings.HasPrefix(contentType, "application/problem+json") { - err := json.NewDecoder(resp.Body).Decode(&errorDetail) - if err != nil { - return err - } - } else { - detailBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize)) - 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} - } - - if errorDetail.StatusCode == http.StatusBadRequest && errorDetail.Type == invalidNonceError { - return NonceError{errorDetail} - } - - return errorDetail -} - -func handleChallengeError(chlng challenge) error { - return chlng.Error -} diff --git a/vendor/github.com/xenolf/lego/acme/errors.go b/vendor/github.com/xenolf/lego/acme/errors.go new file mode 100644 index 000000000..1658fe8d1 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/errors.go @@ -0,0 +1,58 @@ +package acme + +import ( + "fmt" +) + +// Errors types +const ( + errNS = "urn:ietf:params:acme:error:" + BadNonceErr = errNS + "badNonce" +) + +// ProblemDetails the problem details object +// - https://tools.ietf.org/html/rfc7807#section-3.1 +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.3.3 +type ProblemDetails struct { + Type string `json:"type,omitempty"` + Detail string `json:"detail,omitempty"` + HTTPStatus int `json:"status,omitempty"` + Instance string `json:"instance,omitempty"` + SubProblems []SubProblem `json:"subproblems,omitempty"` + + // additional values to have a better error message (Not defined by the RFC) + Method string `json:"method,omitempty"` + URL string `json:"url,omitempty"` +} + +// SubProblem a "subproblems" +// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.7.1 +type SubProblem struct { + Type string `json:"type,omitempty"` + Detail string `json:"detail,omitempty"` + Identifier Identifier `json:"identifier,omitempty"` +} + +func (p ProblemDetails) Error() string { + msg := fmt.Sprintf("acme: error: %d", p.HTTPStatus) + if len(p.Method) != 0 || len(p.URL) != 0 { + msg += fmt.Sprintf(" :: %s :: %s", p.Method, p.URL) + } + msg += fmt.Sprintf(" :: %s :: %s", p.Type, p.Detail) + + for _, sub := range p.SubProblems { + msg += fmt.Sprintf(", problem: %q :: %s", sub.Type, sub.Detail) + } + + if len(p.Instance) == 0 { + msg += ", url: " + p.Instance + } + + return msg +} + +// NonceError represents the error which is returned +// if the nonce sent by the client was not accepted by the server. +type NonceError struct { + *ProblemDetails +} diff --git a/vendor/github.com/xenolf/lego/acme/http.go b/vendor/github.com/xenolf/lego/acme/http.go deleted file mode 100644 index 8343b3690..000000000 --- a/vendor/github.com/xenolf/lego/acme/http.go +++ /dev/null @@ -1,212 +0,0 @@ -package acme - -import ( - "crypto/tls" - "crypto/x509" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "os" - "runtime" - "strings" - "time" -) - -var ( - // UserAgent (if non-empty) will be tacked onto the User-Agent string in requests. - UserAgent string - - // HTTPClient is an HTTP client with a reasonable timeout value and - // potentially a custom *x509.CertPool based on the caCertificatesEnvVar - // environment variable (see the `initCertPool` function) - HTTPClient = http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).DialContext, - TLSHandshakeTimeout: 15 * time.Second, - ResponseHeaderTimeout: 15 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - TLSClientConfig: &tls.Config{ - ServerName: os.Getenv(caServerNameEnvVar), - RootCAs: initCertPool(), - }, - }, - } -) - -const ( - // ourUserAgent is the User-Agent of this underlying library package. - // NOTE: Update this with each tagged release. - ourUserAgent = "xenolf-acme/1.2.1" - - // ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package. - // values: detach|release - // NOTE: Update this with each tagged release. - ourUserAgentComment = "detach" - - // caCertificatesEnvVar is the environment variable name that can be used to - // specify the path to PEM encoded CA Certificates that can be used to - // authenticate an ACME server with a HTTPS certificate not issued by a CA in - // the system-wide trusted root list. - caCertificatesEnvVar = "LEGO_CA_CERTIFICATES" - - // caServerNameEnvVar is the environment variable name that can be used to - // specify the CA server name that can be used to - // authenticate an ACME server with a HTTPS certificate not issued by a CA in - // the system-wide trusted root list. - caServerNameEnvVar = "LEGO_CA_SERVER_NAME" -) - -// initCertPool creates a *x509.CertPool populated with the PEM certificates -// found in the filepath specified in the caCertificatesEnvVar OS environment -// variable. If the caCertificatesEnvVar is not set then initCertPool will -// return nil. If there is an error creating a *x509.CertPool from the provided -// caCertificatesEnvVar value then initCertPool will panic. -func initCertPool() *x509.CertPool { - if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" { - customCAs, err := ioutil.ReadFile(customCACertsPath) - if err != nil { - panic(fmt.Sprintf("error reading %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) - } - certPool := x509.NewCertPool() - if ok := certPool.AppendCertsFromPEM(customCAs); !ok { - panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) - } - return certPool - } - return nil -} - -// 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(http.MethodHead, url, nil) - if err != nil { - return nil, fmt.Errorf("failed to head %q: %v", url, err) - } - - req.Header.Set("User-Agent", userAgent()) - - resp, err = HTTPClient.Do(req) - if err != nil { - return resp, fmt.Errorf("failed to do head %q: %v", url, 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(http.MethodPost, url, body) - if err != nil { - return nil, fmt.Errorf("failed to post %q: %v", url, err) - } - req.Header.Set("Content-Type", bodyType) - req.Header.Set("User-Agent", userAgent()) - - return HTTPClient.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(http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("failed to get %q: %v", url, err) - } - req.Header.Set("User-Agent", userAgent()) - - return HTTPClient.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 json %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 := post(j, uri, jsonBytes, respBody) - if resp == nil { - return nil, err - } - - defer resp.Body.Close() - - return resp.Header, err -} - -func postAsGet(j *jws, uri string, respBody interface{}) (*http.Response, error) { - return post(j, uri, []byte{}, respBody) -} - -func post(j *jws, uri string, reqBody []byte, respBody interface{}) (*http.Response, error) { - resp, err := j.post(uri, reqBody) - if err != nil { - return nil, fmt.Errorf("failed to post JWS message. -> %v", err) - } - - if resp.StatusCode >= http.StatusBadRequest { - err = handleHTTPError(resp) - switch err.(type) { - case NonceError: - // Retry once if the nonce was invalidated - - retryResp, errP := j.post(uri, reqBody) - if errP != nil { - return nil, fmt.Errorf("failed to post JWS message. -> %v", errP) - } - - if retryResp.StatusCode >= http.StatusBadRequest { - return retryResp, handleHTTPError(retryResp) - } - - if respBody == nil { - return retryResp, nil - } - - return retryResp, json.NewDecoder(retryResp.Body).Decode(respBody) - default: - return resp, err - } - } - - if respBody == nil { - return resp, nil - } - - return resp, 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)", UserAgent, ourUserAgent, ourUserAgentComment, runtime.GOOS, runtime.GOARCH) - return strings.TrimSpace(ua) -} diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge.go b/vendor/github.com/xenolf/lego/acme/http_challenge.go deleted file mode 100644 index 77a8edd49..000000000 --- a/vendor/github.com/xenolf/lego/acme/http_challenge.go +++ /dev/null @@ -1,42 +0,0 @@ -package acme - -import ( - "fmt" - - "github.com/xenolf/lego/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 { - - log.Infof("[%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.Warnf("[%s] error cleaning up: %v", domain, err) - } - }() - - return s.validate(s.jws, domain, chlng.URL, challenge{Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth}) -} diff --git a/vendor/github.com/xenolf/lego/acme/jws.go b/vendor/github.com/xenolf/lego/acme/jws.go deleted file mode 100644 index bea762104..000000000 --- a/vendor/github.com/xenolf/lego/acme/jws.go +++ /dev/null @@ -1,167 +0,0 @@ -package acme - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "fmt" - "net/http" - "sync" - - "gopkg.in/square/go-jose.v2" -) - -type jws struct { - getNonceURL string - privKey crypto.PrivateKey - kid string - nonces nonceManager -} - -// Posts a JWS signed message to the specified URL. -// It does NOT close the response body, so the caller must -// do that if no error was returned. -func (j *jws) post(url string, content []byte) (*http.Response, error) { - signedContent, err := j.signContent(url, content) - if err != nil { - return nil, fmt.Errorf("failed to sign content -> %s", err.Error()) - } - - data := bytes.NewBuffer([]byte(signedContent.FullSerialize())) - resp, err := httpPost(url, "application/jose+json", data) - if err != nil { - return nil, fmt.Errorf("failed to HTTP POST to %s -> %s", url, err.Error()) - } - - nonce, nonceErr := getNonceFromResponse(resp) - if nonceErr == nil { - j.nonces.Push(nonce) - } - - return resp, nil -} - -func (j *jws) signContent(url string, 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 - } - } - - jsonKey := jose.JSONWebKey{ - Key: j.privKey, - KeyID: j.kid, - } - - signKey := jose.SigningKey{ - Algorithm: alg, - Key: jsonKey, - } - options := jose.SignerOptions{ - NonceSource: j, - ExtraHeaders: make(map[jose.HeaderKey]interface{}), - } - options.ExtraHeaders["url"] = url - if j.kid == "" { - options.EmbedJWK = true - } - - signer, err := jose.NewSigner(signKey, &options) - if err != nil { - return nil, fmt.Errorf("failed to create jose signer -> %s", err.Error()) - } - - signed, err := signer.Sign(content) - if err != nil { - return nil, fmt.Errorf("failed to sign content -> %s", err.Error()) - } - return signed, nil -} - -func (j *jws) signEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignature, error) { - jwk := jose.JSONWebKey{Key: j.privKey} - jwkJSON, err := jwk.Public().MarshalJSON() - if err != nil { - return nil, fmt.Errorf("acme: error encoding eab jwk key: %s", err.Error()) - } - - signer, err := jose.NewSigner( - jose.SigningKey{Algorithm: jose.HS256, Key: hmac}, - &jose.SignerOptions{ - EmbedJWK: false, - ExtraHeaders: map[jose.HeaderKey]interface{}{ - "kid": kid, - "url": url, - }, - }, - ) - if err != nil { - return nil, fmt.Errorf("failed to create External Account Binding jose signer -> %s", err.Error()) - } - - signed, err := signer.Sign(jwkJSON) - if err != nil { - return nil, fmt.Errorf("failed to External Account Binding sign content -> %s", err.Error()) - } - - return signed, nil -} - -func (j *jws) Nonce() (string, error) { - if nonce, ok := j.nonces.Pop(); ok { - return nonce, nil - } - - return getNonce(j.getNonceURL) -} - -type nonceManager struct { - nonces []string - sync.Mutex -} - -func (n *nonceManager) Pop() (string, bool) { - n.Lock() - defer n.Unlock() - - if len(n.nonces) == 0 { - return "", false - } - - nonce := n.nonces[len(n.nonces)-1] - n.nonces = n.nonces[:len(n.nonces)-1] - return nonce, true -} - -func (n *nonceManager) Push(nonce string) { - n.Lock() - defer n.Unlock() - n.nonces = append(n.nonces, nonce) -} - -func getNonce(url string) (string, error) { - resp, err := httpHead(url) - if err != nil { - return "", fmt.Errorf("failed to get nonce from HTTP HEAD -> %s", err.Error()) - } - - return getNonceFromResponse(resp) -} - -func getNonceFromResponse(resp *http.Response) (string, error) { - nonce := resp.Header.Get("Replay-Nonce") - if nonce == "" { - return "", fmt.Errorf("server did not respond with a proper nonce header") - } - - return nonce, nil -} diff --git a/vendor/github.com/xenolf/lego/acme/messages.go b/vendor/github.com/xenolf/lego/acme/messages.go deleted file mode 100644 index 6946cc15a..000000000 --- a/vendor/github.com/xenolf/lego/acme/messages.go +++ /dev/null @@ -1,103 +0,0 @@ -package acme - -import ( - "encoding/json" - "time" -) - -// RegistrationResource represents all important informations about a registration -// of which the client needs to keep track itself. -type RegistrationResource struct { - Body accountMessage `json:"body,omitempty"` - URI string `json:"uri,omitempty"` -} - -type directory struct { - NewNonceURL string `json:"newNonce"` - NewAccountURL string `json:"newAccount"` - NewOrderURL string `json:"newOrder"` - RevokeCertURL string `json:"revokeCert"` - KeyChangeURL string `json:"keyChange"` - Meta struct { - TermsOfService string `json:"termsOfService"` - Website string `json:"website"` - CaaIdentities []string `json:"caaIdentities"` - ExternalAccountRequired bool `json:"externalAccountRequired"` - } `json:"meta"` -} - -type accountMessage struct { - Status string `json:"status,omitempty"` - Contact []string `json:"contact,omitempty"` - TermsOfServiceAgreed bool `json:"termsOfServiceAgreed,omitempty"` - Orders string `json:"orders,omitempty"` - OnlyReturnExisting bool `json:"onlyReturnExisting,omitempty"` - ExternalAccountBinding json.RawMessage `json:"externalAccountBinding,omitempty"` -} - -type orderResource struct { - URL string `json:"url,omitempty"` - Domains []string `json:"domains,omitempty"` - orderMessage `json:"body,omitempty"` -} - -type orderMessage struct { - Status string `json:"status,omitempty"` - Expires string `json:"expires,omitempty"` - Identifiers []identifier `json:"identifiers"` - NotBefore string `json:"notBefore,omitempty"` - NotAfter string `json:"notAfter,omitempty"` - Authorizations []string `json:"authorizations,omitempty"` - Finalize string `json:"finalize,omitempty"` - Certificate string `json:"certificate,omitempty"` -} - -type authorization struct { - Status string `json:"status"` - Expires time.Time `json:"expires"` - Identifier identifier `json:"identifier"` - Challenges []challenge `json:"challenges"` -} - -type identifier struct { - Type string `json:"type"` - Value string `json:"value"` -} - -type challenge struct { - URL string `json:"url"` - Type string `json:"type"` - Status string `json:"status"` - Token string `json:"token"` - Validated time.Time `json:"validated"` - KeyAuthorization string `json:"keyAuthorization"` - Error RemoteError `json:"error"` -} - -type csrMessage struct { - Csr string `json:"csr"` -} - -type revokeCertMessage struct { - Certificate string `json:"certificate"` -} - -type deactivateAuthMessage struct { - Status string `jsom:"status"` -} - -// CertificateResource represents a CA issued certificate. -// PrivateKey, Certificate and IssuerCertificate are all -// 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:"-"` - IssuerCertificate []byte `json:"-"` - CSR []byte `json:"-"` -} diff --git a/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go b/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go deleted file mode 100644 index cc70c3502..000000000 --- a/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go +++ /dev/null @@ -1,104 +0,0 @@ -package acme - -import ( - "crypto/rsa" - "crypto/sha256" - "crypto/tls" - "crypto/x509/pkix" - "encoding/asn1" - "fmt" - - "github.com/xenolf/lego/log" -) - -// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension. -// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1 -var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} - -type tlsALPNChallenge struct { - jws *jws - validate validateFunc - provider ChallengeProvider -} - -// Solve manages the provider to validate and solve the challenge. -func (t *tlsALPNChallenge) Solve(chlng challenge, domain string) error { - log.Infof("[%s] acme: Trying to solve TLS-ALPN-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.Warnf("[%s] error cleaning up: %v", domain, err) - } - }() - - return t.validate(t.jws, domain, chlng.URL, challenge{Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth}) -} - -// TLSALPNChallengeBlocks returns PEM blocks (certPEMBlock, keyPEMBlock) with the acmeValidation-v1 extension -// and domain name for the `tls-alpn-01` challenge. -func TLSALPNChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) { - // Compute the SHA-256 digest of the key authorization. - zBytes := sha256.Sum256([]byte(keyAuth)) - - value, err := asn1.Marshal(zBytes[:sha256.Size]) - if err != nil { - return nil, nil, err - } - - // Add the keyAuth digest as the acmeValidation-v1 extension - // (marked as critical such that it won't be used by non-ACME software). - // Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-3 - extensions := []pkix.Extension{ - { - Id: idPeAcmeIdentifierV1, - Critical: true, - Value: value, - }, - } - - // Generate a new RSA key for the certificates. - tempPrivKey, err := generatePrivateKey(RSA2048) - if err != nil { - return nil, nil, err - } - - rsaPrivKey := tempPrivKey.(*rsa.PrivateKey) - - // Generate the PEM certificate using the provided private key, domain, and extra extensions. - tempCertPEM, err := generatePemCert(rsaPrivKey, domain, extensions) - if err != nil { - return nil, nil, err - } - - // Encode the private key into a PEM format. We'll need to use it to generate the x509 keypair. - rsaPrivPEM := pemEncode(rsaPrivKey) - - return tempCertPEM, rsaPrivPEM, nil -} - -// TLSALPNChallengeCert returns a certificate with the acmeValidation-v1 extension -// and domain name for the `tls-alpn-01` challenge. -func TLSALPNChallengeCert(domain, keyAuth string) (*tls.Certificate, error) { - tempCertPEM, rsaPrivPEM, err := TLSALPNChallengeBlocks(domain, keyAuth) - if err != nil { - return nil, err - } - - certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM) - if err != nil { - return nil, err - } - - return &certificate, nil -} diff --git a/vendor/github.com/xenolf/lego/certcrypto/crypto.go b/vendor/github.com/xenolf/lego/certcrypto/crypto.go new file mode 100644 index 000000000..bb99d7d29 --- /dev/null +++ b/vendor/github.com/xenolf/lego/certcrypto/crypto.go @@ -0,0 +1,252 @@ +package certcrypto + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "errors" + "fmt" + "math/big" + "time" + + "golang.org/x/crypto/ocsp" +) + +// 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 +) + +// Constants for OCSP must staple +var ( + tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24} + ocspMustStapleFeature = []byte{0x30, 0x03, 0x02, 0x01, 0x05} +) + +// KeyType represents the key algo as well as the key size or curve to use. +type KeyType string + +type DERCertificateBytes []byte + +// 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, mustStaple bool) ([]byte, error) { + template := x509.CertificateRequest{ + Subject: pkix.Name{CommonName: domain}, + DNSNames: san, + } + + if mustStaple { + template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{ + Id: tlsFeatureExtensionOID, + Value: ocspMustStapleFeature, + }) + } + + 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)} + case *x509.CertificateRequest: + pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw} + 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 PemDecodeTox509CSR(pem []byte) (*x509.CertificateRequest, error) { + pemBlock, err := pemDecode(pem) + if pemBlock == nil { + return nil, err + } + + if pemBlock.Type != "CERTIFICATE REQUEST" { + return nil, fmt.Errorf("PEM block is not a certificate request") + } + + return x509.ParseCertificateRequest(pemBlock.Bytes) +} + +// ParsePEMCertificate returns Certificate from a PEM encoded certificate. +// The certificate has to be PEM encoded. Any other encodings like DER will fail. +func ParsePEMCertificate(cert []byte) (*x509.Certificate, error) { + pemBlock, err := pemDecode(cert) + if pemBlock == nil { + return nil, err + } + + // from a DER encoded certificate + return x509.ParseCertificate(pemBlock.Bytes) +} + +func ExtractDomains(cert *x509.Certificate) []string { + domains := []string{cert.Subject.CommonName} + + // Check for SAN certificate + for _, sanDomain := range cert.DNSNames { + if sanDomain == cert.Subject.CommonName { + continue + } + domains = append(domains, sanDomain) + } + + return domains +} + +func ExtractDomainsCSR(csr *x509.CertificateRequest) []string { + domains := []string{csr.Subject.CommonName} + + // loop over the SubjectAltName DNS names + for _, sanName := range csr.DNSNames { + if containsSAN(domains, sanName) { + // Duplicate; skip this name + continue + } + + // Name is unique + domains = append(domains, sanName) + } + + return domains +} + +func containsSAN(domains []string, sanName string) bool { + for _, existingName := range domains { + if existingName == sanName { + return true + } + } + return false +} + +func GeneratePemCert(privateKey *rsa.PrivateKey, domain string, extensions []pkix.Extension) ([]byte, error) { + derBytes, err := generateDerCert(privateKey, time.Time{}, domain, extensions) + if err != nil { + return nil, err + } + + return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil +} + +func generateDerCert(privateKey *rsa.PrivateKey, expiration time.Time, domain string, extensions []pkix.Extension) ([]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}, + ExtraExtensions: extensions, + } + + return x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) +} diff --git a/vendor/github.com/xenolf/lego/certificate/authorization.go b/vendor/github.com/xenolf/lego/certificate/authorization.go new file mode 100644 index 000000000..c35de109e --- /dev/null +++ b/vendor/github.com/xenolf/lego/certificate/authorization.go @@ -0,0 +1,69 @@ +package certificate + +import ( + "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/log" +) + +const ( + // overallRequestLimit is the overall number of request per second + // limited on the "new-reg", "new-authz" and "new-cert" endpoints. + // From the documentation the limitation is 20 requests per second, + // but using 20 as value doesn't work but 18 do + overallRequestLimit = 18 +) + +func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authorization, error) { + resc, errc := make(chan acme.Authorization), make(chan domainError) + + delay := time.Second / overallRequestLimit + + for _, authzURL := range order.Authorizations { + time.Sleep(delay) + + go func(authzURL string) { + authz, err := c.core.Authorizations.Get(authzURL) + if err != nil { + errc <- domainError{Domain: authz.Identifier.Value, Error: err} + return + } + + resc <- authz + }(authzURL) + } + + var responses []acme.Authorization + failures := make(obtainError) + for i := 0; i < len(order.Authorizations); i++ { + select { + case res := <-resc: + responses = append(responses, res) + case err := <-errc: + failures[err.Domain] = err.Error + } + } + + for i, auth := range order.Authorizations { + log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth) + } + + close(resc) + close(errc) + + // be careful to not return an empty failures map; + // even if empty, they become non-nil error values + if len(failures) > 0 { + return responses, failures + } + return responses, nil +} + +func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) { + for _, auth := range order.Authorizations { + if err := c.core.Authorizations.Deactivate(auth); err != nil { + log.Infof("Unable to deactivated authorizations: %s", auth) + } + } +} diff --git a/vendor/github.com/xenolf/lego/certificate/certificates.go b/vendor/github.com/xenolf/lego/certificate/certificates.go new file mode 100644 index 000000000..e9c041979 --- /dev/null +++ b/vendor/github.com/xenolf/lego/certificate/certificates.go @@ -0,0 +1,493 @@ +package certificate + +import ( + "bytes" + "crypto" + "crypto/x509" + "encoding/base64" + "errors" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/acme/api" + "github.com/xenolf/lego/certcrypto" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/log" + "golang.org/x/crypto/ocsp" + "golang.org/x/net/idna" +) + +// maxBodySize is the maximum size of body that we will read. +const maxBodySize = 1024 * 1024 + +// Resource represents a CA issued certificate. +// PrivateKey, Certificate and IssuerCertificate are all +// 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 Resource struct { + Domain string `json:"domain"` + CertURL string `json:"certUrl"` + CertStableURL string `json:"certStableUrl"` + PrivateKey []byte `json:"-"` + Certificate []byte `json:"-"` + IssuerCertificate []byte `json:"-"` + CSR []byte `json:"-"` +} + +// ObtainRequest The request to obtain certificate. +// +// 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 the function Obtain. +// If you do not want that you can supply your own private key in the privateKey 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. +type ObtainRequest struct { + Domains []string + Bundle bool + PrivateKey crypto.PrivateKey + MustStaple bool +} + +type resolver interface { + Solve(authorizations []acme.Authorization) error +} + +type Certifier struct { + core *api.Core + keyType certcrypto.KeyType + resolver resolver +} + +func NewCertifier(core *api.Core, keyType certcrypto.KeyType, resolver resolver) *Certifier { + return &Certifier{ + core: core, + keyType: keyType, + resolver: resolver, + } +} + +// Obtain tries to obtain a single certificate using all domains passed into it. +// +// This function will never return a partial certificate. +// If one domain in the list fails, the whole certificate will fail. +func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) { + if len(request.Domains) == 0 { + return nil, errors.New("no domains to obtain a certificate for") + } + + domains := sanitizeDomain(request.Domains) + + if request.Bundle { + log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) + } else { + log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) + } + + order, err := c.core.Orders.New(domains) + if err != nil { + return nil, err + } + + authz, err := c.getAuthorizations(order) + if err != nil { + // If any challenge fails, return. Do not generate partial SAN certificates. + c.deactivateAuthorizations(order) + return nil, err + } + + err = c.resolver.Solve(authz) + if err != nil { + // If any challenge fails, return. Do not generate partial SAN certificates. + return nil, err + } + + log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) + + failures := make(obtainError) + cert, err := c.getForOrder(domains, order, request.Bundle, request.PrivateKey, request.MustStaple) + if err != nil { + for _, auth := range authz { + failures[challenge.GetTargetedDomain(auth)] = err + } + } + + // Do not return an empty failures map, because + // it would still be a non-nil error value + if len(failures) > 0 { + return cert, failures + } + return cert, nil +} + +// ObtainForCSR tries to obtain a certificate matching the CSR passed into it. +// +// The domains are inferred from the CommonName and SubjectAltNames, if any. +// The private key for this CSR is not required. +// +// 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 *Certifier) ObtainForCSR(csr x509.CertificateRequest, bundle bool) (*Resource, error) { + // figure out what domains it concerns + // start with the common name + domains := certcrypto.ExtractDomainsCSR(&csr) + + if bundle { + log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) + } else { + log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) + } + + order, err := c.core.Orders.New(domains) + if err != nil { + return nil, err + } + + authz, err := c.getAuthorizations(order) + if err != nil { + // If any challenge fails, return. Do not generate partial SAN certificates. + c.deactivateAuthorizations(order) + return nil, err + } + + err = c.resolver.Solve(authz) + if err != nil { + // If any challenge fails, return. Do not generate partial SAN certificates. + return nil, err + } + + log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) + + failures := make(obtainError) + cert, err := c.getForCSR(domains, order, bundle, csr.Raw, nil) + if err != nil { + for _, auth := range authz { + failures[challenge.GetTargetedDomain(auth)] = err + } + } + + if cert != nil { + // Add the CSR to the certificate so that it can be used for renewals. + cert.CSR = certcrypto.PEMEncode(&csr) + } + + // Do not return an empty failures map, + // because it would still be a non-nil error value + if len(failures) > 0 { + return cert, failures + } + return cert, nil +} + +func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bundle bool, privateKey crypto.PrivateKey, mustStaple bool) (*Resource, error) { + if privateKey == nil { + var err error + privateKey, err = certcrypto.GeneratePrivateKey(c.keyType) + if err != nil { + return nil, err + } + } + + // Determine certificate name(s) based on the authorization resources + commonName := domains[0] + + // ACME draft Section 7.4 "Applying for Certificate Issuance" + // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4 + // says: + // Clients SHOULD NOT make any assumptions about the sort order of + // "identifiers" or "authorizations" elements in the returned order + // object. + san := []string{commonName} + for _, auth := range order.Identifiers { + if auth.Value != commonName { + san = append(san, auth.Value) + } + } + + // TODO: should the CSR be customizable? + csr, err := certcrypto.GenerateCSR(privateKey, commonName, san, mustStaple) + if err != nil { + return nil, err + } + + return c.getForCSR(domains, order, bundle, csr, certcrypto.PEMEncode(privateKey)) +} + +func (c *Certifier) getForCSR(domains []string, order acme.ExtendedOrder, bundle bool, csr []byte, privateKeyPem []byte) (*Resource, error) { + respOrder, err := c.core.Orders.UpdateForCSR(order.Finalize, csr) + if err != nil { + return nil, err + } + + commonName := domains[0] + certRes := &Resource{ + Domain: commonName, + CertURL: respOrder.Certificate, + PrivateKey: privateKeyPem, + } + + if respOrder.Status == acme.StatusValid { + // if the certificate is available right away, short cut! + ok, err := c.checkResponse(respOrder, certRes, bundle) + if err != nil { + return nil, err + } + + if ok { + return certRes, nil + } + } + + return c.waitForCertificate(certRes, order.Location, bundle) +} + +func (c *Certifier) waitForCertificate(certRes *Resource, orderURL string, bundle bool) (*Resource, error) { + stopTimer := time.NewTimer(30 * time.Second) + defer stopTimer.Stop() + retryTick := time.NewTicker(500 * time.Millisecond) + defer retryTick.Stop() + + for { + select { + case <-stopTimer.C: + return nil, errors.New("certificate polling timed out") + case <-retryTick.C: + order, err := c.core.Orders.Get(orderURL) + if err != nil { + return nil, err + } + + done, err := c.checkResponse(order, certRes, bundle) + if err != nil { + return nil, err + } + if done { + return certRes, nil + } + } + } +} + +// checkResponse checks to see if the certificate is ready and a link is contained in the response. +// +// If so, loads it into certRes and returns true. +// If the cert is not yet ready, it returns false. +// +// The certRes input should already have the Domain (common name) field populated. +// +// If bundle is true, the certificate will be bundled with the issuer's cert. +func (c *Certifier) checkResponse(order acme.Order, certRes *Resource, bundle bool) (bool, error) { + valid, err := checkOrderStatus(order) + if err != nil || !valid { + return valid, err + } + + cert, issuer, err := c.core.Certificates.Get(order.Certificate, bundle) + if err != nil { + return false, err + } + + log.Infof("[%s] Server responded with a certificate.", certRes.Domain) + + certRes.IssuerCertificate = issuer + certRes.Certificate = cert + certRes.CertURL = order.Certificate + certRes.CertStableURL = order.Certificate + + return true, nil +} + +// Revoke takes a PEM encoded certificate or bundle and tries to revoke it at the CA. +func (c *Certifier) Revoke(cert []byte) error { + certificates, err := certcrypto.ParsePEMBundle(cert) + if err != nil { + return err + } + + x509Cert := certificates[0] + if x509Cert.IsCA { + return fmt.Errorf("certificate bundle starts with a CA certificate") + } + + revokeMsg := acme.RevokeCertMessage{ + Certificate: base64.RawURLEncoding.EncodeToString(x509Cert.Raw), + } + + return c.core.Certificates.Revoke(revokeMsg) +} + +// Renew takes a Resource 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 Resource should be non-nil. +func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool) (*Resource, 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 := certcrypto.ParsePEMBundle(certRes.Certificate) + if err != nil { + return nil, err + } + + x509Cert := certificates[0] + if x509Cert.IsCA { + return nil, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", certRes.Domain) + } + + // This is just meant to be informal for the user. + timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC()) + log.Infof("[%s] acme: Trying renewal with %d hours remaining", certRes.Domain, int(timeLeft.Hours())) + + // We always need to request a new certificate to renew. + // Start by checking to see if the certificate was based off a CSR, + // and use that if it's defined. + if len(certRes.CSR) > 0 { + csr, errP := certcrypto.PemDecodeTox509CSR(certRes.CSR) + if errP != nil { + return nil, errP + } + + return c.ObtainForCSR(*csr, bundle) + } + + var privateKey crypto.PrivateKey + if certRes.PrivateKey != nil { + privateKey, err = certcrypto.ParsePEMPrivateKey(certRes.PrivateKey) + if err != nil { + return nil, err + } + } + + query := ObtainRequest{ + Domains: certcrypto.ExtractDomains(x509Cert), + Bundle: bundle, + PrivateKey: privateKey, + MustStaple: mustStaple, + } + return c.Obtain(query) +} + +// GetOCSP 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 (c *Certifier) GetOCSP(bundle []byte) ([]byte, *ocsp.Response, error) { + certificates, err := certcrypto.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, errC := c.core.HTTPClient.Get(issuedCert.IssuingCertificateURL[0]) + if errC != nil { + return nil, nil, errC + } + defer resp.Body.Close() + + issuerBytes, errC := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize)) + if errC != nil { + return nil, nil, errC + } + + issuerCert, errC := x509.ParseCertificate(issuerBytes) + if errC != nil { + return nil, nil, errC + } + + // 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 + } + + resp, err := c.core.HTTPClient.Post(issuedCert.OCSPServer[0], "application/ocsp-request", bytes.NewReader(ocspReq)) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + ocspResBytes, err := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize)) + if err != nil { + return nil, nil, err + } + + ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert) + if err != nil { + return nil, nil, err + } + + return ocspResBytes, ocspRes, nil +} + +func checkOrderStatus(order acme.Order) (bool, error) { + switch order.Status { + case acme.StatusValid: + return true, nil + case acme.StatusInvalid: + return false, order.Error + default: + return false, nil + } +} + +// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.4 +// The domain name MUST be encoded +// in the form in which it would appear in a certificate. That is, it +// MUST be encoded according to the rules in Section 7 of [RFC5280]. +// +// https://tools.ietf.org/html/rfc5280#section-7 +func sanitizeDomain(domains []string) []string { + var sanitizedDomains []string + for _, domain := range domains { + sanitizedDomain, err := idna.ToASCII(domain) + if err != nil { + log.Infof("skip domain %q: unable to sanitize (punnycode): %v", domain, err) + } else { + sanitizedDomains = append(sanitizedDomains, sanitizedDomain) + } + } + return sanitizedDomains +} diff --git a/vendor/github.com/xenolf/lego/certificate/errors.go b/vendor/github.com/xenolf/lego/certificate/errors.go new file mode 100644 index 000000000..0fec7c16a --- /dev/null +++ b/vendor/github.com/xenolf/lego/certificate/errors.go @@ -0,0 +1,30 @@ +package certificate + +import ( + "bytes" + "fmt" + "sort" +) + +// obtainError is returned when there are specific errors available per domain. +type obtainError map[string]error + +func (e obtainError) Error() string { + buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n") + + var domains []string + for domain := range e { + domains = append(domains, domain) + } + sort.Strings(domains) + + for _, domain := range domains { + buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain])) + } + return buffer.String() +} + +type domainError struct { + Domain string + Error error +} diff --git a/vendor/github.com/xenolf/lego/challenge/challenges.go b/vendor/github.com/xenolf/lego/challenge/challenges.go new file mode 100644 index 000000000..e8c862ea1 --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/challenges.go @@ -0,0 +1,44 @@ +package challenge + +import ( + "fmt" + + "github.com/xenolf/lego/acme" +) + +// Type is a string that identifies a particular challenge type and version of ACME challenge. +type Type string + +const ( + // HTTP01 is the "http-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.3 + // Note: ChallengePath returns the URL path to fulfill this challenge + HTTP01 = Type("http-01") + + // DNS01 is the "dns-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.4 + // Note: GetRecord returns a DNS record which will fulfill this challenge + DNS01 = Type("dns-01") + + // TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05 + TLSALPN01 = Type("tls-alpn-01") +) + +func (t Type) String() string { + return string(t) +} + +func FindChallenge(chlgType Type, authz acme.Authorization) (acme.Challenge, error) { + for _, chlg := range authz.Challenges { + if chlg.Type == string(chlgType) { + return chlg, nil + } + } + + return acme.Challenge{}, fmt.Errorf("[%s] acme: unable to find challenge %s", GetTargetedDomain(authz), chlgType) +} + +func GetTargetedDomain(authz acme.Authorization) string { + if authz.Wildcard { + return "*." + authz.Identifier.Value + } + return authz.Identifier.Value +} diff --git a/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge.go b/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge.go new file mode 100644 index 000000000..c9ef2ee3d --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge.go @@ -0,0 +1,176 @@ +package dns01 + +import ( + "crypto/sha256" + "encoding/base64" + "fmt" + "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/acme/api" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/log" + "github.com/xenolf/lego/platform/wait" +) + +const ( + // DefaultPropagationTimeout default propagation timeout + DefaultPropagationTimeout = 60 * time.Second + + // DefaultPollingInterval default polling interval + DefaultPollingInterval = 2 * time.Second + + // DefaultTTL default TTL + DefaultTTL = 120 +) + +type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error + +type ChallengeOption func(*Challenge) error + +// CondOption Conditional challenge option. +func CondOption(condition bool, opt ChallengeOption) ChallengeOption { + if !condition { + // NoOp options + return func(*Challenge) error { + return nil + } + } + return opt +} + +// Challenge implements the dns-01 challenge +type Challenge struct { + core *api.Core + validate ValidateFunc + provider challenge.Provider + preCheck preCheck + dnsTimeout time.Duration +} + +func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider, opts ...ChallengeOption) *Challenge { + chlg := &Challenge{ + core: core, + validate: validate, + provider: provider, + preCheck: newPreCheck(), + dnsTimeout: 10 * time.Second, + } + + for _, opt := range opts { + err := opt(chlg) + if err != nil { + log.Infof("challenge option error: %v", err) + } + } + + return chlg +} + +// PreSolve just submits the txt record to the dns provider. +// It does not validate record propagation, or do anything at all with the acme server. +func (c *Challenge) PreSolve(authz acme.Authorization) error { + domain := challenge.GetTargetedDomain(authz) + log.Infof("[%s] acme: Preparing to solve DNS-01", domain) + + chlng, err := challenge.FindChallenge(challenge.DNS01, authz) + if err != nil { + return err + } + + if c.provider == nil { + return fmt.Errorf("[%s] acme: no DNS Provider configured", domain) + } + + // Generate the Key Authorization for the challenge + keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) + if err != nil { + return err + } + + err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth) + if err != nil { + return fmt.Errorf("[%s] acme: error presenting token: %s", domain, err) + } + + return nil +} + +func (c *Challenge) Solve(authz acme.Authorization) error { + domain := challenge.GetTargetedDomain(authz) + log.Infof("[%s] acme: Trying to solve DNS-01", domain) + + chlng, err := challenge.FindChallenge(challenge.DNS01, authz) + if err != nil { + return err + } + + // Generate the Key Authorization for the challenge + keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) + if err != nil { + return err + } + + fqdn, value := GetRecord(authz.Identifier.Value, keyAuth) + + var timeout, interval time.Duration + switch provider := c.provider.(type) { + case challenge.ProviderTimeout: + timeout, interval = provider.Timeout() + default: + timeout, interval = DefaultPropagationTimeout, DefaultPollingInterval + } + + log.Infof("[%s] acme: Checking DNS record propagation using %+v", domain, recursiveNameservers) + + err = wait.For("propagation", timeout, interval, func() (bool, error) { + stop, errP := c.preCheck.call(fqdn, value) + if !stop || errP != nil { + log.Infof("[%s] acme: Waiting for DNS record propagation.", domain) + } + return stop, errP + }) + if err != nil { + return err + } + + chlng.KeyAuthorization = keyAuth + return c.validate(c.core, authz.Identifier.Value, chlng) +} + +// CleanUp cleans the challenge. +func (c *Challenge) CleanUp(authz acme.Authorization) error { + log.Infof("[%s] acme: Cleaning DNS-01 challenge", challenge.GetTargetedDomain(authz)) + + chlng, err := challenge.FindChallenge(challenge.DNS01, authz) + if err != nil { + return err + } + + keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) + if err != nil { + return err + } + + return c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth) +} + +func (c *Challenge) Sequential() (bool, time.Duration) { + if p, ok := c.provider.(sequential); ok { + return ok, p.Sequential() + } + return false, 0 +} + +type sequential interface { + Sequential() time.Duration +} + +// GetRecord returns a DNS record which will fulfill the `dns-01` challenge +func GetRecord(domain, keyAuth string) (fqdn string, value string) { + keyAuthShaBytes := sha256.Sum256([]byte(keyAuth)) + // base64URL encoding without padding + value = base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size]) + fqdn = fmt.Sprintf("_acme-challenge.%s.", domain) + return +} diff --git a/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge_manual.go b/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge_manual.go new file mode 100644 index 000000000..490108dd1 --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge_manual.go @@ -0,0 +1,52 @@ +package dns01 + +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 := GetRecord(domain, keyAuth) + + authZone, err := FindZoneByFqdn(fqdn) + if err != nil { + return err + } + + fmt.Printf("lego: Please create the following TXT record in your %s zone:\n", authZone) + fmt.Printf(dnsTemplate+"\n", fqdn, DefaultTTL, value) + fmt.Printf("lego: Press 'Enter' when you are done\n") + + _, err = bufio.NewReader(os.Stdin).ReadBytes('\n') + + return err +} + +// CleanUp prints instructions for manually removing the TXT record +func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error { + fqdn, _ := GetRecord(domain, keyAuth) + + authZone, err := FindZoneByFqdn(fqdn) + if err != nil { + return err + } + + fmt.Printf("lego: You can now remove this TXT record from your %s zone:\n", authZone) + fmt.Printf(dnsTemplate+"\n", fqdn, DefaultTTL, "...") + + return nil +} diff --git a/vendor/github.com/xenolf/lego/challenge/dns01/fqdn.go b/vendor/github.com/xenolf/lego/challenge/dns01/fqdn.go new file mode 100644 index 000000000..c238c8cf5 --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/dns01/fqdn.go @@ -0,0 +1,19 @@ +package dns01 + +// 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 +} diff --git a/vendor/github.com/xenolf/lego/challenge/dns01/nameserver.go b/vendor/github.com/xenolf/lego/challenge/dns01/nameserver.go new file mode 100644 index 000000000..03f1a8d12 --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/dns01/nameserver.go @@ -0,0 +1,232 @@ +package dns01 + +import ( + "fmt" + "net" + "strings" + "sync" + "time" + + "github.com/miekg/dns" +) + +const defaultResolvConf = "/etc/resolv.conf" + +// dnsTimeout is used to override the default DNS timeout of 10 seconds. +var dnsTimeout = 10 * time.Second + +var ( + fqdnToZone = map[string]string{} + muFqdnToZone sync.Mutex +) + +var defaultNameservers = []string{ + "google-public-dns-a.google.com:53", + "google-public-dns-b.google.com:53", +} + +// recursiveNameservers are used to pre-check DNS propagation +var recursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers) + +// ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. +func ClearFqdnCache() { + muFqdnToZone.Lock() + fqdnToZone = map[string]string{} + muFqdnToZone.Unlock() +} + +func AddDNSTimeout(timeout time.Duration) ChallengeOption { + return func(_ *Challenge) error { + dnsTimeout = timeout + return nil + } +} + +func AddRecursiveNameservers(nameservers []string) ChallengeOption { + return func(_ *Challenge) error { + recursiveNameservers = ParseNameservers(nameservers) + return nil + } +} + +// getNameservers attempts to get systems nameservers before falling back to the defaults +func getNameservers(path string, defaults []string) []string { + config, err := dns.ClientConfigFromFile(path) + if err != nil || len(config.Servers) == 0 { + return defaults + } + + return ParseNameservers(config.Servers) +} + +func ParseNameservers(servers []string) []string { + var resolvers []string + for _, resolver := range servers { + // ensure all servers have a port number + if _, _, err := net.SplitHostPort(resolver); err != nil { + resolvers = append(resolvers, net.JoinHostPort(resolver, "53")) + } else { + resolvers = append(resolvers, resolver) + } + } + return resolvers +} + +// lookupNameservers returns the authoritative nameservers for the given fqdn. +func lookupNameservers(fqdn string) ([]string, error) { + var authoritativeNss []string + + zone, err := FindZoneByFqdn(fqdn) + if err != nil { + return nil, fmt.Errorf("could not determine the zone: %v", 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 apex for the given fqdn +// by recursing up the domain labels until the nameserver returns a SOA record in the answer section. +func FindZoneByFqdn(fqdn string) (string, error) { + return FindZoneByFqdnCustom(fqdn, recursiveNameservers) +} + +// FindZoneByFqdnCustom determines the zone apex for the given fqdn +// by recursing up the domain labels until the nameserver returns a SOA record in the answer section. +func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) { + muFqdnToZone.Lock() + defer muFqdnToZone.Unlock() + + // Do we have it cached? + if zone, ok := fqdnToZone[fqdn]; ok { + return zone, nil + } + + var err error + var in *dns.Msg + + labelIndexes := dns.Split(fqdn) + for _, index := range labelIndexes { + domain := fqdn[index:] + + in, err = dnsQuery(domain, dns.TypeSOA, nameservers, true) + if err != nil { + continue + } + + if in == nil { + continue + } + + switch in.Rcode { + case dns.RcodeSuccess: + // Check if we got a SOA RR in the answer section + + if len(in.Answer) == 0 { + continue + } + + // CNAME records cannot/should not exist at the root of a zone. + // So we skip a domain when a CNAME is found. + if dnsMsgContainsCNAME(in) { + continue + } + + for _, ans := range in.Answer { + if soa, ok := ans.(*dns.SOA); ok { + zone := soa.Hdr.Name + fqdnToZone[fqdn] = zone + return zone, nil + } + } + case dns.RcodeNameError: + // NXDOMAIN + default: + // Any response code other than NOERROR and NXDOMAIN is treated as error + return "", fmt.Errorf("unexpected response code '%s' for %s", dns.RcodeToString[in.Rcode], domain) + } + } + + return "", fmt.Errorf("could not find the start of authority for %s%s", fqdn, formatDNSError(in, err)) +} + +// dnsMsgContainsCNAME checks for a CNAME answer in msg +func dnsMsgContainsCNAME(msg *dns.Msg) bool { + for _, ans := range msg.Answer { + if _, ok := ans.(*dns.CNAME); ok { + return true + } + } + return false +} + +func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (*dns.Msg, error) { + m := createDNSMsg(fqdn, rtype, recursive) + + var in *dns.Msg + var err error + + for _, ns := range nameservers { + in, err = sendDNSQuery(m, ns) + if err == nil && len(in.Answer) > 0 { + break + } + } + return in, err +} + +func createDNSMsg(fqdn string, rtype uint16, recursive bool) *dns.Msg { + m := new(dns.Msg) + m.SetQuestion(fqdn, rtype) + m.SetEdns0(4096, false) + + if !recursive { + m.RecursionDesired = false + } + + return m +} + +func sendDNSQuery(m *dns.Msg, ns string) (*dns.Msg, error) { + udp := &dns.Client{Net: "udp", Timeout: dnsTimeout} + in, _, err := udp.Exchange(m, ns) + + if in != nil && in.Truncated { + tcp := &dns.Client{Net: "tcp", Timeout: dnsTimeout} + // If the TCP request succeeds, the err will reset to nil + in, _, err = tcp.Exchange(m, ns) + } + + return in, err +} + +func formatDNSError(msg *dns.Msg, err error) string { + var parts []string + + if msg != nil { + parts = append(parts, dns.RcodeToString[msg.Rcode]) + } + + if err != nil { + parts = append(parts, fmt.Sprintf("%v", err)) + } + + if len(parts) > 0 { + return ": " + strings.Join(parts, " ") + } + + return "" +} diff --git a/vendor/github.com/xenolf/lego/challenge/dns01/precheck.go b/vendor/github.com/xenolf/lego/challenge/dns01/precheck.go new file mode 100644 index 000000000..63b72cef0 --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/dns01/precheck.go @@ -0,0 +1,114 @@ +package dns01 + +import ( + "fmt" + "net" + "strings" + + "github.com/miekg/dns" +) + +// PreCheckFunc checks DNS propagation before notifying ACME that the DNS challenge is ready. +type PreCheckFunc func(fqdn, value string) (bool, error) + +func AddPreCheck(preCheck PreCheckFunc) ChallengeOption { + // Prevent race condition + check := preCheck + return func(chlg *Challenge) error { + chlg.preCheck.checkFunc = check + return nil + } +} + +func DisableCompletePropagationRequirement() ChallengeOption { + return func(chlg *Challenge) error { + chlg.preCheck.requireCompletePropagation = false + return nil + } +} + +type preCheck struct { + // checks DNS propagation before notifying ACME that the DNS challenge is ready. + checkFunc PreCheckFunc + // require the TXT record to be propagated to all authoritative name servers + requireCompletePropagation bool +} + +func newPreCheck() preCheck { + return preCheck{ + requireCompletePropagation: true, + } +} + +func (p preCheck) call(fqdn, value string) (bool, error) { + if p.checkFunc == nil { + return p.checkDNSPropagation(fqdn, value) + } + return p.checkFunc(fqdn, value) +} + +// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers. +func (p preCheck) 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 !p.requireCompletePropagation { + return true, nil + } + + 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 records []string + + var found bool + for _, rr := range r.Answer { + if txt, ok := rr.(*dns.TXT); ok { + record := strings.Join(txt.Txt, "") + records = append(records, record) + if record == value { + found = true + break + } + } + } + + if !found { + return false, fmt.Errorf("NS %s did not return the expected TXT record [fqdn: %s, value: %s]: %s", ns, fqdn, value, strings.Join(records, " ,")) + } + } + + return true, nil +} diff --git a/vendor/github.com/xenolf/lego/challenge/http01/http_challenge.go b/vendor/github.com/xenolf/lego/challenge/http01/http_challenge.go new file mode 100644 index 000000000..4176ecae4 --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/http01/http_challenge.go @@ -0,0 +1,65 @@ +package http01 + +import ( + "fmt" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/acme/api" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/log" +) + +type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error + +// ChallengePath returns the URL path for the `http-01` challenge +func ChallengePath(token string) string { + return "/.well-known/acme-challenge/" + token +} + +type Challenge struct { + core *api.Core + validate ValidateFunc + provider challenge.Provider +} + +func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge { + return &Challenge{ + core: core, + validate: validate, + provider: provider, + } +} + +func (c *Challenge) SetProvider(provider challenge.Provider) { + c.provider = provider +} + +func (c *Challenge) Solve(authz acme.Authorization) error { + domain := challenge.GetTargetedDomain(authz) + log.Infof("[%s] acme: Trying to solve HTTP-01", domain) + + chlng, err := challenge.FindChallenge(challenge.HTTP01, authz) + if err != nil { + return err + } + + // Generate the Key Authorization for the challenge + keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) + if err != nil { + return err + } + + err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth) + if err != nil { + return fmt.Errorf("[%s] acme: error presenting token: %v", domain, err) + } + defer func() { + err := c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth) + if err != nil { + log.Warnf("[%s] acme: error cleaning up: %v", domain, err) + } + }() + + chlng.KeyAuthorization = keyAuth + return c.validate(c.core, authz.Identifier.Value, chlng) +} diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge_server.go b/vendor/github.com/xenolf/lego/challenge/http01/http_challenge_server.go similarity index 61% rename from vendor/github.com/xenolf/lego/acme/http_challenge_server.go rename to vendor/github.com/xenolf/lego/challenge/http01/http_challenge_server.go index 9c595d7f5..aa463bb42 100644 --- a/vendor/github.com/xenolf/lego/acme/http_challenge_server.go +++ b/vendor/github.com/xenolf/lego/challenge/http01/http_challenge_server.go @@ -1,4 +1,4 @@ -package acme +package http01 import ( "fmt" @@ -9,31 +9,31 @@ import ( "github.com/xenolf/lego/log" ) -// HTTPProviderServer implements ChallengeProvider for `http-01` challenge -// It may be instantiated without using the NewHTTPProviderServer function if +// ProviderServer implements ChallengeProvider for `http-01` challenge +// It may be instantiated without using the NewProviderServer function if // you want only to use the default values. -type HTTPProviderServer struct { +type ProviderServer struct { iface string port string done chan bool listener net.Listener } -// NewHTTPProviderServer creates a new HTTPProviderServer on the selected interface and port. +// NewProviderServer creates a new ProviderServer 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} +func NewProviderServer(iface, port string) *ProviderServer { + return &ProviderServer{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 { +// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests. +func (s *ProviderServer) 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)) + s.listener, err = net.Listen("tcp", s.GetAddress()) if err != nil { return fmt.Errorf("could not start HTTP server for challenge -> %v", err) } @@ -43,8 +43,12 @@ func (s *HTTPProviderServer) Present(domain, token, keyAuth string) error { return nil } -// CleanUp closes the HTTP server and removes the token from `HTTP01ChallengePath(token)` -func (s *HTTPProviderServer) CleanUp(domain, token, keyAuth string) error { +func (s *ProviderServer) GetAddress() string { + return net.JoinHostPort(s.iface, s.port) +} + +// CleanUp closes the HTTP server and removes the token from `ChallengePath(token)` +func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { if s.listener == nil { return nil } @@ -53,8 +57,8 @@ func (s *HTTPProviderServer) CleanUp(domain, token, keyAuth string) error { return nil } -func (s *HTTPProviderServer) serve(domain, token, keyAuth string) { - path := HTTP01ChallengePath(token) +func (s *ProviderServer) serve(domain, token, keyAuth string) { + path := ChallengePath(token) // The handler validates the HOST header and request type. // For validation it then writes the token the server returned with the challenge @@ -80,12 +84,12 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) { httpServer := &http.Server{Handler: mux} - // Once httpServer is shut down we don't want any lingering - // connections, so disable KeepAlives. + // Once httpServer is shut down + // we don't want any lingering connections, so disable KeepAlives. httpServer.SetKeepAlivesEnabled(false) err := httpServer.Serve(s.listener) - if err != nil { + if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { log.Println(err) } s.done <- true diff --git a/vendor/github.com/xenolf/lego/acme/provider.go b/vendor/github.com/xenolf/lego/challenge/provider.go similarity index 58% rename from vendor/github.com/xenolf/lego/acme/provider.go rename to vendor/github.com/xenolf/lego/challenge/provider.go index d177ff07a..d7cc213f7 100644 --- a/vendor/github.com/xenolf/lego/acme/provider.go +++ b/vendor/github.com/xenolf/lego/challenge/provider.go @@ -1,28 +1,28 @@ -package acme +package challenge import "time" -// ChallengeProvider enables implementing a custom challenge +// Provider 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 { +type Provider 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 +// ProviderTimeout allows for implementing a +// Provider 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 +// checking for DNS record propagation. If an implementor of a +// Provider 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 +// defined for the Provider. +type ProviderTimeout interface { + Provider Timeout() (timeout, interval time.Duration) } diff --git a/vendor/github.com/xenolf/lego/challenge/resolver/errors.go b/vendor/github.com/xenolf/lego/challenge/resolver/errors.go new file mode 100644 index 000000000..9d6091432 --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/resolver/errors.go @@ -0,0 +1,25 @@ +package resolver + +import ( + "bytes" + "fmt" + "sort" +) + +// obtainError is returned when there are specific errors available per domain. +type obtainError map[string]error + +func (e obtainError) Error() string { + buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n") + + var domains []string + for domain := range e { + domains = append(domains, domain) + } + sort.Strings(domains) + + for _, domain := range domains { + buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain])) + } + return buffer.String() +} diff --git a/vendor/github.com/xenolf/lego/challenge/resolver/prober.go b/vendor/github.com/xenolf/lego/challenge/resolver/prober.go new file mode 100644 index 000000000..2f477f9cc --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/resolver/prober.go @@ -0,0 +1,173 @@ +package resolver + +import ( + "fmt" + "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/log" +) + +// Interface for all challenge solvers to implement. +type solver interface { + Solve(authorization acme.Authorization) error +} + +// Interface for challenges like dns, where we can set a record in advance for ALL challenges. +// This saves quite a bit of time vs creating the records and solving them serially. +type preSolver interface { + PreSolve(authorization acme.Authorization) error +} + +// Interface for challenges like dns, where we can solve all the challenges before to delete them. +type cleanup interface { + CleanUp(authorization acme.Authorization) error +} + +type sequential interface { + Sequential() (bool, time.Duration) +} + +// an authz with the solver we have chosen and the index of the challenge associated with it +type selectedAuthSolver struct { + authz acme.Authorization + solver solver +} + +type Prober struct { + solverManager *SolverManager +} + +func NewProber(solverManager *SolverManager) *Prober { + return &Prober{ + solverManager: solverManager, + } +} + +// Solve Looks through the challenge combinations to find a solvable match. +// Then solves the challenges in series and returns. +func (p *Prober) Solve(authorizations []acme.Authorization) error { + failures := make(obtainError) + + var authSolvers []*selectedAuthSolver + var authSolversSequential []*selectedAuthSolver + + // Loop through the resources, basically through the domains. + // First pass just selects a solver for each authz. + for _, authz := range authorizations { + domain := challenge.GetTargetedDomain(authz) + if authz.Status == acme.StatusValid { + // Boulder might recycle recent validated authz (see issue #267) + log.Infof("[%s] acme: authorization already valid; skipping challenge", domain) + continue + } + + if solvr := p.solverManager.chooseSolver(authz); solvr != nil { + authSolver := &selectedAuthSolver{authz: authz, solver: solvr} + + switch s := solvr.(type) { + case sequential: + if ok, _ := s.Sequential(); ok { + authSolversSequential = append(authSolversSequential, authSolver) + } else { + authSolvers = append(authSolvers, authSolver) + } + default: + authSolvers = append(authSolvers, authSolver) + } + } else { + failures[domain] = fmt.Errorf("[%s] acme: could not determine solvers", domain) + } + } + + parallelSolve(authSolvers, failures) + + sequentialSolve(authSolversSequential, failures) + + // Be careful not to return an empty failures map, + // for even an empty obtainError is a non-nil error value + if len(failures) > 0 { + return failures + } + return nil +} + +func sequentialSolve(authSolvers []*selectedAuthSolver, failures obtainError) { + for i, authSolver := range authSolvers { + // Submit the challenge + domain := challenge.GetTargetedDomain(authSolver.authz) + + if solvr, ok := authSolver.solver.(preSolver); ok { + err := solvr.PreSolve(authSolver.authz) + if err != nil { + failures[domain] = err + cleanUp(authSolver.solver, authSolver.authz) + continue + } + } + + // Solve challenge + err := authSolver.solver.Solve(authSolver.authz) + if err != nil { + failures[domain] = err + cleanUp(authSolver.solver, authSolver.authz) + continue + } + + // Clean challenge + cleanUp(authSolver.solver, authSolver.authz) + + if len(authSolvers)-1 > i { + solvr := authSolver.solver.(sequential) + _, interval := solvr.Sequential() + log.Infof("sequence: wait for %s", interval) + time.Sleep(interval) + } + } +} + +func parallelSolve(authSolvers []*selectedAuthSolver, failures obtainError) { + // For all valid preSolvers, first submit the challenges so they have max time to propagate + for _, authSolver := range authSolvers { + authz := authSolver.authz + if solvr, ok := authSolver.solver.(preSolver); ok { + err := solvr.PreSolve(authz) + if err != nil { + failures[challenge.GetTargetedDomain(authz)] = err + } + } + } + + defer func() { + // Clean all created TXT records + for _, authSolver := range authSolvers { + cleanUp(authSolver.solver, authSolver.authz) + } + }() + + // Finally solve all challenges for real + for _, authSolver := range authSolvers { + authz := authSolver.authz + domain := challenge.GetTargetedDomain(authz) + if failures[domain] != nil { + // already failed in previous loop + continue + } + + err := authSolver.solver.Solve(authz) + if err != nil { + failures[domain] = err + } + } +} + +func cleanUp(solvr solver, authz acme.Authorization) { + if solvr, ok := solvr.(cleanup); ok { + domain := challenge.GetTargetedDomain(authz) + err := solvr.CleanUp(authz) + if err != nil { + log.Warnf("[%s] acme: error cleaning up: %v ", domain, err) + } + } +} diff --git a/vendor/github.com/xenolf/lego/challenge/resolver/solver_manager.go b/vendor/github.com/xenolf/lego/challenge/resolver/solver_manager.go new file mode 100644 index 000000000..55faf77ab --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/resolver/solver_manager.go @@ -0,0 +1,154 @@ +package resolver + +import ( + "errors" + "fmt" + "sort" + "strconv" + "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/acme/api" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/challenge/dns01" + "github.com/xenolf/lego/challenge/http01" + "github.com/xenolf/lego/challenge/tlsalpn01" + "github.com/xenolf/lego/log" +) + +type byType []acme.Challenge + +func (a byType) Len() int { return len(a) } +func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byType) Less(i, j int) bool { return a[i].Type > a[j].Type } + +type SolverManager struct { + core *api.Core + solvers map[challenge.Type]solver +} + +func NewSolversManager(core *api.Core) *SolverManager { + return &SolverManager{ + solvers: map[challenge.Type]solver{}, + core: core, + } +} + +// SetHTTP01Provider specifies a custom provider p that can solve the given HTTP-01 challenge. +func (c *SolverManager) SetHTTP01Provider(p challenge.Provider) error { + c.solvers[challenge.HTTP01] = http01.NewChallenge(c.core, validate, p) + return nil +} + +// SetTLSALPN01Provider specifies a custom provider p that can solve the given TLS-ALPN-01 challenge. +func (c *SolverManager) SetTLSALPN01Provider(p challenge.Provider) error { + c.solvers[challenge.TLSALPN01] = tlsalpn01.NewChallenge(c.core, validate, p) + return nil +} + +// SetDNS01Provider specifies a custom provider p that can solve the given DNS-01 challenge. +func (c *SolverManager) SetDNS01Provider(p challenge.Provider, opts ...dns01.ChallengeOption) error { + c.solvers[challenge.DNS01] = dns01.NewChallenge(c.core, validate, p, opts...) + return nil +} + +// Remove Remove a challenge type from the available solvers. +func (c *SolverManager) Remove(chlgType challenge.Type) { + delete(c.solvers, chlgType) +} + +// Checks all challenges from the server in order and returns the first matching solver. +func (c *SolverManager) chooseSolver(authz acme.Authorization) solver { + // Allow to have a deterministic challenge order + sort.Sort(byType(authz.Challenges)) + + domain := challenge.GetTargetedDomain(authz) + for _, chlg := range authz.Challenges { + if solvr, ok := c.solvers[challenge.Type(chlg.Type)]; ok { + log.Infof("[%s] acme: use %s solver", domain, chlg.Type) + return solvr + } + log.Infof("[%s] acme: Could not find solver for: %s", domain, chlg.Type) + } + + return nil +} + +func validate(core *api.Core, domain string, chlg acme.Challenge) error { + chlng, err := core.Challenges.New(chlg.URL) + if err != nil { + return fmt.Errorf("failed to initiate challenge: %v", err) + } + + valid, err := checkChallengeStatus(chlng) + if err != nil { + return err + } + + if valid { + log.Infof("[%s] The server validated our request", domain) + return nil + } + + // After the path is sent, the ACME server will access our server. + // Repeatedly check the server for an updated status on our request. + for { + authz, err := core.Authorizations.Get(chlng.AuthorizationURL) + if err != nil { + return err + } + + valid, err := checkAuthorizationStatus(authz) + if err != nil { + return err + } + + if valid { + log.Infof("[%s] The server validated our request", domain) + return nil + } + + ra, err := strconv.Atoi(chlng.RetryAfter) + if err != nil { + // The ACME server MUST return a Retry-After. + // If it doesn't, we'll just poll hard. + // Boulder does not implement the ability to retry challenges or the Retry-After header. + // https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md#section-82 + ra = 5 + } + time.Sleep(time.Duration(ra) * time.Second) + } +} + +func checkChallengeStatus(chlng acme.ExtendedChallenge) (bool, error) { + switch chlng.Status { + case acme.StatusValid: + return true, nil + case acme.StatusPending, acme.StatusProcessing: + return false, nil + case acme.StatusInvalid: + return false, chlng.Error + default: + return false, errors.New("the server returned an unexpected state") + } +} + +func checkAuthorizationStatus(authz acme.Authorization) (bool, error) { + switch authz.Status { + case acme.StatusValid: + return true, nil + case acme.StatusPending, acme.StatusProcessing: + return false, nil + case acme.StatusDeactivated, acme.StatusExpired, acme.StatusRevoked: + return false, fmt.Errorf("the authorization state %s", authz.Status) + case acme.StatusInvalid: + for _, chlg := range authz.Challenges { + if chlg.Status == acme.StatusInvalid && chlg.Error != nil { + return false, chlg.Error + } + } + return false, fmt.Errorf("the authorization state %s", authz.Status) + default: + return false, errors.New("the server returned an unexpected state") + } +} diff --git a/vendor/github.com/xenolf/lego/challenge/tlsalpn01/tls_alpn_challenge.go b/vendor/github.com/xenolf/lego/challenge/tlsalpn01/tls_alpn_challenge.go new file mode 100644 index 000000000..fa03ffdbb --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/tlsalpn01/tls_alpn_challenge.go @@ -0,0 +1,129 @@ +package tlsalpn01 + +import ( + "crypto/rsa" + "crypto/sha256" + "crypto/tls" + "crypto/x509/pkix" + "encoding/asn1" + "fmt" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/acme/api" + "github.com/xenolf/lego/certcrypto" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/log" +) + +// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension. +// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1 +var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} + +type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error + +type Challenge struct { + core *api.Core + validate ValidateFunc + provider challenge.Provider +} + +func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge { + return &Challenge{ + core: core, + validate: validate, + provider: provider, + } +} + +func (c *Challenge) SetProvider(provider challenge.Provider) { + c.provider = provider +} + +// Solve manages the provider to validate and solve the challenge. +func (c *Challenge) Solve(authz acme.Authorization) error { + domain := authz.Identifier.Value + log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", challenge.GetTargetedDomain(authz)) + + chlng, err := challenge.FindChallenge(challenge.TLSALPN01, authz) + if err != nil { + return err + } + + // Generate the Key Authorization for the challenge + keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) + if err != nil { + return err + } + + err = c.provider.Present(domain, chlng.Token, keyAuth) + if err != nil { + return fmt.Errorf("[%s] acme: error presenting token: %v", challenge.GetTargetedDomain(authz), err) + } + defer func() { + err := c.provider.CleanUp(domain, chlng.Token, keyAuth) + if err != nil { + log.Warnf("[%s] acme: error cleaning up: %v", challenge.GetTargetedDomain(authz), err) + } + }() + + chlng.KeyAuthorization = keyAuth + return c.validate(c.core, domain, chlng) +} + +// ChallengeBlocks returns PEM blocks (certPEMBlock, keyPEMBlock) with the acmeValidation-v1 extension +// and domain name for the `tls-alpn-01` challenge. +func ChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) { + // Compute the SHA-256 digest of the key authorization. + zBytes := sha256.Sum256([]byte(keyAuth)) + + value, err := asn1.Marshal(zBytes[:sha256.Size]) + if err != nil { + return nil, nil, err + } + + // Add the keyAuth digest as the acmeValidation-v1 extension + // (marked as critical such that it won't be used by non-ACME software). + // Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-3 + extensions := []pkix.Extension{ + { + Id: idPeAcmeIdentifierV1, + Critical: true, + Value: value, + }, + } + + // Generate a new RSA key for the certificates. + tempPrivateKey, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) + if err != nil { + return nil, nil, err + } + + rsaPrivateKey := tempPrivateKey.(*rsa.PrivateKey) + + // Generate the PEM certificate using the provided private key, domain, and extra extensions. + tempCertPEM, err := certcrypto.GeneratePemCert(rsaPrivateKey, domain, extensions) + if err != nil { + return nil, nil, err + } + + // Encode the private key into a PEM format. We'll need to use it to generate the x509 keypair. + rsaPrivatePEM := certcrypto.PEMEncode(rsaPrivateKey) + + return tempCertPEM, rsaPrivatePEM, nil +} + +// ChallengeCert returns a certificate with the acmeValidation-v1 extension +// and domain name for the `tls-alpn-01` challenge. +func ChallengeCert(domain, keyAuth string) (*tls.Certificate, error) { + tempCertPEM, rsaPrivatePEM, err := ChallengeBlocks(domain, keyAuth) + if err != nil { + return nil, err + } + + cert, err := tls.X509KeyPair(tempCertPEM, rsaPrivatePEM) + if err != nil { + return nil, err + } + + return &cert, nil +} diff --git a/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go b/vendor/github.com/xenolf/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go similarity index 54% rename from vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go rename to vendor/github.com/xenolf/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go index ee06a16d4..1f7480c28 100644 --- a/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go +++ b/vendor/github.com/xenolf/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go @@ -1,49 +1,54 @@ -package acme +package tlsalpn01 import ( "crypto/tls" "fmt" - "log" "net" "net/http" + "strings" + + "github.com/xenolf/lego/log" ) const ( // ACMETLS1Protocol is the ALPN Protocol ID for the ACME-TLS/1 Protocol. ACMETLS1Protocol = "acme-tls/1" - // defaultTLSPort is the port that the TLSALPNProviderServer will default to + // defaultTLSPort is the port that the ProviderServer will default to // when no other port is provided. defaultTLSPort = "443" ) -// TLSALPNProviderServer implements ChallengeProvider for `TLS-ALPN-01` -// challenge. It may be instantiated without using the NewTLSALPNProviderServer +// ProviderServer implements ChallengeProvider for `TLS-ALPN-01` challenge. +// It may be instantiated without using the NewProviderServer // if you want only to use the default values. -type TLSALPNProviderServer struct { +type ProviderServer struct { iface string port string listener net.Listener } -// NewTLSALPNProviderServer creates a new TLSALPNProviderServer 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 NewTLSALPNProviderServer(iface, port string) *TLSALPNProviderServer { - return &TLSALPNProviderServer{iface: iface, port: port} +// NewProviderServer creates a new ProviderServer 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 NewProviderServer(iface, port string) *ProviderServer { + return &ProviderServer{iface: iface, port: port} +} + +func (s *ProviderServer) GetAddress() string { + return net.JoinHostPort(s.iface, s.port) } // Present generates a certificate with a SHA-256 digest of the keyAuth provided -// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN -// spec. -func (t *TLSALPNProviderServer) Present(domain, token, keyAuth string) error { - if t.port == "" { +// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN spec. +func (s *ProviderServer) Present(domain, token, keyAuth string) error { + if s.port == "" { // Fallback to port 443 if the port was not provided. - t.port = defaultTLSPort + s.port = defaultTLSPort } // Generate the challenge certificate using the provided keyAuth and domain. - cert, err := TLSALPNChallengeCert(domain, keyAuth) + cert, err := ChallengeCert(domain, keyAuth) if err != nil { return err } @@ -59,15 +64,15 @@ func (t *TLSALPNProviderServer) Present(domain, token, keyAuth string) error { tlsConf.NextProtos = []string{ACMETLS1Protocol} // Create the listener with the created tls.Config. - t.listener, err = tls.Listen("tcp", net.JoinHostPort(t.iface, t.port), tlsConf) + s.listener, err = tls.Listen("tcp", s.GetAddress(), tlsConf) if err != nil { return fmt.Errorf("could not start HTTPS server for challenge -> %v", err) } // Shut the server down when we're finished. go func() { - err := http.Serve(t.listener, nil) - if err != nil { + err := http.Serve(s.listener, nil) + if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { log.Println(err) } }() @@ -76,13 +81,13 @@ func (t *TLSALPNProviderServer) Present(domain, token, keyAuth string) error { } // CleanUp closes the HTTPS server. -func (t *TLSALPNProviderServer) CleanUp(domain, token, keyAuth string) error { - if t.listener == nil { +func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { + if s.listener == nil { return nil } // Server was created, close it. - if err := t.listener.Close(); err != nil && err != http.ErrServerClosed { + if err := s.listener.Close(); err != nil && err != http.ErrServerClosed { return err } diff --git a/vendor/github.com/xenolf/lego/lego/client.go b/vendor/github.com/xenolf/lego/lego/client.go new file mode 100644 index 000000000..5235b1de8 --- /dev/null +++ b/vendor/github.com/xenolf/lego/lego/client.go @@ -0,0 +1,73 @@ +package lego + +import ( + "errors" + "net/url" + + "github.com/xenolf/lego/acme/api" + "github.com/xenolf/lego/certificate" + "github.com/xenolf/lego/challenge/resolver" + "github.com/xenolf/lego/registration" +) + +// Client is the user-friendly way to ACME +type Client struct { + Certificate *certificate.Certifier + Challenge *resolver.SolverManager + Registration *registration.Registrar + core *api.Core +} + +// 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. +// A private key of type keyType (see KeyType constants) will be generated when requesting a new certificate if one isn't provided. +func NewClient(config *Config) (*Client, error) { + if config == nil { + return nil, errors.New("a configuration must be provided") + } + + _, err := url.Parse(config.CADirURL) + if err != nil { + return nil, err + } + + if config.HTTPClient == nil { + return nil, errors.New("the HTTP client cannot be nil") + } + + privateKey := config.User.GetPrivateKey() + if privateKey == nil { + return nil, errors.New("private key was nil") + } + + var kid string + if reg := config.User.GetRegistration(); reg != nil { + kid = reg.URI + } + + core, err := api.New(config.HTTPClient, config.UserAgent, config.CADirURL, kid, privateKey) + if err != nil { + return nil, err + } + + solversManager := resolver.NewSolversManager(core) + + prober := resolver.NewProber(solversManager) + + return &Client{ + Certificate: certificate.NewCertifier(core, config.KeyType, prober), + Challenge: solversManager, + Registration: registration.NewRegistrar(core, config.User), + core: core, + }, nil +} + +// GetToSURL returns the current ToS URL from the Directory +func (c *Client) GetToSURL() string { + return c.core.GetDirectory().Meta.TermsOfService +} + +// GetExternalAccountRequired returns the External Account Binding requirement of the Directory +func (c *Client) GetExternalAccountRequired() bool { + return c.core.GetDirectory().Meta.ExternalAccountRequired +} diff --git a/vendor/github.com/xenolf/lego/lego/client_config.go b/vendor/github.com/xenolf/lego/lego/client_config.go new file mode 100644 index 000000000..738be86ae --- /dev/null +++ b/vendor/github.com/xenolf/lego/lego/client_config.go @@ -0,0 +1,96 @@ +package lego + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "net" + "net/http" + "os" + "time" + + "github.com/xenolf/lego/certcrypto" + "github.com/xenolf/lego/registration" +) + +const ( + // caCertificatesEnvVar is the environment variable name that can be used to + // specify the path to PEM encoded CA Certificates that can be used to + // authenticate an ACME server with a HTTPS certificate not issued by a CA in + // the system-wide trusted root list. + caCertificatesEnvVar = "LEGO_CA_CERTIFICATES" + + // caServerNameEnvVar is the environment variable name that can be used to + // specify the CA server name that can be used to + // authenticate an ACME server with a HTTPS certificate not issued by a CA in + // the system-wide trusted root list. + caServerNameEnvVar = "LEGO_CA_SERVER_NAME" + + // LEDirectoryProduction URL to the Let's Encrypt production + LEDirectoryProduction = "https://acme-v02.api.letsencrypt.org/directory" + + // LEDirectoryStaging URL to the Let's Encrypt staging + LEDirectoryStaging = "https://acme-staging-v02.api.letsencrypt.org/directory" +) + +type Config struct { + CADirURL string + User registration.User + KeyType certcrypto.KeyType + UserAgent string + HTTPClient *http.Client +} + +func NewConfig(user registration.User) *Config { + return &Config{ + CADirURL: LEDirectoryProduction, + User: user, + KeyType: certcrypto.RSA2048, + HTTPClient: createDefaultHTTPClient(), + } +} + +// createDefaultHTTPClient Creates an HTTP client with a reasonable timeout value +// and potentially a custom *x509.CertPool +// based on the caCertificatesEnvVar environment variable (see the `initCertPool` function) +func createDefaultHTTPClient() *http.Client { + return &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + TLSHandshakeTimeout: 15 * time.Second, + ResponseHeaderTimeout: 15 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + TLSClientConfig: &tls.Config{ + ServerName: os.Getenv(caServerNameEnvVar), + RootCAs: initCertPool(), + }, + }, + } +} + +// initCertPool creates a *x509.CertPool populated with the PEM certificates +// found in the filepath specified in the caCertificatesEnvVar OS environment +// variable. If the caCertificatesEnvVar is not set then initCertPool will +// return nil. If there is an error creating a *x509.CertPool from the provided +// caCertificatesEnvVar value then initCertPool will panic. +func initCertPool() *x509.CertPool { + if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" { + customCAs, err := ioutil.ReadFile(customCACertsPath) + if err != nil { + panic(fmt.Sprintf("error reading %s=%q: %v", + caCertificatesEnvVar, customCACertsPath, err)) + } + certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(customCAs); !ok { + panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v", + caCertificatesEnvVar, customCACertsPath, err)) + } + return certPool + } + return nil +} diff --git a/vendor/github.com/xenolf/lego/acme/utils.go b/vendor/github.com/xenolf/lego/platform/wait/wait.go similarity index 58% rename from vendor/github.com/xenolf/lego/acme/utils.go rename to vendor/github.com/xenolf/lego/platform/wait/wait.go index f3160806d..511e1f28f 100644 --- a/vendor/github.com/xenolf/lego/acme/utils.go +++ b/vendor/github.com/xenolf/lego/platform/wait/wait.go @@ -1,4 +1,4 @@ -package acme +package wait import ( "fmt" @@ -7,9 +7,9 @@ import ( "github.com/xenolf/lego/log" ) -// WaitFor polls the given function 'f', once every 'interval', up to 'timeout'. -func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error { - log.Infof("Wait [timeout: %s, interval: %s]", timeout, interval) +// For polls the given function 'f', once every 'interval', up to 'timeout'. +func For(msg string, timeout, interval time.Duration, f func() (bool, error)) error { + log.Infof("Wait for %s [timeout: %s, interval: %s]", msg, timeout, interval) var lastErr string timeUp := time.After(timeout) diff --git a/vendor/github.com/xenolf/lego/providers/dns/acmedns/acmedns.go b/vendor/github.com/xenolf/lego/providers/dns/acmedns/acmedns.go index 9ad0ef369..e642420a2 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/acmedns/acmedns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/acmedns/acmedns.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/cpu/goacmedns" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -17,21 +17,19 @@ const ( // apiBaseEnvVar is the environment variable name for the ACME-DNS API address // (e.g. https://acmedns.your-domain.com). apiBaseEnvVar = envNamespace + "API_BASE" - // storagePathEnvVar is the environment variable name for the ACME-DNS JSON - // account data file. A per-domain account will be registered/persisted to - // this file and used for TXT updates. + // storagePathEnvVar is the environment variable name for the ACME-DNS JSON account data file. + // A per-domain account will be registered/persisted to this file and used for TXT updates. storagePathEnvVar = envNamespace + "STORAGE_PATH" ) -// acmeDNSClient is an interface describing the goacmedns.Client functions -// the DNSProvider uses. It makes it easier for tests to shim a mock Client into -// the DNSProvider. +// acmeDNSClient is an interface describing the goacmedns.Client functions the DNSProvider uses. +// It makes it easier for tests to shim a mock Client into the DNSProvider. type acmeDNSClient interface { - // UpdateTXTRecord updates the provided account's TXT record to the given - // value or returns an error. + // UpdateTXTRecord updates the provided account's TXT record + // to the given value or returns an error. UpdateTXTRecord(goacmedns.Account, string) error - // RegisterAccount registers and returns a new account with the given - // allowFrom restriction or returns an error. + // RegisterAccount registers and returns a new account + // with the given allowFrom restriction or returns an error. RegisterAccount([]string) (goacmedns.Account, error) } @@ -43,8 +41,7 @@ type DNSProvider struct { } // NewDNSProvider creates an ACME-DNS provider using file based account storage. -// Its configuration is loaded from the environment by reading apiBaseEnvVar and -// storagePathEnvVar. +// Its configuration is loaded from the environment by reading apiBaseEnvVar and storagePathEnvVar. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get(apiBaseEnvVar, storagePathEnvVar) if err != nil { @@ -56,8 +53,7 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderClient(client, storage) } -// NewDNSProviderClient creates an ACME-DNS DNSProvider with the given -// acmeDNSClient and goacmedns.Storage. +// NewDNSProviderClient creates an ACME-DNS DNSProvider with the given acmeDNSClient and goacmedns.Storage. func NewDNSProviderClient(client acmeDNSClient, storage goacmedns.Storage) (*DNSProvider, error) { if client == nil { return nil, errors.New("ACME-DNS Client must be not nil") @@ -76,8 +72,7 @@ func NewDNSProviderClient(client acmeDNSClient, storage goacmedns.Storage) (*DNS // ErrCNAMERequired is returned by Present when the Domain indicated had no // existing ACME-DNS account in the Storage and additional setup is required. // The user must create a CNAME in the DNS zone for Domain that aliases FQDN -// to Target in order to complete setup for the ACME-DNS account that was -// created. +// to Target in order to complete setup for the ACME-DNS account that was created. type ErrCNAMERequired struct { // The Domain that is being issued for. Domain string @@ -100,18 +95,16 @@ func (e ErrCNAMERequired) Error() string { e.Domain, e.Domain, e.FQDN, e.Target) } -// Present creates a TXT record to fulfill the DNS-01 challenge. If there is an -// existing account for the domain in the provider's storage then it will be -// used to set the challenge response TXT record with the ACME-DNS server and -// issuance will continue. If there is not an account for the given domain -// present in the DNSProvider storage one will be created and registered with -// the ACME DNS server and an ErrCNAMERequired error is returned. This will halt -// issuance and indicate to the user that a one-time manual setup is required -// for the domain. +// Present creates a TXT record to fulfill the DNS-01 challenge. +// If there is an existing account for the domain in the provider's storage +// then it will be used to set the challenge response TXT record with the ACME-DNS server and issuance will continue. +// If there is not an account for the given domain present in the DNSProvider storage +// one will be created and registered with the ACME DNS server and an ErrCNAMERequired error is returned. +// This will halt issuance and indicate to the user that a one-time manual setup is required for the domain. func (d *DNSProvider) Present(domain, _, keyAuth string) error { // Compute the challenge response FQDN and TXT value for the domain based // on the keyAuth. - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) // Check if credentials were previously saved for this domain. account, err := d.storage.Fetch(domain) @@ -132,15 +125,15 @@ func (d *DNSProvider) Present(domain, _, keyAuth string) error { // CleanUp removes the record matching the specified parameters. It is not // implemented for the ACME-DNS provider. func (d *DNSProvider) CleanUp(_, _, _ string) error { - // ACME-DNS doesn't support the notion of removing a record. For users of - // ACME-DNS it is expected the stale records remain in-place. + // ACME-DNS doesn't support the notion of removing a record. + // For users of ACME-DNS it is expected the stale records remain in-place. return nil } -// register creates a new ACME-DNS account for the given domain. If account -// creation works as expected a ErrCNAMERequired error is returned describing -// the one-time manual CNAME setup required to complete setup of the ACME-DNS -// hook for the domain. If any other error occurs it is returned as-is. +// register creates a new ACME-DNS account for the given domain. +// If account creation works as expected a ErrCNAMERequired error is returned describing +// the one-time manual CNAME setup required to complete setup of the ACME-DNS hook for the domain. +// If any other error occurs it is returned as-is. func (d *DNSProvider) register(domain, fqdn string) error { // TODO(@cpu): Read CIDR whitelists from the environment newAcct, err := d.client.RegisterAccount(nil) @@ -158,9 +151,9 @@ func (d *DNSProvider) register(domain, fqdn string) error { return err } - // Stop issuance by returning an error. The user needs to perform a manual - // one-time CNAME setup in their DNS zone to complete the setup of the new - // account we created. + // Stop issuance by returning an error. + // The user needs to perform a manual one-time CNAME setup in their DNS zone + // to complete the setup of the new account we created. return ErrCNAMERequired{ Domain: domain, FQDN: fqdn, diff --git a/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go b/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go index 41aeb905b..e7ea22ef0 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go @@ -1,5 +1,4 @@ -// Package alidns implements a DNS provider for solving the DNS-01 challenge -// using Alibaba Cloud DNS. +// Package alidns implements a DNS provider for solving the DNS-01 challenge using Alibaba Cloud DNS. package alidns import ( @@ -12,7 +11,7 @@ import ( "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -33,8 +32,8 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ TTL: env.GetOrDefaultInt("ALICLOUD_TTL", 600), - PropagationTimeout: env.GetOrDefaultSecond("ALICLOUD_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("ALICLOUD_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("ALICLOUD_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("ALICLOUD_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPTimeout: env.GetOrDefaultSecond("ALICLOUD_HTTP_TIMEOUT", 10*time.Second), } } @@ -61,18 +60,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for alidns. -// Deprecated -func NewDNSProviderCredentials(apiKey, secretKey, regionID string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIKey = apiKey - config.SecretKey = secretKey - config.RegionID = regionID - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for alidns. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -106,7 +93,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) _, zoneName, err := d.getHostedZone(domain) if err != nil { @@ -124,7 +111,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) records, err := d.findTxtRecords(domain, fqdn) if err != nil { @@ -149,19 +136,35 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) getHostedZone(domain string) (string, string, error) { request := alidns.CreateDescribeDomainsRequest() - zones, err := d.client.DescribeDomains(request) - if err != nil { - return "", "", fmt.Errorf("API call failed: %v", err) + + var domains []alidns.Domain + startPage := 1 + + for { + request.PageNumber = requests.NewInteger(startPage) + + response, err := d.client.DescribeDomains(request) + if err != nil { + return "", "", fmt.Errorf("API call failed: %v", err) + } + + domains = append(domains, response.Domains.Domain...) + + if response.PageNumber >= response.PageSize { + break + } + + startPage++ } - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return "", "", err } var hostedZone alidns.Domain - for _, zone := range zones.Domains.Domain { - if zone.DomainName == acme.UnFqdn(authZone) { + for _, zone := range domains { + if zone.DomainName == dns01.UnFqdn(authZone) { hostedZone = zone } } @@ -209,7 +212,7 @@ func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]alidns.Record, erro } func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } diff --git a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go index 21e455c90..56dff6d0c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go @@ -8,7 +8,7 @@ import ( "time" "github.com/ldez/go-auroradns" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -28,8 +28,8 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ TTL: env.GetOrDefaultInt("AURORA_TTL", 300), - PropagationTimeout: env.GetOrDefaultSecond("AURORA_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("AURORA_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("AURORA_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("AURORA_POLLING_INTERVAL", dns01.DefaultPollingInterval), } } @@ -58,18 +58,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for AuroraDNS. -// Deprecated -func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.BaseURL = baseURL - config.UserID = userID - config.Key = key - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for AuroraDNS. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -103,9 +91,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a record with a secret func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return fmt.Errorf("aurora: could not determine zone for domain: '%s'. %s", domain, err) } @@ -119,7 +107,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { subdomain := fqdn[0 : len(fqdn)-len(authZone)-1] - authZone = acme.UnFqdn(authZone) + authZone = dns01.UnFqdn(authZone) zone, err := d.getZoneInformationByName(authZone) if err != nil { @@ -147,7 +135,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes a given record that was generated by Present func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) d.recordIDsMu.Lock() recordID, ok := d.recordIDs[fqdn] @@ -157,12 +145,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("unknown recordID for %q", fqdn) } - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return fmt.Errorf("could not determine zone for domain: %q. %v", domain, err) } - authZone = acme.UnFqdn(authZone) + authZone = dns01.UnFqdn(authZone) zone, err := d.getZoneInformationByName(authZone) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go b/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go index 74a4e531f..5d95b7cb5 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go +++ b/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go @@ -1,5 +1,4 @@ -// Package azure implements a DNS provider for solving the DNS-01 -// challenge using azure DNS. +// Package azure implements a DNS provider for solving the DNS-01 challenge using azure DNS. // Azure doesn't like trailing dots on domain names, most of the acme code does. package azure @@ -18,7 +17,7 @@ import ( "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure/auth" "github.com/Azure/go-autorest/autorest/to" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -72,20 +71,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for azure. -// Deprecated -func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, resourceGroup string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.ClientID = clientID - config.ClientSecret = clientSecret - config.TenantID = tenantID - config.SubscriptionID = subscriptionID - config.ResourceGroup = resourceGroup - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Azure. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -128,8 +113,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{config: config, authorizer: authorizer}, nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } @@ -137,7 +122,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { ctx := context.Background() - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZoneID(ctx, fqdn) if err != nil { @@ -147,12 +132,38 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { rsc := dns.NewRecordSetsClient(d.config.SubscriptionID) rsc.Authorizer = d.authorizer - relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) + relative := toRelativeRecord(fqdn, dns01.ToFqdn(zone)) + + // Get existing record set + rset, err := rsc.Get(ctx, d.config.ResourceGroup, zone, relative, dns.TXT) + if err != nil { + detailedError, ok := err.(autorest.DetailedError) + if !ok || detailedError.StatusCode != http.StatusNotFound { + return fmt.Errorf("azure: %v", err) + } + } + + // Construct unique TXT records using map + uniqRecords := map[string]struct{}{value: {}} + if rset.RecordSetProperties != nil && rset.TxtRecords != nil { + for _, txtRecord := range *rset.TxtRecords { + // Assume Value doesn't contain multiple strings + if txtRecord.Value != nil && len(*txtRecord.Value) > 0 { + uniqRecords[(*txtRecord.Value)[0]] = struct{}{} + } + } + } + + var txtRecords []dns.TxtRecord + for txt := range uniqRecords { + txtRecords = append(txtRecords, dns.TxtRecord{Value: &[]string{txt}}) + } + rec := dns.RecordSet{ Name: &relative, RecordSetProperties: &dns.RecordSetProperties{ TTL: to.Int64Ptr(int64(d.config.TTL)), - TxtRecords: &[]dns.TxtRecord{{Value: &[]string{value}}}, + TxtRecords: &txtRecords, }, } @@ -166,14 +177,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { ctx := context.Background() - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZoneID(ctx, fqdn) if err != nil { return fmt.Errorf("azure: %v", err) } - relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) + relative := toRelativeRecord(fqdn, dns01.ToFqdn(zone)) rsc := dns.NewRecordSetsClient(d.config.SubscriptionID) rsc.Authorizer = d.authorizer @@ -186,7 +197,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { // Checks that azure has a zone for this domain name. func (d *DNSProvider) getHostedZoneID(ctx context.Context, fqdn string) (string, error) { - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return "", err } @@ -194,7 +205,7 @@ func (d *DNSProvider) getHostedZoneID(ctx context.Context, fqdn string) (string, dc := dns.NewZonesClient(d.config.SubscriptionID) dc.Authorizer = d.authorizer - zone, err := dc.Get(ctx, d.config.ResourceGroup, acme.UnFqdn(authZone)) + zone, err := dc.Get(ctx, d.config.ResourceGroup, dns01.UnFqdn(authZone)) if err != nil { return "", err } @@ -205,7 +216,7 @@ func (d *DNSProvider) getHostedZoneID(ctx context.Context, fqdn string) (string, // Returns the relative record to the domain func toRelativeRecord(domain, zone string) string { - return acme.UnFqdn(strings.TrimSuffix(domain, zone)) + return dns01.UnFqdn(strings.TrimSuffix(domain, zone)) } func getAuthorizer(config *Config) (autorest.Authorizer, error) { @@ -259,5 +270,5 @@ func getMetadata(config *Config, field string) (string, error) { return "", err } - return string(respBody[:]), nil + return string(respBody), nil } diff --git a/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go b/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go index fabfdae3b..3cee02fb5 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go +++ b/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go @@ -1,27 +1,25 @@ -// Package bluecat implements a DNS provider for solving the DNS-01 challenge -// using a self-hosted Bluecat Address Manager. +// Package bluecat implements a DNS provider for solving the DNS-01 challenge using a self-hosted Bluecat Address Manager. package bluecat import ( - "bytes" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" - "regexp" "strconv" - "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) -const configType = "Configuration" -const viewType = "View" -const txtType = "TXTRecord" -const zoneType = "Zone" +const ( + configType = "Configuration" + viewType = "View" + zoneType = "Zone" + txtType = "TXTRecord" +) // Config is used to configure the creation of the DNSProvider type Config struct { @@ -39,9 +37,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("BLUECAT_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("BLUECAT_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("BLUECAT_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("BLUECAT_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("BLUECAT_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("BLUECAT_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("BLUECAT_HTTP_TIMEOUT", 30*time.Second), }, @@ -58,8 +56,8 @@ type DNSProvider struct { // NewDNSProvider returns a DNSProvider instance configured for Bluecat DNS. // Credentials must be passed in the environment variables: BLUECAT_SERVER_URL, BLUECAT_USER_NAME and BLUECAT_PASSWORD. // BLUECAT_SERVER_URL should have the scheme, hostname, and port (if required) of the authoritative Bluecat BAM server. -// The REST endpoint will be appended. In addition, the Configuration name -// and external DNS View Name must be passed in BLUECAT_CONFIG_NAME and BLUECAT_DNS_VIEW +// The REST endpoint will be appended. +// In addition, the Configuration name and external DNS View Name must be passed in BLUECAT_CONFIG_NAME and BLUECAT_DNS_VIEW func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("BLUECAT_SERVER_URL", "BLUECAT_USER_NAME", "BLUECAT_PASSWORD", "BLUECAT_CONFIG_NAME", "BLUECAT_DNS_VIEW") if err != nil { @@ -76,24 +74,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Bluecat DNS. -// Deprecated -func NewDNSProviderCredentials(baseURL, userName, password, configName, dnsView string, httpClient *http.Client) (*DNSProvider, error) { - config := NewDefaultConfig() - config.BaseURL = baseURL - config.UserName = userName - config.Password = password - config.ConfigName = configName - config.DNSView = dnsView - - if httpClient != nil { - config.HTTPClient = httpClient - } - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Bluecat DNS. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -111,7 +91,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // This will *not* create a subzone to contain the TXT record, // so make sure the FQDN specified is within an extant zone. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) err := d.login() if err != nil { @@ -162,7 +142,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) err := d.login() if err != nil { @@ -219,223 +199,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -// Send a REST request, using query parameters specified. The Authorization -// header will be set if we have an active auth token -func (d *DNSProvider) sendRequest(method, resource string, payload interface{}, queryArgs map[string]string) (*http.Response, error) { - url := fmt.Sprintf("%s/Services/REST/v1/%s", d.config.BaseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, fmt.Errorf("bluecat: %v", err) - } - - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, fmt.Errorf("bluecat: %v", err) - } - req.Header.Set("Content-Type", "application/json") - if len(d.token) > 0 { - req.Header.Set("Authorization", d.token) - } - - // Add all query parameters - q := req.URL.Query() - for argName, argVal := range queryArgs { - q.Add(argName, argVal) - } - req.URL.RawQuery = q.Encode() - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("bluecat: %v", err) - } - - if resp.StatusCode >= 400 { - errBytes, _ := ioutil.ReadAll(resp.Body) - errResp := string(errBytes) - return nil, fmt.Errorf("bluecat: request failed with HTTP status code %d\n Full message: %s", - resp.StatusCode, errResp) - } - - return resp, nil -} - -// Starts a new Bluecat API Session. Authenticates using customerName, userName, -// password and receives a token to be used in for subsequent requests. -func (d *DNSProvider) login() error { - queryArgs := map[string]string{ - "username": d.config.UserName, - "password": d.config.Password, - } - - resp, err := d.sendRequest(http.MethodGet, "login", nil, queryArgs) - if err != nil { - return err - } - defer resp.Body.Close() - - authBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("bluecat: %v", err) - } - authResp := string(authBytes) - - if strings.Contains(authResp, "Authentication Error") { - msg := strings.Trim(authResp, "\"") - return fmt.Errorf("bluecat: request failed: %s", msg) - } - // Upon success, API responds with "Session Token-> BAMAuthToken: dQfuRMTUxNjc3MjcyNDg1ODppcGFybXM= <- for User : username" - d.token = regexp.MustCompile("BAMAuthToken: [^ ]+").FindString(authResp) - return nil -} - -// Destroys Bluecat Session -func (d *DNSProvider) logout() error { - if len(d.token) == 0 { - // nothing to do - return nil - } - - resp, err := d.sendRequest(http.MethodGet, "logout", nil, nil) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return fmt.Errorf("bluecat: request failed to delete session with HTTP status code %d", resp.StatusCode) - } - - authBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - authResp := string(authBytes) - - if !strings.Contains(authResp, "successfully") { - msg := strings.Trim(authResp, "\"") - return fmt.Errorf("bluecat: request failed to delete session: %s", msg) - } - - d.token = "" - - return nil -} - -// Lookup the entity ID of the configuration named in our properties -func (d *DNSProvider) lookupConfID() (uint, error) { - queryArgs := map[string]string{ - "parentId": strconv.Itoa(0), - "name": d.config.ConfigName, - "type": configType, - } - - resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) - if err != nil { - return 0, err - } - defer resp.Body.Close() - - var conf entityResponse - err = json.NewDecoder(resp.Body).Decode(&conf) - if err != nil { - return 0, fmt.Errorf("bluecat: %v", err) - } - return conf.ID, nil -} - -// Find the DNS view with the given name within -func (d *DNSProvider) lookupViewID(viewName string) (uint, error) { - confID, err := d.lookupConfID() - if err != nil { - return 0, err - } - - queryArgs := map[string]string{ - "parentId": strconv.FormatUint(uint64(confID), 10), - "name": viewName, - "type": viewType, - } - - resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) - if err != nil { - return 0, err - } - defer resp.Body.Close() - - var view entityResponse - err = json.NewDecoder(resp.Body).Decode(&view) - if err != nil { - return 0, fmt.Errorf("bluecat: %v", err) - } - - return view.ID, nil -} - -// Return the entityId of the parent zone by recursing from the root view -// Also return the simple name of the host -func (d *DNSProvider) lookupParentZoneID(viewID uint, fqdn string) (uint, string, error) { - parentViewID := viewID - name := "" - - if fqdn != "" { - zones := strings.Split(strings.Trim(fqdn, "."), ".") - last := len(zones) - 1 - name = zones[0] - - for i := last; i > -1; i-- { - zoneID, err := d.getZone(parentViewID, zones[i]) - if err != nil || zoneID == 0 { - return parentViewID, name, err - } - if i > 0 { - name = strings.Join(zones[0:i], ".") - } - parentViewID = zoneID - } - } - - return parentViewID, name, nil -} - -// Get the DNS zone with the specified name under the parentId -func (d *DNSProvider) getZone(parentID uint, name string) (uint, error) { - queryArgs := map[string]string{ - "parentId": strconv.FormatUint(uint64(parentID), 10), - "name": name, - "type": zoneType, - } - - resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) - // Return an empty zone if the named zone doesn't exist - if resp != nil && resp.StatusCode == 404 { - return 0, fmt.Errorf("bluecat: could not find zone named %s", name) - } - if err != nil { - return 0, err - } - defer resp.Body.Close() - - var zone entityResponse - err = json.NewDecoder(resp.Body).Decode(&zone) - if err != nil { - return 0, fmt.Errorf("bluecat: %v", err) - } - - return zone.ID, nil -} - -// Deploy the DNS config for the specified entity to the authoritative servers -func (d *DNSProvider) deploy(entityID uint) error { - queryArgs := map[string]string{ - "entityId": strconv.FormatUint(uint64(entityID), 10), - } - - resp, err := d.sendRequest(http.MethodPost, "quickDeploy", nil, queryArgs) - if err != nil { - return err - } - defer resp.Body.Close() - - return nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/bluecat/client.go b/vendor/github.com/xenolf/lego/providers/dns/bluecat/client.go index 55deeed45..d910594ce 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/bluecat/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/bluecat/client.go @@ -1,5 +1,16 @@ package bluecat +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "regexp" + "strconv" + "strings" +) + // JSON body for Bluecat entity requests and responses type bluecatEntity struct { ID string `json:"id,omitempty"` @@ -14,3 +25,225 @@ type entityResponse struct { Type string `json:"type"` Properties string `json:"properties"` } + +// Starts a new Bluecat API Session. Authenticates using customerName, userName, +// password and receives a token to be used in for subsequent requests. +func (d *DNSProvider) login() error { + queryArgs := map[string]string{ + "username": d.config.UserName, + "password": d.config.Password, + } + + resp, err := d.sendRequest(http.MethodGet, "login", nil, queryArgs) + if err != nil { + return err + } + defer resp.Body.Close() + + authBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("bluecat: %v", err) + } + authResp := string(authBytes) + + if strings.Contains(authResp, "Authentication Error") { + msg := strings.Trim(authResp, "\"") + return fmt.Errorf("bluecat: request failed: %s", msg) + } + + // Upon success, API responds with "Session Token-> BAMAuthToken: dQfuRMTUxNjc3MjcyNDg1ODppcGFybXM= <- for User : username" + d.token = regexp.MustCompile("BAMAuthToken: [^ ]+").FindString(authResp) + return nil +} + +// Destroys Bluecat Session +func (d *DNSProvider) logout() error { + if len(d.token) == 0 { + // nothing to do + return nil + } + + resp, err := d.sendRequest(http.MethodGet, "logout", nil, nil) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf("bluecat: request failed to delete session with HTTP status code %d", resp.StatusCode) + } + + authBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + authResp := string(authBytes) + + if !strings.Contains(authResp, "successfully") { + msg := strings.Trim(authResp, "\"") + return fmt.Errorf("bluecat: request failed to delete session: %s", msg) + } + + d.token = "" + + return nil +} + +// Lookup the entity ID of the configuration named in our properties +func (d *DNSProvider) lookupConfID() (uint, error) { + queryArgs := map[string]string{ + "parentId": strconv.Itoa(0), + "name": d.config.ConfigName, + "type": configType, + } + + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) + if err != nil { + return 0, err + } + defer resp.Body.Close() + + var conf entityResponse + err = json.NewDecoder(resp.Body).Decode(&conf) + if err != nil { + return 0, fmt.Errorf("bluecat: %v", err) + } + return conf.ID, nil +} + +// Find the DNS view with the given name within +func (d *DNSProvider) lookupViewID(viewName string) (uint, error) { + confID, err := d.lookupConfID() + if err != nil { + return 0, err + } + + queryArgs := map[string]string{ + "parentId": strconv.FormatUint(uint64(confID), 10), + "name": viewName, + "type": viewType, + } + + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) + if err != nil { + return 0, err + } + defer resp.Body.Close() + + var view entityResponse + err = json.NewDecoder(resp.Body).Decode(&view) + if err != nil { + return 0, fmt.Errorf("bluecat: %v", err) + } + + return view.ID, nil +} + +// Return the entityId of the parent zone by recursing from the root view +// Also return the simple name of the host +func (d *DNSProvider) lookupParentZoneID(viewID uint, fqdn string) (uint, string, error) { + parentViewID := viewID + name := "" + + if fqdn != "" { + zones := strings.Split(strings.Trim(fqdn, "."), ".") + last := len(zones) - 1 + name = zones[0] + + for i := last; i > -1; i-- { + zoneID, err := d.getZone(parentViewID, zones[i]) + if err != nil || zoneID == 0 { + return parentViewID, name, err + } + if i > 0 { + name = strings.Join(zones[0:i], ".") + } + parentViewID = zoneID + } + } + + return parentViewID, name, nil +} + +// Get the DNS zone with the specified name under the parentId +func (d *DNSProvider) getZone(parentID uint, name string) (uint, error) { + queryArgs := map[string]string{ + "parentId": strconv.FormatUint(uint64(parentID), 10), + "name": name, + "type": zoneType, + } + + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) + + // Return an empty zone if the named zone doesn't exist + if resp != nil && resp.StatusCode == http.StatusNotFound { + return 0, fmt.Errorf("bluecat: could not find zone named %s", name) + } + if err != nil { + return 0, err + } + defer resp.Body.Close() + + var zone entityResponse + err = json.NewDecoder(resp.Body).Decode(&zone) + if err != nil { + return 0, fmt.Errorf("bluecat: %v", err) + } + + return zone.ID, nil +} + +// Deploy the DNS config for the specified entity to the authoritative servers +func (d *DNSProvider) deploy(entityID uint) error { + queryArgs := map[string]string{ + "entityId": strconv.FormatUint(uint64(entityID), 10), + } + + resp, err := d.sendRequest(http.MethodPost, "quickDeploy", nil, queryArgs) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil +} + +// Send a REST request, using query parameters specified. The Authorization +// header will be set if we have an active auth token +func (d *DNSProvider) sendRequest(method, resource string, payload interface{}, queryArgs map[string]string) (*http.Response, error) { + url := fmt.Sprintf("%s/Services/REST/v1/%s", d.config.BaseURL, resource) + + body, err := json.Marshal(payload) + if err != nil { + return nil, fmt.Errorf("bluecat: %v", err) + } + + req, err := http.NewRequest(method, url, bytes.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("bluecat: %v", err) + } + req.Header.Set("Content-Type", "application/json") + if len(d.token) > 0 { + req.Header.Set("Authorization", d.token) + } + + // Add all query parameters + q := req.URL.Query() + for argName, argVal := range queryArgs { + q.Add(argName, argVal) + } + req.URL.RawQuery = q.Encode() + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("bluecat: %v", err) + } + + if resp.StatusCode >= 400 { + errBytes, _ := ioutil.ReadAll(resp.Body) + errResp := string(errBytes) + return nil, fmt.Errorf("bluecat: request failed with HTTP status code %d\n Full message: %s", + resp.StatusCode, errResp) + } + + return resp, nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go index 92c4b69fa..c530326a4 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go @@ -1,5 +1,4 @@ -// Package cloudflare implements a DNS provider for solving the DNS-01 -// challenge using cloudflare DNS. +// Package cloudflare implements a DNS provider for solving the DNS-01 challenge using cloudflare DNS. package cloudflare import ( @@ -9,14 +8,11 @@ import ( "time" "github.com/cloudflare/cloudflare-go" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) -// CloudFlareAPIURL represents the API endpoint to call. -const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4" // Deprecated - const ( minTTL = 120 ) @@ -67,17 +63,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Cloudflare. -// Deprecated -func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.AuthEmail = email - config.AuthKey = key - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Cloudflare. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -93,9 +78,6 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, err } - // TODO: must be remove. keep only for compatibility reason. - client.BaseURL = CloudFlareAPIURL - return &DNSProvider{client: client, config: config}, nil } @@ -107,21 +89,21 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("cloudflare: %v", err) } - zoneID, err := d.client.ZoneIDByName(acme.UnFqdn(authZone)) + zoneID, err := d.client.ZoneIDByName(dns01.UnFqdn(authZone)) if err != nil { return fmt.Errorf("cloudflare: failed to find zone %s: %v", authZone, err) } dnsRecord := cloudflare.DNSRecord{ Type: "TXT", - Name: acme.UnFqdn(fqdn), + Name: dns01.UnFqdn(fqdn), Content: value, TTL: d.config.TTL, } @@ -142,21 +124,21 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("cloudflare: %v", err) } - zoneID, err := d.client.ZoneIDByName(acme.UnFqdn(authZone)) + zoneID, err := d.client.ZoneIDByName(dns01.UnFqdn(authZone)) if err != nil { return fmt.Errorf("cloudflare: failed to find zone %s: %v", authZone, err) } dnsRecord := cloudflare.DNSRecord{ Type: "TXT", - Name: acme.UnFqdn(fqdn), + Name: dns01.UnFqdn(fqdn), } records, err := d.client.DNSRecords(zoneID, dnsRecord) diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go index 6f4bb6bd7..0a3a7d890 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go @@ -1,5 +1,4 @@ -// Package cloudxns implements a DNS provider for solving the DNS-01 challenge -// using CloudXNS DNS. +// Package cloudxns implements a DNS provider for solving the DNS-01 challenge using CloudXNS DNS. package cloudxns import ( @@ -8,8 +7,9 @@ import ( "net/http" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" + "github.com/xenolf/lego/providers/dns/cloudxns/internal" ) // Config is used to configure the creation of the DNSProvider @@ -24,21 +24,20 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { - client := acme.HTTPClient - client.Timeout = time.Second * time.Duration(env.GetOrDefaultInt("CLOUDXNS_HTTP_TIMEOUT", 30)) - return &Config{ - PropagationTimeout: env.GetOrDefaultSecond("CLOUDXNS_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("CLOUDXNS_POLLING_INTERVAL", acme.DefaultPollingInterval), - TTL: env.GetOrDefaultInt("CLOUDXNS_TTL", 120), - HTTPClient: &client, + PropagationTimeout: env.GetOrDefaultSecond("CLOUDXNS_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("CLOUDXNS_POLLING_INTERVAL", dns01.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("CLOUDXNS_TTL", dns01.DefaultTTL), + HTTPClient: &http.Client{ + Timeout: time.Second * time.Duration(env.GetOrDefaultInt("CLOUDXNS_HTTP_TIMEOUT", 30)), + }, } } // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { config *Config - client *Client + client *internal.Client } // NewDNSProvider returns a DNSProvider instance configured for CloudXNS. @@ -50,15 +49,9 @@ func NewDNSProvider() (*DNSProvider, error) { return nil, fmt.Errorf("CloudXNS: %v", err) } - return NewDNSProviderCredentials(values["CLOUDXNS_API_KEY"], values["CLOUDXNS_SECRET_KEY"]) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for CloudXNS. -func NewDNSProviderCredentials(apiKey, secretKey string) (*DNSProvider, error) { config := NewDefaultConfig() - config.APIKey = apiKey - config.SecretKey = secretKey + config.APIKey = values["CLOUDXNS_API_KEY"] + config.SecretKey = values["CLOUDXNS_SECRET_KEY"] return NewDNSProviderConfig(config) } @@ -69,7 +62,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, errors.New("CloudXNS: the configuration of the DNS provider is nil") } - client, err := NewClient(config.APIKey, config.SecretKey) + client, err := internal.NewClient(config.APIKey, config.SecretKey) if err != nil { return nil, err } @@ -81,7 +74,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) info, err := d.client.GetDomainInformation(fqdn) if err != nil { @@ -93,7 +86,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) info, err := d.client.GetDomainInformation(fqdn) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/client.go b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/internal/client.go similarity index 95% rename from vendor/github.com/xenolf/lego/providers/dns/cloudxns/client.go rename to vendor/github.com/xenolf/lego/providers/dns/cloudxns/internal/client.go index 4cf1dd201..fd3319a04 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/internal/client.go @@ -1,4 +1,4 @@ -package cloudxns +package internal import ( "bytes" @@ -12,7 +12,7 @@ import ( "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" ) const defaultBaseURL = "https://www.cloudxns.net/api2/" @@ -70,7 +70,7 @@ type Client struct { // GetDomainInformation Get domain name information for a FQDN func (c *Client) GetDomainInformation(fqdn string) (*Data, error) { - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return nil, err } @@ -111,7 +111,7 @@ func (c *Client) FindTxtRecord(zoneID, fqdn string) (*TXTRecord, error) { } for _, record := range records { - if record.Host == acme.UnFqdn(fqdn) && record.Type == "TXT" { + if record.Host == dns01.UnFqdn(fqdn) && record.Type == "TXT" { return &record, nil } } @@ -128,7 +128,7 @@ func (c *Client) AddTxtRecord(info *Data, fqdn, value string, ttl int) error { payload := TXTRecord{ ID: id, - Host: acme.UnFqdn(strings.TrimSuffix(fqdn, info.Domain)), + Host: dns01.UnFqdn(strings.TrimSuffix(fqdn, info.Domain)), Value: value, Type: "TXT", LineID: 1, diff --git a/vendor/github.com/xenolf/lego/providers/dns/conoha/conoha.go b/vendor/github.com/xenolf/lego/providers/dns/conoha/conoha.go index a872789f3..d2f937985 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/conoha/conoha.go +++ b/vendor/github.com/xenolf/lego/providers/dns/conoha/conoha.go @@ -1,5 +1,4 @@ -// Package conoha implements a DNS provider for solving the DNS-01 challenge -// using ConoHa DNS. +// Package conoha implements a DNS provider for solving the DNS-01 challenge using ConoHa DNS. package conoha import ( @@ -8,8 +7,9 @@ import ( "net/http" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" + "github.com/xenolf/lego/providers/dns/conoha/internal" ) // Config is used to configure the creation of the DNSProvider @@ -29,8 +29,8 @@ func NewDefaultConfig() *Config { return &Config{ Region: env.GetOrDefaultString("CONOHA_REGION", "tyo1"), TTL: env.GetOrDefaultInt("CONOHA_TTL", 60), - PropagationTimeout: env.GetOrDefaultSecond("CONOHA_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("CONOHA_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("CONOHA_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("CONOHA_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("CONOHA_HTTP_TIMEOUT", 30*time.Second), }, @@ -40,7 +40,7 @@ func NewDefaultConfig() *Config { // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { config *Config - client *Client + client *internal.Client } // NewDNSProvider returns a DNSProvider instance configured for ConoHa DNS. @@ -69,15 +69,15 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, errors.New("conoha: some credentials information are missing") } - auth := Auth{ + auth := internal.Auth{ TenantID: config.TenantID, - PasswordCredentials: PasswordCredentials{ + PasswordCredentials: internal.PasswordCredentials{ Username: config.Username, Password: config.Password, }, } - client, err := NewClient(config.Region, auth, config.HTTPClient) + client, err := internal.NewClient(config.Region, auth, config.HTTPClient) if err != nil { return nil, fmt.Errorf("conoha: failed to create client: %v", err) } @@ -87,9 +87,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return err } @@ -99,7 +99,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("conoha: failed to get domain ID: %v", err) } - record := Record{ + record := internal.Record{ Name: fqdn, Type: "TXT", Data: value, @@ -116,9 +116,9 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp clears ConoHa DNS TXT record func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/conoha/client.go b/vendor/github.com/xenolf/lego/providers/dns/conoha/internal/client.go similarity index 99% rename from vendor/github.com/xenolf/lego/providers/dns/conoha/client.go rename to vendor/github.com/xenolf/lego/providers/dns/conoha/internal/client.go index 3fa8b5bbb..3136a24d6 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/conoha/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/conoha/internal/client.go @@ -1,4 +1,4 @@ -package conoha +package internal import ( "bytes" diff --git a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/client.go b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/client.go index d2f6c95f8..ce0be0507 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/client.go @@ -1,26 +1,132 @@ package digitalocean +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + + "github.com/xenolf/lego/challenge/dns01" +) + const defaultBaseURL = "https://api.digitalocean.com" -// txtRecordRequest represents the request body to DO's API to make a TXT record -type txtRecordRequest struct { - RecordType string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - TTL int `json:"ttl"` -} - // txtRecordResponse represents a response from DO's API after making a TXT record type txtRecordResponse struct { - DomainRecord struct { - ID int `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - } `json:"domain_record"` + DomainRecord record `json:"domain_record"` } -type digitalOceanAPIError struct { +type record struct { + ID int `json:"id,omitempty"` + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + Data string `json:"data,omitempty"` + TTL int `json:"ttl,omitempty"` +} + +type apiError struct { ID string `json:"id"` Message string `json:"message"` } + +func (d *DNSProvider) removeTxtRecord(domain string, recordID int) error { + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) + if err != nil { + return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) + } + + reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", d.config.BaseURL, dns01.UnFqdn(authZone), recordID) + req, err := d.newRequest(http.MethodDelete, reqURL, nil) + if err != nil { + return err + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return readError(req, resp) + } + + return nil +} + +func (d *DNSProvider) addTxtRecord(domain, fqdn, value string) (*txtRecordResponse, error) { + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) + if err != nil { + return nil, fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) + } + + reqData := record{Type: "TXT", Name: fqdn, Data: value, TTL: d.config.TTL} + body, err := json.Marshal(reqData) + if err != nil { + return nil, err + } + + reqURL := fmt.Sprintf("%s/v2/domains/%s/records", d.config.BaseURL, dns01.UnFqdn(authZone)) + req, err := d.newRequest(http.MethodPost, reqURL, bytes.NewReader(body)) + if err != nil { + return nil, err + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return nil, readError(req, resp) + } + + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.New(toUnreadableBodyMessage(req, content)) + } + + // Everything looks good; but we'll need the ID later to delete the record + respData := &txtRecordResponse{} + err = json.Unmarshal(content, respData) + if err != nil { + return nil, fmt.Errorf("%v: %s", err, toUnreadableBodyMessage(req, content)) + } + + return respData, nil +} + +func (d *DNSProvider) newRequest(method, reqURL string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, reqURL, body) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.config.AuthToken)) + + return req, nil +} + +func readError(req *http.Request, resp *http.Response) error { + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return errors.New(toUnreadableBodyMessage(req, content)) + } + + var errInfo apiError + err = json.Unmarshal(content, &errInfo) + if err != nil { + return fmt.Errorf("apiError unmarshaling error: %v: %s", err, toUnreadableBodyMessage(req, content)) + } + + return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) +} + +func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string { + return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody)) +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go index 97620dbf3..0f9f3e770 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go +++ b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go @@ -1,19 +1,14 @@ -// Package digitalocean implements a DNS provider for solving the DNS-01 -// challenge using digitalocean DNS. +// Package digitalocean implements a DNS provider for solving the DNS-01 challenge using digitalocean DNS. package digitalocean import ( - "bytes" - "encoding/json" "errors" "fmt" - "io" - "io/ioutil" "net/http" "sync" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -63,16 +58,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Digital Ocean. -// Deprecated -func NewDNSProviderCredentials(apiAuthToken string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.AuthToken = apiAuthToken - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Digital Ocean. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -101,7 +86,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) respData, err := d.addTxtRecord(domain, fqdn, value) if err != nil { @@ -117,7 +102,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() @@ -139,102 +124,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } - -func (d *DNSProvider) removeTxtRecord(domain string, recordID int) error { - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) - } - - reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", d.config.BaseURL, acme.UnFqdn(authZone), recordID) - req, err := d.newRequest(http.MethodDelete, reqURL, nil) - if err != nil { - return err - } - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return readError(req, resp) - } - - return nil -} - -func (d *DNSProvider) addTxtRecord(domain, fqdn, value string) (*txtRecordResponse, error) { - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return nil, fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) - } - - reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value, TTL: d.config.TTL} - body, err := json.Marshal(reqData) - if err != nil { - return nil, err - } - - reqURL := fmt.Sprintf("%s/v2/domains/%s/records", d.config.BaseURL, acme.UnFqdn(authZone)) - req, err := d.newRequest(http.MethodPost, reqURL, bytes.NewReader(body)) - if err != nil { - return nil, err - } - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return nil, readError(req, resp) - } - - content, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, errors.New(toUnreadableBodyMessage(req, content)) - } - - // Everything looks good; but we'll need the ID later to delete the record - respData := &txtRecordResponse{} - err = json.Unmarshal(content, respData) - if err != nil { - return nil, fmt.Errorf("%v: %s", err, toUnreadableBodyMessage(req, content)) - } - - return respData, nil -} - -func (d *DNSProvider) newRequest(method, reqURL string, body io.Reader) (*http.Request, error) { - req, err := http.NewRequest(method, reqURL, body) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.config.AuthToken)) - - return req, nil -} - -func readError(req *http.Request, resp *http.Response) error { - content, err := ioutil.ReadAll(resp.Body) - if err != nil { - return errors.New(toUnreadableBodyMessage(req, content)) - } - - var errInfo digitalOceanAPIError - err = json.Unmarshal(content, &errInfo) - if err != nil { - return fmt.Errorf("digitalOceanAPIError unmarshaling error: %v: %s", err, toUnreadableBodyMessage(req, content)) - } - - return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) -} - -func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string { - return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody)) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go index 604137d56..3b661f658 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go @@ -3,7 +3,8 @@ package dns import ( "fmt" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/providers/dns/acmedns" "github.com/xenolf/lego/providers/dns/alidns" "github.com/xenolf/lego/providers/dns/auroradns" @@ -56,7 +57,7 @@ import ( ) // NewDNSChallengeProviderByName Factory for DNS providers -func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) { +func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { switch name { case "acme-dns": return acmedns.NewDNSProvider() @@ -119,7 +120,7 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) case "linodev4": return linodev4.NewDNSProvider() case "manual": - return acme.NewDNSProviderManual() + return dns01.NewDNSProviderManual() case "mydnsjp": return mydnsjp.NewDNSProvider() case "namecheap": diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go b/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go index 8f52cac6b..3709b84fb 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go @@ -1,8 +1,8 @@ -// Package dnsimple implements a DNS provider for solving the DNS-01 challenge -// using dnsimple DNS. +// Package dnsimple implements a DNS provider for solving the DNS-01 challenge using dnsimple DNS. package dnsimple import ( + "context" "errors" "fmt" "strconv" @@ -10,8 +10,9 @@ import ( "time" "github.com/dnsimple/dnsimple-go/dnsimple" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" + "golang.org/x/oauth2" ) // Config is used to configure the creation of the DNSProvider @@ -26,9 +27,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("DNSIMPLE_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("DNSIMPLE_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("DNSIMPLE_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("DNSIMPLE_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("DNSIMPLE_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DNSIMPLE_POLLING_INTERVAL", dns01.DefaultPollingInterval), } } @@ -50,17 +51,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for DNSimple. -// Deprecated -func NewDNSProviderCredentials(accessToken, baseURL string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.AccessToken = accessToken - config.BaseURL = baseURL - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for DNSimple. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -71,8 +61,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, fmt.Errorf("dnsimple: OAuth token is missing") } - client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(config.AccessToken)) - client.UserAgent = acme.UserAgent + ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: config.AccessToken}) + client := dnsimple.NewClient(oauth2.NewClient(context.Background(), ts)) if config.BaseURL != "" { client.BaseURL = config.BaseURL @@ -83,7 +73,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zoneName, err := d.getHostedZone(domain) if err != nil { @@ -106,7 +96,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) records, err := d.findTxtRecords(domain, fqdn) if err != nil { @@ -136,7 +126,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { } func (d *DNSProvider) getHostedZone(domain string) (string, error) { - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return "", err } @@ -146,7 +136,7 @@ func (d *DNSProvider) getHostedZone(domain string) (string, error) { return "", err } - zoneName := acme.UnFqdn(authZone) + zoneName := dns01.UnFqdn(authZone) zones, err := d.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName}) if err != nil { @@ -200,7 +190,7 @@ func newTxtRecord(zoneName, fqdn, value string, ttl int) dnsimple.ZoneRecord { } func extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go index a55ea3978..712c3e75d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go @@ -9,8 +9,9 @@ import ( "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" + "github.com/xenolf/lego/providers/dns/dnsmadeeasy/internal" ) // Config is used to configure the creation of the DNSProvider @@ -28,9 +29,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("DNSMADEEASY_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("DNSMADEEASY_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("DNSMADEEASY_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("DNSMADEEASY_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("DNSMADEEASY_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DNSMADEEASY_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("DNSMADEEASY_HTTP_TIMEOUT", 10*time.Second), Transport: &http.Transport{ @@ -44,7 +45,7 @@ func NewDefaultConfig() *Config { // DNSMadeEasy's DNS API to manage TXT records for a domain. type DNSProvider struct { config *Config - client *Client + client *internal.Client } // NewDNSProvider returns a DNSProvider instance configured for DNSMadeEasy DNS. @@ -64,18 +65,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for DNS Made Easy. -// Deprecated -func NewDNSProviderCredentials(baseURL, apiKey, apiSecret string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.BaseURL = baseURL - config.APIKey = apiKey - config.APISecret = apiSecret - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for DNS Made Easy. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -93,7 +82,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { } } - client, err := NewClient(config.APIKey, config.APISecret) + client, err := internal.NewClient(config.APIKey, config.APISecret) if err != nil { return nil, fmt.Errorf("dnsmadeeasy: %v", err) } @@ -109,9 +98,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domainName, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domainName, keyAuth) + fqdn, value := dns01.GetRecord(domainName, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("dnsmadeeasy: unable to find zone for %s: %v", fqdn, err) } @@ -124,7 +113,7 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error { // create the TXT record name := strings.Replace(fqdn, "."+authZone, "", 1) - record := &Record{Type: "TXT", Name: name, Value: value, TTL: d.config.TTL} + record := &internal.Record{Type: "TXT", Name: name, Value: value, TTL: d.config.TTL} err = d.client.CreateRecord(domain, record) if err != nil { @@ -135,9 +124,9 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error { // CleanUp removes the TXT records matching the specified parameters func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domainName, keyAuth) + fqdn, _ := dns01.GetRecord(domainName, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("dnsmadeeasy: unable to find zone for %s: %v", fqdn, err) } diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/internal/client.go similarity index 99% rename from vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go rename to vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/internal/client.go index 95f2dda05..748d385d8 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/internal/client.go @@ -1,4 +1,4 @@ -package dnsmadeeasy +package internal import ( "bytes" diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go b/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go index 1aa8d9e97..fb8afcbdb 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go @@ -1,5 +1,4 @@ -// Package dnspod implements a DNS provider for solving the DNS-01 challenge -// using dnspod DNS. +// Package dnspod implements a DNS provider for solving the DNS-01 challenge using dnspod DNS. package dnspod import ( @@ -11,7 +10,7 @@ import ( "time" "github.com/decker502/dnspod-go" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -28,8 +27,8 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ TTL: env.GetOrDefaultInt("DNSPOD_TTL", 600), - PropagationTimeout: env.GetOrDefaultSecond("DNSPOD_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("DNSPOD_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("DNSPOD_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DNSPOD_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("DNSPOD_HTTP_TIMEOUT", 0), }, @@ -56,16 +55,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for dnspod. -// Deprecated -func NewDNSProviderCredentials(key string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.LoginToken = key - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for dnspod. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -86,7 +75,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zoneID, zoneName, err := d.getHostedZone(domain) if err != nil { return err @@ -103,7 +92,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) records, err := d.findTxtRecords(domain, fqdn) if err != nil { @@ -136,14 +125,14 @@ func (d *DNSProvider) getHostedZone(domain string) (string, string, error) { return "", "", fmt.Errorf("API call failed: %v", err) } - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return "", "", err } var hostedZone dnspod.Domain for _, zone := range zones { - if zone.Name == acme.UnFqdn(authZone) { + if zone.Name == dns01.UnFqdn(authZone) { hostedZone = zone } } @@ -192,7 +181,7 @@ func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, erro } func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } diff --git a/vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go b/vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go index 9edd27b72..6b4dd2f6f 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go @@ -9,7 +9,7 @@ import ( "net/http" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -72,8 +72,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - record := acme.UnFqdn(fqdn) + fqdn, value := dns01.GetRecord(domain, keyAuth) + record := dns01.UnFqdn(fqdn) u, err := d.buildQuery(cmdAddRecord, record, value) if err != nil { @@ -89,8 +89,8 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp clears DreamHost TXT record func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - record := acme.UnFqdn(fqdn) + fqdn, value := dns01.GetRecord(domain, keyAuth) + record := dns01.UnFqdn(fqdn) u, err := d.buildQuery(cmdRemoveRecord, record, value) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/duckdns/client.go b/vendor/github.com/xenolf/lego/providers/dns/duckdns/client.go new file mode 100644 index 000000000..b7e6cae4e --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/duckdns/client.go @@ -0,0 +1,68 @@ +package duckdns + +import ( + "fmt" + "io/ioutil" + "net/url" + "strconv" + "strings" + + "github.com/miekg/dns" + "github.com/xenolf/lego/challenge/dns01" +) + +// updateTxtRecord Update the domains TXT record +// To update the TXT record we just need to make one simple get request. +// In DuckDNS you only have one TXT record shared with the domain and all sub domains. +func (d *DNSProvider) updateTxtRecord(domain, token, txt string, clear bool) error { + u, _ := url.Parse("https://www.duckdns.org/update") + + mainDomain := getMainDomain(domain) + if len(mainDomain) == 0 { + return fmt.Errorf("unable to find the main domain for: %s", domain) + } + + query := u.Query() + query.Set("domains", mainDomain) + query.Set("token", token) + query.Set("clear", strconv.FormatBool(clear)) + query.Set("txt", txt) + u.RawQuery = query.Encode() + + response, err := d.config.HTTPClient.Get(u.String()) + if err != nil { + return err + } + defer response.Body.Close() + + bodyBytes, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + + body := string(bodyBytes) + if body != "OK" { + return fmt.Errorf("request to change TXT record for DuckDNS returned the following result (%s) this does not match expectation (OK) used url [%s]", body, u) + } + return nil +} + +// DuckDNS only lets you write to your subdomain +// so it must be in format subdomain.duckdns.org +// not in format subsubdomain.subdomain.duckdns.org +// so strip off everything that is not top 3 levels +func getMainDomain(domain string) string { + domain = dns01.UnFqdn(domain) + + split := dns.Split(domain) + if strings.HasSuffix(strings.ToLower(domain), "duckdns.org") { + if len(split) < 3 { + return "" + } + + firstSubDomainIndex := split[len(split)-3] + return domain[firstSubDomainIndex:] + } + + return domain[split[len(split)-1]:] +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go b/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go index 7581af173..bd13be198 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go @@ -5,15 +5,10 @@ package duckdns import ( "errors" "fmt" - "io/ioutil" "net/http" - "net/url" - "strconv" - "strings" "time" - "github.com/miekg/dns" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -22,18 +17,19 @@ type Config struct { Token string PropagationTimeout time.Duration PollingInterval time.Duration + SequenceInterval time.Duration HTTPClient *http.Client } // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { - client := acme.HTTPClient - client.Timeout = env.GetOrDefaultSecond("DUCKDNS_HTTP_TIMEOUT", 30*time.Second) - return &Config{ - PropagationTimeout: env.GetOrDefaultSecond("DUCKDNS_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("DUCKDNS_POLLING_INTERVAL", acme.DefaultPollingInterval), - HTTPClient: &client, + PropagationTimeout: env.GetOrDefaultSecond("DUCKDNS_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DUCKDNS_POLLING_INTERVAL", dns01.DefaultPollingInterval), + SequenceInterval: env.GetOrDefaultSecond("DUCKDNS_SEQUENCE_INTERVAL", dns01.DefaultPropagationTimeout), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("DUCKDNS_HTTP_TIMEOUT", 30*time.Second), + }, } } @@ -56,16 +52,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for http://duckdns.org -// Deprecated -func NewDNSProviderCredentials(token string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.Token = token - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for DuckDNS. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -81,13 +67,13 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - _, txtRecord, _ := acme.DNS01Record(domain, keyAuth) - return updateTxtRecord(domain, d.config.Token, txtRecord, false) + _, txtRecord := dns01.GetRecord(domain, keyAuth) + return d.updateTxtRecord(domain, d.config.Token, txtRecord, false) } // CleanUp clears DuckDNS TXT record func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - return updateTxtRecord(domain, d.config.Token, "", true) + return d.updateTxtRecord(domain, d.config.Token, "", true) } // Timeout returns the timeout and interval to use when checking for DNS propagation. @@ -96,53 +82,8 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } -// updateTxtRecord Update the domains TXT record -// To update the TXT record we just need to make one simple get request. -// In DuckDNS you only have one TXT record shared with the domain and all sub domains. -func updateTxtRecord(domain, token, txt string, clear bool) error { - u, _ := url.Parse("https://www.duckdns.org/update") - - query := u.Query() - query.Set("domains", getMainDomain(domain)) - query.Set("token", token) - query.Set("clear", strconv.FormatBool(clear)) - query.Set("txt", txt) - u.RawQuery = query.Encode() - - response, err := acme.HTTPClient.Get(u.String()) - if err != nil { - return err - } - defer response.Body.Close() - - bodyBytes, err := ioutil.ReadAll(response.Body) - if err != nil { - return err - } - - body := string(bodyBytes) - if body != "OK" { - return fmt.Errorf("request to change TXT record for DuckDNS returned the following result (%s) this does not match expectation (OK) used url [%s]", body, u) - } - return nil -} - -// DuckDNS only lets you write to your subdomain -// so it must be in format subdomain.duckdns.org -// not in format subsubdomain.subdomain.duckdns.org -// so strip off everything that is not top 3 levels -func getMainDomain(domain string) string { - domain = acme.UnFqdn(domain) - - split := dns.Split(domain) - if strings.HasSuffix(strings.ToLower(domain), "duckdns.org") { - if len(split) < 3 { - return "" - } - - firstSubDomainIndex := split[len(split)-3] - return domain[firstSubDomainIndex:] - } - - return domain[split[len(split)-1]:] +// Sequential All DNS challenges for this provider will be resolved sequentially. +// Returns the interval between each iteration. +func (d *DNSProvider) Sequential() time.Duration { + return d.config.SequenceInterval } diff --git a/vendor/github.com/xenolf/lego/providers/dns/dyn/client.go b/vendor/github.com/xenolf/lego/providers/dns/dyn/client.go index 1ca7b8e56..f7e6cee84 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dyn/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dyn/client.go @@ -1,6 +1,11 @@ package dyn -import "encoding/json" +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) const defaultBaseURL = "https://api.dynect.net/REST" @@ -18,7 +23,7 @@ type dynResponse struct { Messages json.RawMessage `json:"msgs"` } -type creds struct { +type credentials struct { Customer string `json:"customer_name"` User string `json:"user_name"` Pass string `json:"password"` @@ -33,3 +38,109 @@ type publish struct { Publish bool `json:"publish"` Notes string `json:"notes"` } + +// Starts a new Dyn API Session. Authenticates using customerName, userName, +// password and receives a token to be used in for subsequent requests. +func (d *DNSProvider) login() error { + payload := &credentials{Customer: d.config.CustomerName, User: d.config.UserName, Pass: d.config.Password} + dynRes, err := d.sendRequest(http.MethodPost, "Session", payload) + if err != nil { + return err + } + + var s session + err = json.Unmarshal(dynRes.Data, &s) + if err != nil { + return err + } + + d.token = s.Token + + return nil +} + +// Destroys Dyn Session +func (d *DNSProvider) logout() error { + if len(d.token) == 0 { + // nothing to do + return nil + } + + url := fmt.Sprintf("%s/Session", defaultBaseURL) + req, err := http.NewRequest(http.MethodDelete, url, nil) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Auth-Token", d.token) + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return err + } + resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("API request failed to delete session with HTTP status code %d", resp.StatusCode) + } + + d.token = "" + + return nil +} + +func (d *DNSProvider) publish(zone, notes string) error { + pub := &publish{Publish: true, Notes: notes} + resource := fmt.Sprintf("Zone/%s/", zone) + + _, err := d.sendRequest(http.MethodPut, resource, pub) + return err +} + +func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (*dynResponse, error) { + url := fmt.Sprintf("%s/%s", defaultBaseURL, resource) + + body, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + if len(d.token) > 0 { + req.Header.Set("Auth-Token", d.token) + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 500 { + return nil, fmt.Errorf("API request failed with HTTP status code %d", resp.StatusCode) + } + + var dynRes dynResponse + err = json.NewDecoder(resp.Body).Decode(&dynRes) + if err != nil { + return nil, err + } + + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("API request failed with HTTP status code %d: %s", resp.StatusCode, dynRes.Messages) + } else if resp.StatusCode == 307 { + // TODO add support for HTTP 307 response and long running jobs + return nil, fmt.Errorf("API request returned HTTP 307. This is currently unsupported") + } + + if dynRes.Status == "failure" { + // TODO add better error handling + return nil, fmt.Errorf("API request failed: %s", dynRes.Messages) + } + + return &dynRes, nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go b/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go index 8ec7753f5..40fdc19f8 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go @@ -1,17 +1,14 @@ -// Package dyn implements a DNS provider for solving the DNS-01 challenge -// using Dyn Managed DNS. +// Package dyn implements a DNS provider for solving the DNS-01 challenge using Dyn Managed DNS. package dyn import ( - "bytes" - "encoding/json" "errors" "fmt" "net/http" "strconv" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -29,9 +26,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("DYN_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("DYN_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("DYN_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("DYN_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("DYN_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DYN_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("DYN_HTTP_TIMEOUT", 10*time.Second), }, @@ -62,18 +59,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Dyn DNS. -// Deprecated -func NewDNSProviderCredentials(customerName, userName, password string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.CustomerName = customerName - config.UserName = userName - config.Password = password - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Dyn DNS func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -89,9 +74,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("dyn: %v", err) } @@ -124,9 +109,9 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("dyn: %v", err) } @@ -170,109 +155,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -// Starts a new Dyn API Session. Authenticates using customerName, userName, -// password and receives a token to be used in for subsequent requests. -func (d *DNSProvider) login() error { - payload := &creds{Customer: d.config.CustomerName, User: d.config.UserName, Pass: d.config.Password} - dynRes, err := d.sendRequest(http.MethodPost, "Session", payload) - if err != nil { - return err - } - - var s session - err = json.Unmarshal(dynRes.Data, &s) - if err != nil { - return err - } - - d.token = s.Token - - return nil -} - -// Destroys Dyn Session -func (d *DNSProvider) logout() error { - if len(d.token) == 0 { - // nothing to do - return nil - } - - url := fmt.Sprintf("%s/Session", defaultBaseURL) - req, err := http.NewRequest(http.MethodDelete, url, nil) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Auth-Token", d.token) - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return err - } - resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("API request failed to delete session with HTTP status code %d", resp.StatusCode) - } - - d.token = "" - - return nil -} - -func (d *DNSProvider) publish(zone, notes string) error { - pub := &publish{Publish: true, Notes: notes} - resource := fmt.Sprintf("Zone/%s/", zone) - - _, err := d.sendRequest(http.MethodPut, resource, pub) - return err -} - -func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (*dynResponse, error) { - url := fmt.Sprintf("%s/%s", defaultBaseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - if len(d.token) > 0 { - req.Header.Set("Auth-Token", d.token) - } - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 500 { - return nil, fmt.Errorf("API request failed with HTTP status code %d", resp.StatusCode) - } - - var dynRes dynResponse - err = json.NewDecoder(resp.Body).Decode(&dynRes) - if err != nil { - return nil, err - } - - if resp.StatusCode >= 400 { - return nil, fmt.Errorf("API request failed with HTTP status code %d: %s", resp.StatusCode, dynRes.Messages) - } else if resp.StatusCode == 307 { - // TODO add support for HTTP 307 response and long running jobs - return nil, fmt.Errorf("API request returned HTTP 307. This is currently unsupported") - } - - if dynRes.Status == "failure" { - // TODO add better error handling - return nil, fmt.Errorf("API request failed: %s", dynRes.Messages) - } - - return &dynRes, nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/exec/doc.go b/vendor/github.com/xenolf/lego/providers/dns/exec/doc.go deleted file mode 100644 index 9aa53fccc..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/exec/doc.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Package exec implements a manual DNS provider which runs a program for adding/removing the DNS record. - -The file name of the external program is specified in the environment variable `EXEC_PATH`. -When it is run by lego, three command-line parameters are passed to it: -The action ("present" or "cleanup"), the fully-qualified domain name, the value for the record and the TTL. - -For example, requesting a certificate for the domain 'foo.example.com' can be achieved by calling lego as follows: - - EXEC_PATH=./update-dns.sh \ - lego --dns exec \ - --domains foo.example.com \ - --email invalid@example.com run - -It will then call the program './update-dns.sh' with like this: - - ./update-dns.sh "present" "_acme-challenge.foo.example.com." "MsijOYZxqyjGnFGwhjrhfg-Xgbl5r68WPda0J9EgqqI" "120" - -The program then needs to make sure the record is inserted. -When it returns an error via a non-zero exit code, lego aborts. - -When the record is to be removed again, -the program is called with the first command-line parameter set to "cleanup" instead of "present". - -If you want to use the raw domain, token, and keyAuth values with your program, you can set `EXEC_MODE=RAW`: - - EXEC_MODE=RAW \ - EXEC_PATH=./update-dns.sh \ - lego --dns exec \ - --domains foo.example.com \ - --email invalid@example.com run - -It will then call the program './update-dns.sh' like this: - - ./update-dns.sh "present" "foo.example.com." "--" "some-token" "KxAy-J3NwUmg9ZQuM-gP_Mq1nStaYSaP9tYQs5_-YsE.ksT-qywTd8058G-SHHWA3RAN72Pr0yWtPYmmY5UBpQ8" - -NOTE: -The `--` is because the token MAY start with a `-`, and the called program may try and interpret a - as indicating a flag. -In the case of urfave, which is commonly used, -you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely. -*/ -package exec diff --git a/vendor/github.com/xenolf/lego/providers/dns/exec/exec.go b/vendor/github.com/xenolf/lego/providers/dns/exec/exec.go index b8ff0c7da..dc0467e16 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/exec/exec.go +++ b/vendor/github.com/xenolf/lego/providers/dns/exec/exec.go @@ -1,3 +1,4 @@ +// Package exec implements a DNS provider which runs a program for adding/removing the DNS record. package exec import ( @@ -5,17 +6,27 @@ import ( "fmt" "os" "os/exec" - "strconv" + "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) // Config Provider configuration. type Config struct { - Program string - Mode string + Program string + Mode string + PropagationTimeout time.Duration + PollingInterval time.Duration +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + PropagationTimeout: env.GetOrDefaultSecond("EXEC_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("EXEC_POLLING_INTERVAL", dns01.DefaultPollingInterval), + } } // DNSProvider adds and removes the record for the DNS challenge by calling a @@ -32,10 +43,11 @@ func NewDNSProvider() (*DNSProvider, error) { return nil, fmt.Errorf("exec: %v", err) } - return NewDNSProviderConfig(&Config{ - Program: values["EXEC_PATH"], - Mode: os.Getenv("EXEC_MODE"), - }) + config := NewDefaultConfig() + config.Program = values["EXEC_PATH"] + config.Mode = os.Getenv("EXEC_MODE") + + return NewDNSProviderConfig(config) } // NewDNSProviderConfig returns a new DNS provider which runs the given configuration @@ -48,25 +60,14 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{config: config}, nil } -// NewDNSProviderProgram returns a new DNS provider which runs the given program -// for adding and removing the DNS record. -// Deprecated: use NewDNSProviderConfig instead -func NewDNSProviderProgram(program string) (*DNSProvider, error) { - if len(program) == 0 { - return nil, errors.New("the program is undefined") - } - - return NewDNSProviderConfig(&Config{Program: program}) -} - // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { var args []string if d.config.Mode == "RAW" { args = []string{"present", "--", domain, token, keyAuth} } else { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - args = []string{"present", fqdn, value, strconv.Itoa(ttl)} + fqdn, value := dns01.GetRecord(domain, keyAuth) + args = []string{"present", fqdn, value} } cmd := exec.Command(d.config.Program, args...) @@ -85,8 +86,8 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { if d.config.Mode == "RAW" { args = []string{"cleanup", "--", domain, token, keyAuth} } else { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - args = []string{"cleanup", fqdn, value, strconv.Itoa(ttl)} + fqdn, value := dns01.GetRecord(domain, keyAuth) + args = []string{"cleanup", fqdn, value} } cmd := exec.Command(d.config.Program, args...) @@ -98,3 +99,9 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return err } + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go index 4ffcc6e71..10a7a592a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go +++ b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go @@ -1,5 +1,4 @@ -// Package exoscale implements a DNS provider for solving the DNS-01 challenge -// using exoscale DNS. +// Package exoscale implements a DNS provider for solving the DNS-01 challenge using exoscale DNS. package exoscale import ( @@ -9,7 +8,7 @@ import ( "time" "github.com/exoscale/egoscale" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -29,9 +28,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("EXOSCALE_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("EXOSCALE_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("EXOSCALE_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("EXOSCALE_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("EXOSCALE_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("EXOSCALE_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("EXOSCALE_HTTP_TIMEOUT", 0), }, @@ -60,18 +59,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderClient Uses the supplied parameters -// to return a DNSProvider instance configured for Exoscale. -// Deprecated -func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIKey = key - config.APISecret = secret - config.Endpoint = endpoint - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Exoscale. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -94,7 +81,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain) if err != nil { return err @@ -137,7 +124,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain) if err != nil { return err @@ -181,12 +168,12 @@ func (d *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, erro // FindZoneAndRecordName Extract DNS zone and DNS entry name func (d *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) { - zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + zone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return "", "", err } - zone = acme.UnFqdn(zone) - name := acme.UnFqdn(fqdn) + zone = dns01.UnFqdn(zone) + name := dns01.UnFqdn(fqdn) name = name[:len(name)-len("."+zone)] return zone, name, nil diff --git a/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go b/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go index 9ef6cb0ef..a8d94f5d2 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go @@ -1,3 +1,4 @@ +// Package fastdns implements a DNS provider for solving the DNS-01 challenge using FastDNS. package fastdns import ( @@ -8,7 +9,7 @@ import ( configdns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1" "github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -23,9 +24,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - PropagationTimeout: env.GetOrDefaultSecond("AKAMAI_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("AKAMAI_POLLING_INTERVAL", acme.DefaultPollingInterval), - TTL: env.GetOrDefaultInt("AKAMAI_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("AKAMAI_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("AKAMAI_POLLING_INTERVAL", dns01.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("AKAMAI_TTL", dns01.DefaultTTL), } } @@ -54,22 +55,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderClient uses the supplied parameters -// to return a DNSProvider instance configured for FastDNS. -// Deprecated -func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.Config = edgegrid.Config{ - Host: host, - ClientToken: clientToken, - ClientSecret: clientSecret, - AccessToken: accessToken, - MaxBody: 131072, - } - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for FastDNS. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -85,7 +70,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fullfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain) if err != nil { return fmt.Errorf("fastdns: %v", err) @@ -121,7 +106,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain) if err != nil { return fmt.Errorf("fastdns: %v", err) @@ -154,12 +139,12 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { } func (d *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) { - zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + zone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return "", "", err } - zone = acme.UnFqdn(zone) - name := acme.UnFqdn(fqdn) + zone = dns01.UnFqdn(zone) + name := dns01.UnFqdn(fqdn) name = name[:len(name)-len("."+zone)] return zone, name, nil diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandi/client.go b/vendor/github.com/xenolf/lego/providers/dns/gandi/client.go index 0ed0bee44..901a78947 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gandi/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gandi/client.go @@ -1,8 +1,11 @@ package gandi import ( + "bytes" "encoding/xml" "fmt" + "io" + "io/ioutil" ) // types for XML-RPC method calls and parameters @@ -90,3 +93,224 @@ type rpcError struct { func (e rpcError) Error() string { return fmt.Sprintf("Gandi DNS: RPC Error: (%d) %s", e.faultCode, e.faultString) } + +// rpcCall makes an XML-RPC call to Gandi's RPC endpoint by +// marshaling the data given in the call argument to XML and sending +// that via HTTP Post to Gandi. +// The response is then unmarshalled into the resp argument. +func (d *DNSProvider) rpcCall(call *methodCall, resp response) error { + // marshal + b, err := xml.MarshalIndent(call, "", " ") + if err != nil { + return fmt.Errorf("marshal error: %v", err) + } + + // post + b = append([]byte(``+"\n"), b...) + respBody, err := d.httpPost(d.config.BaseURL, "text/xml", bytes.NewReader(b)) + if err != nil { + return err + } + + // unmarshal + err = xml.Unmarshal(respBody, resp) + if err != nil { + return fmt.Errorf("unmarshal error: %v", err) + } + if resp.faultCode() != 0 { + return rpcError{ + faultCode: resp.faultCode(), faultString: resp.faultString()} + } + return nil +} + +// functions to perform API actions + +func (d *DNSProvider) getZoneID(domain string) (int, error) { + resp := &responseStruct{} + err := d.rpcCall(&methodCall{ + MethodName: "domain.info", + Params: []param{ + paramString{Value: d.config.APIKey}, + paramString{Value: domain}, + }, + }, resp) + if err != nil { + return 0, err + } + + var zoneID int + for _, member := range resp.StructMembers { + if member.Name == "zone_id" { + zoneID = member.ValueInt + } + } + + if zoneID == 0 { + return 0, fmt.Errorf("could not determine zone_id for %s", domain) + } + return zoneID, nil +} + +func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { + resp := &responseStruct{} + err := d.rpcCall(&methodCall{ + MethodName: "domain.zone.clone", + Params: []param{ + paramString{Value: d.config.APIKey}, + paramInt{Value: zoneID}, + paramInt{Value: 0}, + paramStruct{ + StructMembers: []structMember{ + structMemberString{ + Name: "name", + Value: name, + }}, + }, + }, + }, resp) + if err != nil { + return 0, err + } + + var newZoneID int + for _, member := range resp.StructMembers { + if member.Name == "id" { + newZoneID = member.ValueInt + } + } + + if newZoneID == 0 { + return 0, fmt.Errorf("could not determine cloned zone_id") + } + return newZoneID, nil +} + +func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { + resp := &responseInt{} + err := d.rpcCall(&methodCall{ + MethodName: "domain.zone.version.new", + Params: []param{ + paramString{Value: d.config.APIKey}, + paramInt{Value: zoneID}, + }, + }, resp) + if err != nil { + return 0, err + } + + if resp.Value == 0 { + return 0, fmt.Errorf("could not create new zone version") + } + return resp.Value, nil +} + +func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value string, ttl int) error { + resp := &responseStruct{} + err := d.rpcCall(&methodCall{ + MethodName: "domain.zone.record.add", + Params: []param{ + paramString{Value: d.config.APIKey}, + paramInt{Value: zoneID}, + paramInt{Value: version}, + paramStruct{ + StructMembers: []structMember{ + structMemberString{ + Name: "type", + Value: "TXT", + }, structMemberString{ + Name: "name", + Value: name, + }, structMemberString{ + Name: "value", + Value: value, + }, structMemberInt{ + Name: "ttl", + Value: ttl, + }}, + }, + }, + }, resp) + return err +} + +func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { + resp := &responseBool{} + err := d.rpcCall(&methodCall{ + MethodName: "domain.zone.version.set", + Params: []param{ + paramString{Value: d.config.APIKey}, + paramInt{Value: zoneID}, + paramInt{Value: version}, + }, + }, resp) + if err != nil { + return err + } + + if !resp.Value { + return fmt.Errorf("could not set zone version") + } + return nil +} + +func (d *DNSProvider) setZone(domain string, zoneID int) error { + resp := &responseStruct{} + err := d.rpcCall(&methodCall{ + MethodName: "domain.zone.set", + Params: []param{ + paramString{Value: d.config.APIKey}, + paramString{Value: domain}, + paramInt{Value: zoneID}, + }, + }, resp) + if err != nil { + return err + } + + var respZoneID int + for _, member := range resp.StructMembers { + if member.Name == "zone_id" { + respZoneID = member.ValueInt + } + } + + if respZoneID != zoneID { + return fmt.Errorf("could not set new zone_id for %s", domain) + } + return nil +} + +func (d *DNSProvider) deleteZone(zoneID int) error { + resp := &responseBool{} + err := d.rpcCall(&methodCall{ + MethodName: "domain.zone.delete", + Params: []param{ + paramString{Value: d.config.APIKey}, + paramInt{Value: zoneID}, + }, + }, resp) + if err != nil { + return err + } + + if !resp.Value { + return fmt.Errorf("could not delete zone_id") + } + return nil +} + +func (d *DNSProvider) httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { + resp, err := d.config.HTTPClient.Post(url, bodyType, body) + if err != nil { + return nil, fmt.Errorf("HTTP Post Error: %v", err) + } + defer resp.Body.Close() + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("HTTP Post Error: %v", err) + } + + return b, nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go b/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go index f282d28b3..1a486ecf9 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go @@ -1,20 +1,15 @@ -// Package gandi implements a DNS provider for solving the DNS-01 -// challenge using Gandi DNS. +// Package gandi implements a DNS provider for solving the DNS-01 challenge using Gandi DNS. package gandi import ( - "bytes" - "encoding/xml" "errors" "fmt" - "io" - "io/ioutil" "net/http" "strings" "sync" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -27,10 +22,6 @@ const ( minTTL = 300 ) -// findZoneByFqdn determines the DNS zone of an fqdn. -// It is overridden during tests. -var findZoneByFqdn = acme.FindZoneByFqdn - // Config is used to configure the creation of the DNSProvider type Config struct { BaseURL string @@ -68,6 +59,8 @@ type DNSProvider struct { inProgressAuthZones map[string]struct{} inProgressMu sync.Mutex config *Config + // findZoneByFqdn determines the DNS zone of an fqdn. It is overridden during tests. + findZoneByFqdn func(fqdn string) (string, error) } // NewDNSProvider returns a DNSProvider instance configured for Gandi. @@ -84,16 +77,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Gandi. -// Deprecated -func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIKey = apiKey - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Gandi. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -112,6 +95,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { config: config, inProgressFQDNs: make(map[string]inProgressInfo), inProgressAuthZones: make(map[string]struct{}), + findZoneByFqdn: dns01.FindZoneByFqdn, }, nil } @@ -119,14 +103,14 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // does this by creating and activating a new temporary Gandi DNS // zone. This new zone contains the TXT record. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) if d.config.TTL < minTTL { d.config.TTL = minTTL // 300 is gandi minimum value for ttl } // find authZone and Gandi zone_id for fqdn - authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := d.findZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("gandi: findZoneByFqdn failure: %v", err) } @@ -154,7 +138,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // perform API actions to create and activate new gandi zone // containing the required TXT record - newZoneName := fmt.Sprintf("%s [ACME Challenge %s]", acme.UnFqdn(authZone), time.Now().Format(time.RFC822Z)) + newZoneName := fmt.Sprintf("%s [ACME Challenge %s]", dns01.UnFqdn(authZone), time.Now().Format(time.RFC822Z)) newZoneID, err := d.cloneZone(zoneID, newZoneName) if err != nil { @@ -196,7 +180,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // parameters. It does this by restoring the old Gandi DNS zone and // removing the temporary one created by Present. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) // acquire lock and retrieve zoneID, newZoneID and authZone d.inProgressMu.Lock() @@ -228,224 +212,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -// rpcCall makes an XML-RPC call to Gandi's RPC endpoint by -// marshaling the data given in the call argument to XML and sending -// that via HTTP Post to Gandi. -// The response is then unmarshalled into the resp argument. -func (d *DNSProvider) rpcCall(call *methodCall, resp response) error { - // marshal - b, err := xml.MarshalIndent(call, "", " ") - if err != nil { - return fmt.Errorf("marshal error: %v", err) - } - - // post - b = append([]byte(``+"\n"), b...) - respBody, err := d.httpPost(d.config.BaseURL, "text/xml", bytes.NewReader(b)) - if err != nil { - return err - } - - // unmarshal - err = xml.Unmarshal(respBody, resp) - if err != nil { - return fmt.Errorf("unmarshal error: %v", err) - } - if resp.faultCode() != 0 { - return rpcError{ - faultCode: resp.faultCode(), faultString: resp.faultString()} - } - return nil -} - -// functions to perform API actions - -func (d *DNSProvider) getZoneID(domain string) (int, error) { - resp := &responseStruct{} - err := d.rpcCall(&methodCall{ - MethodName: "domain.info", - Params: []param{ - paramString{Value: d.config.APIKey}, - paramString{Value: domain}, - }, - }, resp) - if err != nil { - return 0, err - } - - var zoneID int - for _, member := range resp.StructMembers { - if member.Name == "zone_id" { - zoneID = member.ValueInt - } - } - - if zoneID == 0 { - return 0, fmt.Errorf("could not determine zone_id for %s", domain) - } - return zoneID, nil -} - -func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { - resp := &responseStruct{} - err := d.rpcCall(&methodCall{ - MethodName: "domain.zone.clone", - Params: []param{ - paramString{Value: d.config.APIKey}, - paramInt{Value: zoneID}, - paramInt{Value: 0}, - paramStruct{ - StructMembers: []structMember{ - structMemberString{ - Name: "name", - Value: name, - }}, - }, - }, - }, resp) - if err != nil { - return 0, err - } - - var newZoneID int - for _, member := range resp.StructMembers { - if member.Name == "id" { - newZoneID = member.ValueInt - } - } - - if newZoneID == 0 { - return 0, fmt.Errorf("could not determine cloned zone_id") - } - return newZoneID, nil -} - -func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { - resp := &responseInt{} - err := d.rpcCall(&methodCall{ - MethodName: "domain.zone.version.new", - Params: []param{ - paramString{Value: d.config.APIKey}, - paramInt{Value: zoneID}, - }, - }, resp) - if err != nil { - return 0, err - } - - if resp.Value == 0 { - return 0, fmt.Errorf("could not create new zone version") - } - return resp.Value, nil -} - -func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value string, ttl int) error { - resp := &responseStruct{} - err := d.rpcCall(&methodCall{ - MethodName: "domain.zone.record.add", - Params: []param{ - paramString{Value: d.config.APIKey}, - paramInt{Value: zoneID}, - paramInt{Value: version}, - paramStruct{ - StructMembers: []structMember{ - structMemberString{ - Name: "type", - Value: "TXT", - }, structMemberString{ - Name: "name", - Value: name, - }, structMemberString{ - Name: "value", - Value: value, - }, structMemberInt{ - Name: "ttl", - Value: ttl, - }}, - }, - }, - }, resp) - return err -} - -func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { - resp := &responseBool{} - err := d.rpcCall(&methodCall{ - MethodName: "domain.zone.version.set", - Params: []param{ - paramString{Value: d.config.APIKey}, - paramInt{Value: zoneID}, - paramInt{Value: version}, - }, - }, resp) - if err != nil { - return err - } - - if !resp.Value { - return fmt.Errorf("could not set zone version") - } - return nil -} - -func (d *DNSProvider) setZone(domain string, zoneID int) error { - resp := &responseStruct{} - err := d.rpcCall(&methodCall{ - MethodName: "domain.zone.set", - Params: []param{ - paramString{Value: d.config.APIKey}, - paramString{Value: domain}, - paramInt{Value: zoneID}, - }, - }, resp) - if err != nil { - return err - } - - var respZoneID int - for _, member := range resp.StructMembers { - if member.Name == "zone_id" { - respZoneID = member.ValueInt - } - } - - if respZoneID != zoneID { - return fmt.Errorf("could not set new zone_id for %s", domain) - } - return nil -} - -func (d *DNSProvider) deleteZone(zoneID int) error { - resp := &responseBool{} - err := d.rpcCall(&methodCall{ - MethodName: "domain.zone.delete", - Params: []param{ - paramString{Value: d.config.APIKey}, - paramInt{Value: zoneID}, - }, - }, resp) - if err != nil { - return err - } - - if !resp.Value { - return fmt.Errorf("could not delete zone_id") - } - return nil -} - -func (d *DNSProvider) httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { - resp, err := d.config.HTTPClient.Post(url, bodyType, body) - if err != nil { - return nil, fmt.Errorf("HTTP Post Error: %v", err) - } - defer resp.Body.Close() - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("HTTP Post Error: %v", err) - } - - return b, nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/client.go b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/client.go index e342007f1..d17010963 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/client.go @@ -6,6 +6,8 @@ import ( "fmt" "io/ioutil" "net/http" + + "github.com/xenolf/lego/log" ) const apiKeyHeader = "X-Api-Key" @@ -24,6 +26,80 @@ type Record struct { RRSetType string `json:"rrset_type,omitempty"` } +func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl int) error { + // Get exiting values for the TXT records + // Needed to create challenges for both wildcard and base name domains + txtRecord, err := d.getTXTRecord(domain, name) + if err != nil { + return err + } + + values := []string{value} + if len(txtRecord.RRSetValues) > 0 { + values = append(values, txtRecord.RRSetValues...) + } + + target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) + + newRecord := &Record{RRSetTTL: ttl, RRSetValues: values} + req, err := d.newRequest(http.MethodPut, target, newRecord) + if err != nil { + return err + } + + message := &apiResponse{} + err = d.do(req, message) + if err != nil { + return fmt.Errorf("unable to create TXT record for domain %s and name %s: %v", domain, name, err) + } + + if message != nil && len(message.Message) > 0 { + log.Infof("API response: %s", message.Message) + } + + return nil +} + +func (d *DNSProvider) getTXTRecord(domain, name string) (*Record, error) { + target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) + + // Get exiting values for the TXT records + // Needed to create challenges for both wildcard and base name domains + req, err := d.newRequest(http.MethodGet, target, nil) + if err != nil { + return nil, err + } + + txtRecord := &Record{} + err = d.do(req, txtRecord) + if err != nil { + return nil, fmt.Errorf("unable to get TXT records for domain %s and name %s: %v", domain, name, err) + } + + return txtRecord, nil +} + +func (d *DNSProvider) deleteTXTRecord(domain string, name string) error { + target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) + + req, err := d.newRequest(http.MethodDelete, target, nil) + if err != nil { + return err + } + + message := &apiResponse{} + err = d.do(req, message) + if err != nil { + return fmt.Errorf("unable to delete TXT record for domain %s and name %s: %v", domain, name, err) + } + + if message != nil && len(message.Message) > 0 { + log.Infof("API response: %s", message.Message) + } + + return nil +} + func (d *DNSProvider) newRequest(method, resource string, body interface{}) (*http.Request, error) { u := fmt.Sprintf("%s/%s", d.config.BaseURL, resource) diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go index 0962d159c..2cf94ba7d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go @@ -1,5 +1,4 @@ -// Package gandiv5 implements a DNS provider for solving the DNS-01 -// challenge using Gandi LiveDNS api. +// Package gandiv5 implements a DNS provider for solving the DNS-01 challenge using Gandi LiveDNS api. package gandiv5 import ( @@ -10,8 +9,7 @@ import ( "sync" "time" - "github.com/xenolf/lego/acme" - "github.com/xenolf/lego/log" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -23,10 +21,6 @@ const ( minTTL = 300 ) -// findZoneByFqdn determines the DNS zone of an fqdn. -// It is overridden during tests. -var findZoneByFqdn = acme.FindZoneByFqdn - // inProgressInfo contains information about an in-progress challenge type inProgressInfo struct { fieldName string @@ -62,6 +56,8 @@ type DNSProvider struct { config *Config inProgressFQDNs map[string]inProgressInfo inProgressMu sync.Mutex + // findZoneByFqdn determines the DNS zone of an fqdn. It is overridden during tests. + findZoneByFqdn func(fqdn string) (string, error) } // NewDNSProvider returns a DNSProvider instance configured for Gandi. @@ -78,16 +74,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Gandi. -// Deprecated -func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIKey = apiKey - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Gandi. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -109,15 +95,16 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{ config: config, inProgressFQDNs: make(map[string]inProgressInfo), + findZoneByFqdn: dns01.FindZoneByFqdn, }, nil } // Present creates a TXT record using the specified parameters. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) // find authZone - authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := d.findZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("gandiv5: findZoneByFqdn failure: %v", err) } @@ -135,7 +122,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { defer d.inProgressMu.Unlock() // add TXT record into authZone - err = d.addTXTRecord(acme.UnFqdn(authZone), name, value, d.config.TTL) + err = d.addTXTRecord(dns01.UnFqdn(authZone), name, value, d.config.TTL) if err != nil { return err } @@ -150,7 +137,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) // acquire lock and retrieve authZone d.inProgressMu.Lock() @@ -165,7 +152,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { delete(d.inProgressFQDNs, fqdn) // delete TXT record from authZone - err := d.deleteTXTRecord(acme.UnFqdn(authZone), fieldName) + err := d.deleteTXTRecord(dns01.UnFqdn(authZone), fieldName) if err != nil { return fmt.Errorf("gandiv5: %v", err) } @@ -178,79 +165,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -// functions to perform API actions - -func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl int) error { - // Get exiting values for the TXT records - // Needed to create challenges for both wildcard and base name domains - txtRecord, err := d.getTXTRecord(domain, name) - if err != nil { - return err - } - - values := []string{value} - if len(txtRecord.RRSetValues) > 0 { - values = append(values, txtRecord.RRSetValues...) - } - - target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) - - newRecord := &Record{RRSetTTL: ttl, RRSetValues: values} - req, err := d.newRequest(http.MethodPut, target, newRecord) - if err != nil { - return err - } - - message := &apiResponse{} - err = d.do(req, message) - if err != nil { - return fmt.Errorf("unable to create TXT record for domain %s and name %s: %v", domain, name, err) - } - - if message != nil && len(message.Message) > 0 { - log.Infof("API response: %s", message.Message) - } - - return nil -} - -func (d *DNSProvider) getTXTRecord(domain, name string) (*Record, error) { - target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) - - // Get exiting values for the TXT records - // Needed to create challenges for both wildcard and base name domains - req, err := d.newRequest(http.MethodGet, target, nil) - if err != nil { - return nil, err - } - - txtRecord := &Record{} - err = d.do(req, txtRecord) - if err != nil { - return nil, fmt.Errorf("unable to get TXT records for domain %s and name %s: %v", domain, name, err) - } - - return txtRecord, nil -} - -func (d *DNSProvider) deleteTXTRecord(domain string, name string) error { - target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) - - req, err := d.newRequest(http.MethodDelete, target, nil) - if err != nil { - return err - } - - message := &apiResponse{} - err = d.do(req, message) - if err != nil { - return fmt.Errorf("unable to delete TXT record for domain %s and name %s: %v", domain, name, err) - } - - if message != nil && len(message.Message) > 0 { - log.Infof("API response: %s", message.Message) - } - - return nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go b/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go index f799461b3..9d0651740 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go @@ -1,5 +1,4 @@ -// Package gcloud implements a DNS provider for solving the DNS-01 -// challenge using Google Cloud DNS. +// Package gcloud implements a DNS provider for solving the DNS-01 challenge using Google Cloud DNS. package gcloud import ( @@ -9,17 +8,26 @@ import ( "io/ioutil" "net/http" "os" + "strconv" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" + "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" + "github.com/xenolf/lego/platform/wait" "golang.org/x/net/context" "golang.org/x/oauth2/google" "google.golang.org/api/dns/v1" + "google.golang.org/api/googleapi" +) + +const ( + changeStatusDone = "done" ) // Config is used to configure the creation of the DNSProvider type Config struct { + Debug bool Project string PropagationTimeout time.Duration PollingInterval time.Duration @@ -30,7 +38,8 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("GCE_TTL", 120), + Debug: env.GetOrDefaultBool("GCE_DEBUG", false), + TTL: env.GetOrDefaultInt("GCE_TTL", dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond("GCE_PROPAGATION_TIMEOUT", 180*time.Second), PollingInterval: env.GetOrDefaultSecond("GCE_POLLING_INTERVAL", 5*time.Second), } @@ -124,7 +133,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZone(domain) if err != nil { @@ -132,11 +141,32 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } // Look for existing records. - existing, err := d.findTxtRecords(zone, fqdn) + existingRrSet, err := d.findTxtRecords(zone, fqdn) if err != nil { return fmt.Errorf("googlecloud: %v", err) } + for _, rrSet := range existingRrSet { + var rrd []string + for _, rr := range rrSet.Rrdatas { + data := mustUnquote(rr) + rrd = append(rrd, data) + + if data == value { + log.Printf("skip: the record already exists: %s", value) + return nil + } + } + rrSet.Rrdatas = rrd + } + + // Attempt to delete the existing records before adding the new one. + if len(existingRrSet) > 0 { + if err = d.applyChanges(zone, &dns.Change{Deletions: existingRrSet}); err != nil { + return fmt.Errorf("googlecloud: %v", err) + } + } + rec := &dns.ResourceRecordSet{ Name: fqdn, Rrdatas: []string{value}, @@ -144,41 +174,74 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { Type: "TXT", } - change := &dns.Change{} - - if len(existing) > 0 { - // Attempt to delete the existing records when adding our new one. - change.Deletions = existing - - // Append existing TXT record data to the new TXT record data - for _, value := range existing { - rec.Rrdatas = append(rec.Rrdatas, value.Rrdatas...) + // Append existing TXT record data to the new TXT record data + for _, rrSet := range existingRrSet { + for _, rr := range rrSet.Rrdatas { + if rr != value { + rec.Rrdatas = append(rec.Rrdatas, rr) + } } } - change.Additions = []*dns.ResourceRecordSet{rec} + change := &dns.Change{ + Additions: []*dns.ResourceRecordSet{rec}, + } - chg, err := d.client.Changes.Create(d.config.Project, zone, change).Do() - if err != nil { + if err = d.applyChanges(zone, change); err != nil { return fmt.Errorf("googlecloud: %v", err) } - // wait for change to be acknowledged - for chg.Status == "pending" { - time.Sleep(time.Second) - - chg, err = d.client.Changes.Get(d.config.Project, zone, chg.Id).Do() - if err != nil { - return fmt.Errorf("googlecloud: %v", err) - } - } - return nil } +func (d *DNSProvider) applyChanges(zone string, change *dns.Change) error { + if d.config.Debug { + data, _ := json.Marshal(change) + log.Printf("change (Create): %s", string(data)) + } + + chg, err := d.client.Changes.Create(d.config.Project, zone, change).Do() + if err != nil { + if v, ok := err.(*googleapi.Error); ok { + if v.Code == http.StatusNotFound { + return nil + } + } + + data, _ := json.Marshal(change) + return fmt.Errorf("failed to perform changes [zone %s, change %s]: %v", zone, string(data), err) + } + + if chg.Status == changeStatusDone { + return nil + } + + chgID := chg.Id + + // wait for change to be acknowledged + return wait.For("apply change", 30*time.Second, 3*time.Second, func() (bool, error) { + if d.config.Debug { + data, _ := json.Marshal(change) + log.Printf("change (Get): %s", string(data)) + } + + chg, err = d.client.Changes.Get(d.config.Project, zone, chgID).Do() + if err != nil { + data, _ := json.Marshal(change) + return false, fmt.Errorf("failed to get changes [zone %s, change %s]: %v", zone, string(data), err) + } + + if chg.Status == changeStatusDone { + return true, nil + } + + return false, fmt.Errorf("status: %s", chg.Status) + }) +} + // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZone(domain) if err != nil { @@ -209,7 +272,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // getHostedZone returns the managed-zone func (d *DNSProvider) getHostedZone(domain string) (string, error) { - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return "", err } @@ -237,3 +300,11 @@ func (d *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSe return recs.Rrsets, nil } + +func mustUnquote(raw string) string { + clean, err := strconv.Unquote(raw) + if err != nil { + return raw + } + return clean +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/glesys/client.go b/vendor/github.com/xenolf/lego/providers/dns/glesys/client.go index a7e8cf8ee..1ab552cd7 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/glesys/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/glesys/client.go @@ -1,5 +1,14 @@ package glesys +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/xenolf/lego/log" +) + // types for JSON method calls, parameters, and responses type addRecordRequest struct { @@ -22,3 +31,61 @@ type responseStruct struct { Record deleteRecordRequest `json:"record"` } `json:"response"` } + +func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, value string, ttl int) (int, error) { + response, err := d.sendRequest(http.MethodPost, "addrecord", addRecordRequest{ + DomainName: domain, + Host: name, + Type: "TXT", + Data: value, + TTL: ttl, + }) + + if response != nil && response.Response.Status.Code == http.StatusOK { + log.Infof("[%s]: Successfully created record id %d", fqdn, response.Response.Record.RecordID) + return response.Response.Record.RecordID, nil + } + return 0, err +} + +func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error { + response, err := d.sendRequest(http.MethodPost, "deleterecord", deleteRecordRequest{ + RecordID: recordid, + }) + if response != nil && response.Response.Status.Code == 200 { + log.Infof("[%s]: Successfully deleted record id %d", fqdn, recordid) + } + return err +} + +func (d *DNSProvider) sendRequest(method string, resource string, payload interface{}) (*responseStruct, error) { + url := fmt.Sprintf("%s/%s", defaultBaseURL, resource) + + body, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.SetBasicAuth(d.config.APIUser, d.config.APIKey) + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("request failed with HTTP status code %d", resp.StatusCode) + } + + var response responseStruct + err = json.NewDecoder(resp.Body).Decode(&response) + + return &response, err +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go b/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go index 4aab8744d..98667d8d5 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go +++ b/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go @@ -1,10 +1,7 @@ -// Package glesys implements a DNS provider for solving the DNS-01 -// challenge using GleSYS api. +// Package glesys implements a DNS provider for solving the DNS-01 challenge using GleSYS api. package glesys import ( - "bytes" - "encoding/json" "errors" "fmt" "net/http" @@ -12,8 +9,7 @@ import ( "sync" "time" - "github.com/xenolf/lego/acme" - "github.com/xenolf/lego/log" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -72,17 +68,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for GleSYS. -// Deprecated -func NewDNSProviderCredentials(apiUser string, apiKey string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIUser = apiUser - config.APIKey = apiKey - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for GleSYS. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -105,10 +90,10 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record using the specified parameters. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) // find authZone - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("glesys: findZoneByFqdn failure: %v", err) } @@ -126,7 +111,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { defer d.inProgressMu.Unlock() // add TXT record into authZone - recordID, err := d.addTXTRecord(domain, acme.UnFqdn(authZone), name, value, d.config.TTL) + recordID, err := d.addTXTRecord(domain, dns01.UnFqdn(authZone), name, value, d.config.TTL) if err != nil { return err } @@ -138,7 +123,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) // acquire lock and retrieve authZone d.inProgressMu.Lock() @@ -161,63 +146,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -func (d *DNSProvider) sendRequest(method string, resource string, payload interface{}) (*responseStruct, error) { - url := fmt.Sprintf("%s/%s", defaultBaseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/json") - req.SetBasicAuth(d.config.APIUser, d.config.APIKey) - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return nil, fmt.Errorf("request failed with HTTP status code %d", resp.StatusCode) - } - - var response responseStruct - err = json.NewDecoder(resp.Body).Decode(&response) - - return &response, err -} - -// functions to perform API actions - -func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, value string, ttl int) (int, error) { - response, err := d.sendRequest(http.MethodPost, "addrecord", addRecordRequest{ - DomainName: domain, - Host: name, - Type: "TXT", - Data: value, - TTL: ttl, - }) - - if response != nil && response.Response.Status.Code == http.StatusOK { - log.Infof("[%s]: Successfully created record id %d", fqdn, response.Response.Record.RecordID) - return response.Response.Record.RecordID, nil - } - return 0, err -} - -func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error { - response, err := d.sendRequest(http.MethodPost, "deleterecord", deleteRecordRequest{ - RecordID: recordid, - }) - if response != nil && response.Response.Status.Code == 200 { - log.Infof("[%s]: Successfully deleted record id %d", fqdn, recordid) - } - return err -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/godaddy/client.go b/vendor/github.com/xenolf/lego/providers/dns/godaddy/client.go new file mode 100644 index 000000000..212b8e69c --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/godaddy/client.go @@ -0,0 +1,53 @@ +package godaddy + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" +) + +// DNSRecord a DNS record +type DNSRecord struct { + Type string `json:"type"` + Name string `json:"name"` + Data string `json:"data"` + Priority int `json:"priority,omitempty"` + TTL int `json:"ttl,omitempty"` +} + +func (d *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error { + body, err := json.Marshal(records) + if err != nil { + return err + } + + var resp *http.Response + resp, err = d.makeRequest(http.MethodPut, fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body)) + if err != nil { + return err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + bodyBytes, _ := ioutil.ReadAll(resp.Body) + return fmt.Errorf("could not create record %v; Status: %v; Body: %s", string(body), resp.StatusCode, string(bodyBytes)) + } + return nil +} + +func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequest(method, fmt.Sprintf("%s%s", defaultBaseURL, uri), body) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", d.config.APIKey, d.config.APISecret)) + + return d.config.HTTPClient.Do(req) +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go index 8aebde650..dd054ab5c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go +++ b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go @@ -2,17 +2,13 @@ package godaddy import ( - "bytes" - "encoding/json" "errors" "fmt" - "io" - "io/ioutil" "net/http" "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -28,6 +24,7 @@ type Config struct { APISecret string PropagationTimeout time.Duration PollingInterval time.Duration + SequenceInterval time.Duration TTL int HTTPClient *http.Client } @@ -38,6 +35,7 @@ func NewDefaultConfig() *Config { TTL: env.GetOrDefaultInt("GODADDY_TTL", minTTL), PropagationTimeout: env.GetOrDefaultSecond("GODADDY_PROPAGATION_TIMEOUT", 120*time.Second), PollingInterval: env.GetOrDefaultSecond("GODADDY_POLLING_INTERVAL", 2*time.Second), + SequenceInterval: env.GetOrDefaultSecond("GODADDY_SEQUENCE_INTERVAL", dns01.DefaultPropagationTimeout), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("GODADDY_HTTP_TIMEOUT", 30*time.Second), }, @@ -65,17 +63,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for godaddy. -// Deprecated -func NewDNSProviderCredentials(apiKey, apiSecret string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIKey = apiKey - config.APISecret = apiSecret - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for godaddy. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -99,17 +86,9 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } -func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) - if idx := strings.Index(name, "."+domain); idx != -1 { - return name[:idx] - } - return name -} - // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) domainZone, err := d.getZone(fqdn) if err != nil { return err @@ -128,30 +107,9 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return d.updateRecords(rec, domainZone, recordName) } -func (d *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error { - body, err := json.Marshal(records) - if err != nil { - return err - } - - var resp *http.Response - resp, err = d.makeRequest(http.MethodPut, fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body)) - if err != nil { - return err - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - bodyBytes, _ := ioutil.ReadAll(resp.Body) - return fmt.Errorf("could not create record %v; Status: %v; Body: %s", string(body), resp.StatusCode, string(bodyBytes)) - } - return nil -} - // CleanUp sets null value in the TXT DNS record as GoDaddy has no proper DELETE record method func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) domainZone, err := d.getZone(fqdn) if err != nil { return err @@ -169,33 +127,25 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return d.updateRecords(rec, domainZone, recordName) } +// Sequential All DNS challenges for this provider will be resolved sequentially. +// Returns the interval between each iteration. +func (d *DNSProvider) Sequential() time.Duration { + return d.config.SequenceInterval +} + +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { + name := dns01.UnFqdn(fqdn) + if idx := strings.Index(name, "."+domain); idx != -1 { + return name[:idx] + } + return name +} + func (d *DNSProvider) getZone(fqdn string) (string, error) { - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return "", err } - return acme.UnFqdn(authZone), nil -} - -func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest(method, fmt.Sprintf("%s%s", defaultBaseURL, uri), body) - if err != nil { - return nil, err - } - - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", d.config.APIKey, d.config.APISecret)) - - return d.config.HTTPClient.Do(req) -} - -// DNSRecord a DNS record -type DNSRecord struct { - Type string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - Priority int `json:"priority,omitempty"` - TTL int `json:"ttl,omitempty"` + return dns01.UnFqdn(authZone), nil } diff --git a/vendor/github.com/xenolf/lego/providers/dns/hostingde/client.go b/vendor/github.com/xenolf/lego/providers/dns/hostingde/client.go index 0bee9cccb..6f2c96356 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/hostingde/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/hostingde/client.go @@ -1,5 +1,16 @@ package hostingde +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" +) + +const defaultBaseURL = "https://secure.hosting.de/api/dns/v1/json" + // RecordsAddRequest represents a DNS record to add type RecordsAddRequest struct { Name string `json:"name"` @@ -89,3 +100,44 @@ type ZoneUpdateRequest struct { RecordsToAdd []RecordsAddRequest `json:"recordsToAdd"` RecordsToDelete []RecordsDeleteRequest `json:"recordsToDelete"` } + +func (d *DNSProvider) updateZone(updateRequest ZoneUpdateRequest) (*ZoneUpdateResponse, error) { + body, err := json.Marshal(updateRequest) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, defaultBaseURL+"/zoneUpdate", bytes.NewReader(body)) + if err != nil { + return nil, err + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error querying API: %v", err) + } + + defer resp.Body.Close() + + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.New(toUnreadableBodyMessage(req, content)) + } + + // Everything looks good; but we'll need the ID later to delete the record + updateResponse := &ZoneUpdateResponse{} + err = json.Unmarshal(content, updateResponse) + if err != nil { + return nil, fmt.Errorf("%v: %s", err, toUnreadableBodyMessage(req, content)) + } + + if updateResponse.Status != "success" && updateResponse.Status != "pending" { + return updateResponse, errors.New(toUnreadableBodyMessage(req, content)) + } + + return updateResponse, nil +} + +func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string { + return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody)) +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/hostingde/hostingde.go b/vendor/github.com/xenolf/lego/providers/dns/hostingde/hostingde.go index 7a64b1b89..738771bf1 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/hostingde/hostingde.go +++ b/vendor/github.com/xenolf/lego/providers/dns/hostingde/hostingde.go @@ -1,23 +1,17 @@ -// Package hostingde implements a DNS provider for solving the DNS-01 -// challenge using hosting.de. +// Package hostingde implements a DNS provider for solving the DNS-01 challenge using hosting.de. package hostingde import ( - "bytes" - "encoding/json" "errors" "fmt" - "io/ioutil" "net/http" "sync" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) -const defaultBaseURL = "https://secure.hosting.de/api/dns/v1/json" - // Config is used to configure the creation of the DNSProvider type Config struct { APIKey string @@ -31,7 +25,7 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("HOSTINGDE_TTL", 120), + TTL: env.GetOrDefaultInt("HOSTINGDE_TTL", dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond("HOSTINGDE_PROPAGATION_TIMEOUT", 2*time.Minute), PollingInterval: env.GetOrDefaultSecond("HOSTINGDE_POLLING_INTERVAL", 2*time.Second), HTTPClient: &http.Client{ @@ -91,11 +85,11 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) rec := []RecordsAddRequest{{ Type: "TXT", - Name: acme.UnFqdn(fqdn), + Name: dns01.UnFqdn(fqdn), Content: value, TTL: d.config.TTL, }} @@ -114,7 +108,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } for _, record := range resp.Response.Records { - if record.Name == acme.UnFqdn(fqdn) && record.Content == fmt.Sprintf(`"%s"`, value) { + if record.Name == dns01.UnFqdn(fqdn) && record.Content == fmt.Sprintf(`"%s"`, value) { d.recordIDsMu.Lock() d.recordIDs[fqdn] = record.ID d.recordIDsMu.Unlock() @@ -130,7 +124,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() @@ -142,7 +136,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { rec := []RecordsDeleteRequest{{ Type: "TXT", - Name: acme.UnFqdn(fqdn), + Name: dns01.UnFqdn(fqdn), Content: value, ID: recordID, }} @@ -166,44 +160,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { } return nil } - -func (d *DNSProvider) updateZone(updateRequest ZoneUpdateRequest) (*ZoneUpdateResponse, error) { - body, err := json.Marshal(updateRequest) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, defaultBaseURL+"/zoneUpdate", bytes.NewReader(body)) - if err != nil { - return nil, err - } - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("error querying API: %v", err) - } - - defer resp.Body.Close() - - content, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, errors.New(toUnreadableBodyMessage(req, content)) - } - - // Everything looks good; but we'll need the ID later to delete the record - updateResponse := &ZoneUpdateResponse{} - err = json.Unmarshal(content, updateResponse) - if err != nil { - return nil, fmt.Errorf("%v: %s", err, toUnreadableBodyMessage(req, content)) - } - - if updateResponse.Status != "success" && updateResponse.Status != "pending" { - return updateResponse, errors.New(toUnreadableBodyMessage(req, content)) - } - - return updateResponse, nil -} - -func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string { - return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody)) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/httpreq/httpreq.go b/vendor/github.com/xenolf/lego/providers/dns/httpreq/httpreq.go index cf957d41b..f6a1fe0ce 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/httpreq/httpreq.go +++ b/vendor/github.com/xenolf/lego/providers/dns/httpreq/httpreq.go @@ -12,7 +12,7 @@ import ( "os" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -41,8 +41,8 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - PropagationTimeout: env.GetOrDefaultSecond("HTTPREQ_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("HTTPREQ_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("HTTPREQ_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("HTTPREQ_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("HTTPREQ_HTTP_TIMEOUT", 30*time.Second), }, @@ -109,7 +109,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return nil } - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) msg := &message{ FQDN: fqdn, Value: value, @@ -138,7 +138,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) msg := &message{ FQDN: fqdn, Value: value, diff --git a/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go b/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go index fc09f8638..9372a9c7f 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go +++ b/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go @@ -9,7 +9,7 @@ import ( "github.com/iij/doapi" "github.com/iij/doapi/protocol" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -73,7 +73,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - _, value, _ := acme.DNS01Record(domain, keyAuth) + _, value := dns01.GetRecord(domain, keyAuth) err := d.addTxtRecord(domain, value) if err != nil { @@ -84,7 +84,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - _, value, _ := acme.DNS01Record(domain, keyAuth) + _, value := dns01.GetRecord(domain, keyAuth) err := d.deleteTxtRecord(domain, value) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/inwx/inwx.go b/vendor/github.com/xenolf/lego/providers/dns/inwx/inwx.go index ba6c8cbec..dd99a5b1a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/inwx/inwx.go +++ b/vendor/github.com/xenolf/lego/providers/dns/inwx/inwx.go @@ -7,7 +7,7 @@ import ( "time" "github.com/smueller18/goinwx" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) @@ -25,8 +25,8 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - PropagationTimeout: env.GetOrDefaultSecond("INWX_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("INWX_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("INWX_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("INWX_POLLING_INTERVAL", dns01.DefaultPollingInterval), TTL: env.GetOrDefaultInt("INWX_TTL", 300), Sandbox: env.GetOrDefaultBool("INWX_SANDBOX", false), } @@ -75,9 +75,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("inwx: %v", err) } @@ -95,8 +95,8 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { }() var request = &goinwx.NameserverRecordRequest{ - Domain: acme.UnFqdn(authZone), - Name: acme.UnFqdn(fqdn), + Domain: dns01.UnFqdn(authZone), + Name: dns01.UnFqdn(fqdn), Type: "TXT", Content: value, Ttl: d.config.TTL, @@ -104,9 +104,9 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { _, err = d.client.Nameservers.CreateRecord(request) if err != nil { - switch err.(type) { + switch er := err.(type) { case *goinwx.ErrorResponse: - if err.(*goinwx.ErrorResponse).Message == "Object exists" { + if er.Message == "Object exists" { return nil } return fmt.Errorf("inwx: %v", err) @@ -120,9 +120,9 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("inwx: %v", err) } @@ -140,8 +140,8 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { }() response, err := d.client.Nameservers.Info(&goinwx.NameserverInfoRequest{ - Domain: acme.UnFqdn(authZone), - Name: acme.UnFqdn(fqdn), + Domain: dns01.UnFqdn(authZone), + Name: dns01.UnFqdn(fqdn), Type: "TXT", }) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go b/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go index 3b3fe6783..44a70bd13 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go +++ b/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go @@ -1,5 +1,4 @@ -// Package lightsail implements a DNS provider for solving the DNS-01 challenge -// using AWS Lightsail DNS. +// Package lightsail implements a DNS provider for solving the DNS-01 challenge using AWS Lightsail DNS. package lightsail import ( @@ -13,7 +12,7 @@ import ( "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/lightsail" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -54,8 +53,8 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ DNSZone: env.GetOrFile("DNS_ZONE"), - PropagationTimeout: env.GetOrDefaultSecond("LIGHTSAIL_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("LIGHTSAIL_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("LIGHTSAIL_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("LIGHTSAIL_POLLING_INTERVAL", dns01.DefaultPollingInterval), Region: env.GetOrDefaultString("LIGHTSAIL_REGION", "us-east-1"), } } @@ -105,7 +104,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) err := d.newTxtRecord(fqdn, `"`+value+`"`) if err != nil { @@ -116,7 +115,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) params := &lightsail.DeleteDomainEntryInput{ DomainName: aws.String(d.config.DNSZone), diff --git a/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go b/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go index 09b92e994..3a129e8c1 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go +++ b/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go @@ -1,5 +1,4 @@ -// Package linode implements a DNS provider for solving the DNS-01 challenge -// using Linode DNS. +// Package linode implements a DNS provider for solving the DNS-01 challenge using Linode DNS. package linode import ( @@ -9,7 +8,7 @@ import ( "time" "github.com/timewasted/linode/dns" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -59,16 +58,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Linode. -// Deprecated -func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIKey = apiKey - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Linode. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -108,13 +97,13 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record using the specified parameters. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZoneInfo(fqdn) if err != nil { return err } - if _, err = d.client.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, d.config.TTL); err != nil { + if _, err = d.client.CreateDomainResourceTXT(zone.domainID, dns01.UnFqdn(fqdn), value, d.config.TTL); err != nil { return err } @@ -123,7 +112,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZoneInfo(fqdn) if err != nil { return err @@ -155,7 +144,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) { // Lookup the zone that handles the specified FQDN. - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return nil, err } @@ -163,7 +152,7 @@ func (d *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) { resourceName := strings.TrimSuffix(fqdn, "."+authZone) // Query the authority zone. - domain, err := d.client.GetDomain(acme.UnFqdn(authZone)) + domain, err := d.client.GetDomain(dns01.UnFqdn(authZone)) if err != nil { return nil, err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/linodev4/linodev4.go b/vendor/github.com/xenolf/lego/providers/dns/linodev4/linodev4.go index 97903e9cf..eee0ddc97 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/linodev4/linodev4.go +++ b/vendor/github.com/xenolf/lego/providers/dns/linodev4/linodev4.go @@ -1,5 +1,4 @@ -// Package linodev4 implements a DNS provider for solving the DNS-01 challenge -// using Linode DNS and Linode's APIv4 +// Package linodev4 implements a DNS provider for solving the DNS-01 challenge using Linode DNS and Linode's APIv4 package linodev4 import ( @@ -12,7 +11,7 @@ import ( "time" "github.com/linode/linodego" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" "golang.org/x/oauth2" ) @@ -115,14 +114,14 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record using the specified parameters. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZoneInfo(fqdn) if err != nil { return err } createOpts := linodego.DomainRecordCreateOptions{ - Name: acme.UnFqdn(fqdn), + Name: dns01.UnFqdn(fqdn), Target: value, TTLSec: d.config.TTL, Type: linodego.RecordTypeTXT, @@ -134,7 +133,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZoneInfo(fqdn) if err != nil { @@ -163,13 +162,13 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) { // Lookup the zone that handles the specified FQDN. - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return nil, err } // Query the authority zone. - data, err := json.Marshal(map[string]string{"domain": acme.UnFqdn(authZone)}) + data, err := json.Marshal(map[string]string{"domain": dns01.UnFqdn(authZone)}) if err != nil { return nil, err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/mydnsjp/client.go b/vendor/github.com/xenolf/lego/providers/dns/mydnsjp/client.go new file mode 100644 index 000000000..d8fc5841e --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/mydnsjp/client.go @@ -0,0 +1,52 @@ +package mydnsjp + +import ( + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +func (d *DNSProvider) doRequest(domain, value string, cmd string) error { + req, err := d.buildRequest(domain, value, cmd) + if err != nil { + return err + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return fmt.Errorf("error querying API: %v", err) + } + + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + var content []byte + content, err = ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + return fmt.Errorf("request %s failed [status code %d]: %s", req.URL, resp.StatusCode, string(content)) + } + + return nil +} + +func (d *DNSProvider) buildRequest(domain, value string, cmd string) (*http.Request, error) { + params := url.Values{} + params.Set("CERTBOT_DOMAIN", domain) + params.Set("CERTBOT_VALIDATION", value) + params.Set("EDIT_CMD", cmd) + + req, err := http.NewRequest(http.MethodPost, defaultBaseURL, strings.NewReader(params.Encode())) + if err != nil { + return nil, fmt.Errorf("invalid request: %v", err) + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.SetBasicAuth(d.config.MasterID, d.config.Password) + + return req, nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/mydnsjp/mydnsjp.go b/vendor/github.com/xenolf/lego/providers/dns/mydnsjp/mydnsjp.go index 05cad5686..fd5d7814d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/mydnsjp/mydnsjp.go +++ b/vendor/github.com/xenolf/lego/providers/dns/mydnsjp/mydnsjp.go @@ -1,17 +1,13 @@ -// Package mydnsjp implements a DNS provider for solving the DNS-01 -// challenge using MyDNS.jp. +// Package mydnsjp implements a DNS provider for solving the DNS-01 challenge using MyDNS.jp. package mydnsjp import ( "errors" "fmt" - "io/ioutil" "net/http" - "net/url" - "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -78,7 +74,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - _, value, _ := acme.DNS01Record(domain, keyAuth) + _, value := dns01.GetRecord(domain, keyAuth) err := d.doRequest(domain, value, "REGIST") if err != nil { return fmt.Errorf("mydnsjp: %v", err) @@ -88,53 +84,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - _, value, _ := acme.DNS01Record(domain, keyAuth) + _, value := dns01.GetRecord(domain, keyAuth) err := d.doRequest(domain, value, "DELETE") if err != nil { return fmt.Errorf("mydnsjp: %v", err) } return nil } - -func (d *DNSProvider) doRequest(domain, value string, cmd string) error { - req, err := d.buildRequest(domain, value, cmd) - if err != nil { - return err - } - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return fmt.Errorf("error querying API: %v", err) - } - - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - var content []byte - content, err = ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - - return fmt.Errorf("request %s failed [status code %d]: %s", req.URL, resp.StatusCode, string(content)) - } - - return nil -} - -func (d *DNSProvider) buildRequest(domain, value string, cmd string) (*http.Request, error) { - params := url.Values{} - params.Set("CERTBOT_DOMAIN", domain) - params.Set("CERTBOT_VALIDATION", value) - params.Set("EDIT_CMD", cmd) - - req, err := http.NewRequest(http.MethodPost, defaultBaseURL, strings.NewReader(params.Encode())) - if err != nil { - return nil, fmt.Errorf("invalid request: %v", err) - } - - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.SetBasicAuth(d.config.MasterID, d.config.Password) - - return req, nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/namecheap/client.go b/vendor/github.com/xenolf/lego/providers/dns/namecheap/client.go index d52f65509..85bec85f8 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/namecheap/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/namecheap/client.go @@ -20,8 +20,8 @@ type Record struct { TTL string `xml:",attr"` } -// apierror describes an error record in a namecheap API response. -type apierror struct { +// apiError describes an error record in a namecheap API response. +type apiError struct { Number int `xml:",attr"` Description string `xml:",innerxml"` } @@ -29,7 +29,7 @@ type apierror struct { type setHostsResponse struct { XMLName xml.Name `xml:"ApiResponse"` Status string `xml:"Status,attr"` - Errors []apierror `xml:"Errors>Error"` + Errors []apiError `xml:"Errors>Error"` Result struct { IsSuccess string `xml:",attr"` } `xml:"CommandResponse>DomainDNSSetHostsResult"` @@ -38,13 +38,13 @@ type setHostsResponse struct { type getHostsResponse struct { XMLName xml.Name `xml:"ApiResponse"` Status string `xml:"Status,attr"` - Errors []apierror `xml:"Errors>Error"` + Errors []apiError `xml:"Errors>Error"` Hosts []Record `xml:"CommandResponse>DomainDNSGetHostsResult>host"` } type getTldsResponse struct { XMLName xml.Name `xml:"ApiResponse"` - Errors []apierror `xml:"Errors>Error"` + Errors []apiError `xml:"Errors>Error"` Result []struct { Name string `xml:",attr"` } `xml:"CommandResponse>Tlds>Tld"` diff --git a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go b/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go index 731699ac4..2bcf91989 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go +++ b/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go @@ -1,5 +1,4 @@ -// Package namecheap implements a DNS provider for solving the DNS-01 -// challenge using namecheap DNS. +// Package namecheap implements a DNS provider for solving the DNS-01 challenge using namecheap DNS. package namecheap import ( @@ -11,7 +10,7 @@ import ( "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) @@ -64,7 +63,7 @@ func NewDefaultConfig() *Config { return &Config{ BaseURL: defaultBaseURL, Debug: env.GetOrDefaultBool("NAMECHEAP_DEBUG", false), - TTL: env.GetOrDefaultInt("NAMECHEAP_TTL", 120), + TTL: env.GetOrDefaultInt("NAMECHEAP_TTL", dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond("NAMECHEAP_PROPAGATION_TIMEOUT", 60*time.Minute), PollingInterval: env.GetOrDefaultSecond("NAMECHEAP_POLLING_INTERVAL", 15*time.Second), HTTPClient: &http.Client{ @@ -95,17 +94,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for namecheap. -// Deprecated -func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIUser = apiUser - config.APIKey = apiKey - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for namecheap. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -233,7 +221,7 @@ func getClientIP(client *http.Client, debug bool) (addr string, err error) { // newChallenge builds a challenge record from a domain name, a challenge // authentication key, and a map of available TLDs. func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, error) { - domain = acme.UnFqdn(domain) + domain = dns01.UnFqdn(domain) parts := strings.Split(domain, ".") // Find the longest matching TLD. @@ -256,7 +244,7 @@ func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, e host = strings.Join(parts[:longest-1], ".") } - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) return &challenge{ domain: domain, diff --git a/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go b/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go index 2cd2b26d1..4f6137b4b 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go +++ b/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go @@ -1,5 +1,4 @@ -// Package namedotcom implements a DNS provider for solving the DNS-01 challenge -// using Name.com's DNS service. +// Package namedotcom implements a DNS provider for solving the DNS-01 challenge using Name.com's DNS service. package namedotcom import ( @@ -10,7 +9,7 @@ import ( "time" "github.com/namedotcom/go/namecom" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -32,8 +31,8 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ TTL: env.GetOrDefaultInt("NAMECOM_TTL", minTTL), - PropagationTimeout: env.GetOrDefaultSecond("NAMECOM_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("NAMECOM_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("NAMECOM_PROPAGATION_TIMEOUT", 15*time.Minute), + PollingInterval: env.GetOrDefaultSecond("NAMECOM_POLLING_INTERVAL", 20*time.Second), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("NAMECOM_HTTP_TIMEOUT", 10*time.Second), }, @@ -63,18 +62,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for namedotcom. -// Deprecated -func NewDNSProviderCredentials(username, apiToken, server string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.Username = username - config.APIToken = apiToken - config.Server = server - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for namedotcom. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -105,7 +92,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) request := &namecom.Record{ DomainName: domain, @@ -125,7 +112,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) records, err := d.getRecords(domain) if err != nil { @@ -175,7 +162,7 @@ func (d *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { } func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } diff --git a/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go b/vendor/github.com/xenolf/lego/providers/dns/netcup/internal/client.go similarity index 94% rename from vendor/github.com/xenolf/lego/providers/dns/netcup/client.go rename to vendor/github.com/xenolf/lego/providers/dns/netcup/internal/client.go index 17a2ae968..733bfd0be 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/netcup/internal/client.go @@ -1,4 +1,4 @@ -package netcup +package internal import ( "bytes" @@ -7,8 +7,6 @@ import ( "io/ioutil" "net/http" "time" - - "github.com/xenolf/lego/acme" ) // defaultBaseURL for reaching the jSON-based API-Endpoint of netcup @@ -246,7 +244,6 @@ func (c *Client) doRequest(payload interface{}, responseData interface{}) error req.Close = true req.Header.Set("content-type", "application/json") - req.Header.Set("User-Agent", acme.UserAgent) resp, err := c.HTTPClient.Do(req) if err != nil { @@ -316,3 +313,15 @@ func decodeResponseMsg(resp *http.Response) (*ResponseMsg, error) { return &respMsg, nil } + +// GetDNSRecordIdx searches a given array of DNSRecords for a given DNSRecord +// equivalence is determined by Destination and RecortType attributes +// returns index of given DNSRecord in given array of DNSRecords +func GetDNSRecordIdx(records []DNSRecord, record DNSRecord) (int, error) { + for index, element := range records { + if record.Destination == element.Destination && record.RecordType == element.RecordType { + return index, nil + } + } + return -1, fmt.Errorf("no DNS Record found") +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go b/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go index 0dc59f960..8e8e1b5be 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go +++ b/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go @@ -8,7 +8,9 @@ import ( "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/providers/dns/netcup/internal" + + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) @@ -27,7 +29,7 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("NETCUP_TTL", 120), + TTL: env.GetOrDefaultInt("NETCUP_TTL", dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond("NETCUP_PROPAGATION_TIMEOUT", 120*time.Second), PollingInterval: env.GetOrDefaultSecond("NETCUP_POLLING_INTERVAL", 5*time.Second), HTTPClient: &http.Client{ @@ -38,7 +40,7 @@ func NewDefaultConfig() *Config { // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { - client *Client + client *internal.Client config *Config } @@ -59,25 +61,13 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for netcup. -// Deprecated -func NewDNSProviderCredentials(customer, key, password string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.Customer = customer - config.Key = key - config.Password = password - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for netcup. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { return nil, errors.New("netcup: the configuration of the DNS provider is nil") } - client, err := NewClient(config.Customer, config.Key, config.Password) + client, err := internal.NewClient(config.Customer, config.Key, config.Password) if err != nil { return nil, fmt.Errorf("netcup: %v", err) } @@ -89,9 +79,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domainName, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domainName, keyAuth) + fqdn, value := dns01.GetRecord(domainName, keyAuth) - zone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + zone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("netcup: failed to find DNSZone, %v", err) } @@ -109,9 +99,14 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error { }() hostname := strings.Replace(fqdn, "."+zone, "", 1) - record := createTxtRecord(hostname, value, d.config.TTL) + record := internal.DNSRecord{ + Hostname: hostname, + RecordType: "TXT", + Destination: value, + TTL: d.config.TTL, + } - zone = acme.UnFqdn(zone) + zone = dns01.UnFqdn(zone) records, err := d.client.GetDNSRecords(zone, sessionID) if err != nil { @@ -131,9 +126,9 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domainName, keyAuth) + fqdn, value := dns01.GetRecord(domainName, keyAuth) - zone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + zone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("netcup: failed to find DNSZone, %v", err) } @@ -152,23 +147,27 @@ func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error { hostname := strings.Replace(fqdn, "."+zone, "", 1) - zone = acme.UnFqdn(zone) + zone = dns01.UnFqdn(zone) records, err := d.client.GetDNSRecords(zone, sessionID) if err != nil { return fmt.Errorf("netcup: %v", err) } - record := createTxtRecord(hostname, value, 0) + record := internal.DNSRecord{ + Hostname: hostname, + RecordType: "TXT", + Destination: value, + } - idx, err := getDNSRecordIdx(records, record) + idx, err := internal.GetDNSRecordIdx(records, record) if err != nil { return fmt.Errorf("netcup: %v", err) } records[idx].DeleteRecord = true - err = d.client.UpdateDNSRecord(sessionID, zone, []DNSRecord{records[idx]}) + err = d.client.UpdateDNSRecord(sessionID, zone, []internal.DNSRecord{records[idx]}) if err != nil { return fmt.Errorf("netcup: %v", err) } @@ -181,29 +180,3 @@ func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -// getDNSRecordIdx searches a given array of DNSRecords for a given DNSRecord -// equivalence is determined by Destination and RecortType attributes -// returns index of given DNSRecord in given array of DNSRecords -func getDNSRecordIdx(records []DNSRecord, record DNSRecord) (int, error) { - for index, element := range records { - if record.Destination == element.Destination && record.RecordType == element.RecordType { - return index, nil - } - } - return -1, fmt.Errorf("no DNS Record found") -} - -// createTxtRecord uses the supplied values to return a DNSRecord of type TXT for the dns-01 challenge -func createTxtRecord(hostname, value string, ttl int) DNSRecord { - return DNSRecord{ - ID: 0, - Hostname: hostname, - RecordType: "TXT", - Priority: "", - Destination: value, - DeleteRecord: false, - State: "", - TTL: ttl, - } -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/internal/client.go similarity index 97% rename from vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go rename to vendor/github.com/xenolf/lego/providers/dns/nifcloud/internal/client.go index 24a8f52b6..4d229f3b2 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/internal/client.go @@ -1,6 +1,4 @@ -// Package nifcloud implements a DNS provider for solving the DNS-01 challenge -// using NIFCLOUD DNS. -package nifcloud +package internal import ( "bytes" @@ -17,7 +15,8 @@ import ( const ( defaultBaseURL = "https://dns.api.cloud.nifty.com" apiVersion = "2012-12-12N2013-12-16" - xmlNs = "https://route53.amazonaws.com/doc/2012-12-12/" + // XMLNs XML NS of Route53 + XMLNs = "https://route53.amazonaws.com/doc/2012-12-12/" ) // ChangeResourceRecordSetsRequest is a complex type that contains change information for the resource record set. diff --git a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go index c8cf04845..51b6b70a3 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go +++ b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go @@ -1,5 +1,4 @@ -// Package nifcloud implements a DNS provider for solving the DNS-01 challenge -// using NIFCLOUD DNS. +// Package nifcloud implements a DNS provider for solving the DNS-01 challenge using NIFCLOUD DNS. package nifcloud import ( @@ -8,8 +7,11 @@ import ( "net/http" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/providers/dns/nifcloud/internal" + + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" + "github.com/xenolf/lego/platform/wait" ) // Config is used to configure the creation of the DNSProvider @@ -26,9 +28,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("NIFCLOUD_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("NIFCLOUD_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("NIFCLOUD_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("NIFCLOUD_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("NIFCLOUD_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("NIFCLOUD_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("NIFCLOUD_HTTP_TIMEOUT", 30*time.Second), }, @@ -37,7 +39,7 @@ func NewDefaultConfig() *Config { // DNSProvider implements the acme.ChallengeProvider interface type DNSProvider struct { - client *Client + client *internal.Client config *Config } @@ -58,26 +60,13 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for NIFCLOUD. -// Deprecated -func NewDNSProviderCredentials(httpClient *http.Client, endpoint, accessKey, secretKey string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.HTTPClient = httpClient - config.BaseURL = endpoint - config.AccessKey = accessKey - config.SecretKey = secretKey - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for NIFCLOUD. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { return nil, errors.New("nifcloud: the configuration of the DNS provider is nil") } - client, err := NewClient(config.AccessKey, config.SecretKey) + client, err := internal.NewClient(config.AccessKey, config.SecretKey) if err != nil { return nil, fmt.Errorf("nifcloud: %v", err) } @@ -95,7 +84,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) err := d.changeRecord("CREATE", fqdn, value, domain, d.config.TTL) if err != nil { @@ -106,7 +95,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) err := d.changeRecord("DELETE", fqdn, value, domain, d.config.TTL) if err != nil { @@ -122,22 +111,22 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { } func (d *DNSProvider) changeRecord(action, fqdn, value, domain string, ttl int) error { - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) - reqParams := ChangeResourceRecordSetsRequest{ - XMLNs: xmlNs, - ChangeBatch: ChangeBatch{ + reqParams := internal.ChangeResourceRecordSetsRequest{ + XMLNs: internal.XMLNs, + ChangeBatch: internal.ChangeBatch{ Comment: "Managed by Lego", - Changes: Changes{ - Change: []Change{ + Changes: internal.Changes{ + Change: []internal.Change{ { Action: action, - ResourceRecordSet: ResourceRecordSet{ + ResourceRecordSet: internal.ResourceRecordSet{ Name: name, Type: "TXT", TTL: ttl, - ResourceRecords: ResourceRecords{ - ResourceRecord: []ResourceRecord{ + ResourceRecords: internal.ResourceRecords{ + ResourceRecord: []internal.ResourceRecord{ { Value: value, }, @@ -157,7 +146,7 @@ func (d *DNSProvider) changeRecord(action, fqdn, value, domain string, ttl int) statusID := resp.ChangeInfo.ID - return acme.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) { + return wait.For("nifcloud", 120*time.Second, 4*time.Second, func() (bool, error) { resp, err := d.client.GetChange(statusID) if err != nil { return false, fmt.Errorf("failed to query NIFCLOUD DNS change status: %v", err) diff --git a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go index 16be1c900..636912482 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go +++ b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go @@ -1,5 +1,4 @@ -// Package ns1 implements a DNS provider for solving the DNS-01 challenge -// using NS1 DNS. +// Package ns1 implements a DNS provider for solving the DNS-01 challenge using NS1 DNS. package ns1 import ( @@ -9,7 +8,7 @@ import ( "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" "gopkg.in/ns1/ns1-go.v2/rest" @@ -28,9 +27,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("NS1_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("NS1_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("NS1_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("NS1_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("NS1_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("NS1_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("NS1_HTTP_TIMEOUT", 10*time.Second), }, @@ -57,16 +56,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for NS1. -// Deprecated -func NewDNSProviderCredentials(key string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIKey = key - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for NS1. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -84,20 +73,20 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZone(fqdn) if err != nil { return fmt.Errorf("ns1: %v", err) } - record, _, err := d.client.Records.Get(zone.Zone, acme.UnFqdn(fqdn), "TXT") + record, _, err := d.client.Records.Get(zone.Zone, dns01.UnFqdn(fqdn), "TXT") // Create a new record if err == rest.ErrRecordMissing || record == nil { log.Infof("Create a new record for [zone: %s, fqdn: %s, domain: %s]", zone.Zone, fqdn) - record = dns.NewRecord(zone.Zone, acme.UnFqdn(fqdn), "TXT") + record = dns.NewRecord(zone.Zone, dns01.UnFqdn(fqdn), "TXT") record.TTL = d.config.TTL record.Answers = []*dns.Answer{{Rdata: []string{value}}} @@ -128,14 +117,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZone(fqdn) if err != nil { return fmt.Errorf("ns1: %v", err) } - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) _, err = d.client.Records.Delete(zone.Zone, name, "TXT") if err != nil { return fmt.Errorf("ns1: failed to delete record [zone: %q, domain: %q]: %v", zone.Zone, name, err) @@ -164,7 +153,7 @@ func (d *DNSProvider) getHostedZone(fqdn string) (*dns.Zone, error) { } func getAuthZone(fqdn string) (string, error) { - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return "", err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/client.go b/vendor/github.com/xenolf/lego/providers/dns/otc/client.go index 7f4fd34be..1cd71f551 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/otc/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/otc/client.go @@ -1,5 +1,14 @@ package otc +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" +) + type recordset struct { Name string `json:"name"` Description string `json:"description"` @@ -41,14 +50,20 @@ type loginResponse struct { } type endpointResponse struct { - Token struct { - Catalog []struct { - Type string `json:"type"` - Endpoints []struct { - URL string `json:"url"` - } `json:"endpoints"` - } `json:"catalog"` - } `json:"token"` + Token token `json:"token"` +} + +type token struct { + Catalog []catalog `json:"catalog"` +} + +type catalog struct { + Type string `json:"type"` + Endpoints []endpoint `json:"endpoints"` +} + +type endpoint struct { + URL string `json:"url"` } type zoneItem struct { @@ -66,3 +81,183 @@ type recordSet struct { type recordSetsResponse struct { RecordSets []recordSet `json:"recordsets"` } + +// Starts a new OTC API Session. Authenticates using userName, password +// and receives a token to be used in for subsequent requests. +func (d *DNSProvider) login() error { + return d.loginRequest() +} + +func (d *DNSProvider) loginRequest() error { + userResp := userResponse{ + Name: d.config.UserName, + Password: d.config.Password, + Domain: nameResponse{ + Name: d.config.DomainName, + }, + } + + loginResp := loginResponse{ + Auth: authResponse{ + Identity: identityResponse{ + Methods: []string{"password"}, + Password: passwordResponse{ + User: userResp, + }, + }, + Scope: scopeResponse{ + Project: nameResponse{ + Name: d.config.ProjectName, + }, + }, + }, + } + + body, err := json.Marshal(loginResp) + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodPost, d.config.IdentityEndpoint, bytes.NewReader(body)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{Timeout: d.config.HTTPClient.Timeout} + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return fmt.Errorf("OTC API request failed with HTTP status code %d", resp.StatusCode) + } + + d.token = resp.Header.Get("X-Subject-Token") + + if d.token == "" { + return fmt.Errorf("unable to get auth token") + } + + var endpointResp endpointResponse + + err = json.NewDecoder(resp.Body).Decode(&endpointResp) + if err != nil { + return err + } + + var endpoints []endpoint + for _, v := range endpointResp.Token.Catalog { + if v.Type == "dns" { + endpoints = append(endpoints, v.Endpoints...) + } + } + + if len(endpoints) > 0 { + d.baseURL = fmt.Sprintf("%s/v2", endpoints[0].URL) + } else { + return fmt.Errorf("unable to get dns endpoint") + } + + return nil +} + +func (d *DNSProvider) getZoneID(zone string) (string, error) { + resource := fmt.Sprintf("zones?name=%s", zone) + resp, err := d.sendRequest(http.MethodGet, resource, nil) + if err != nil { + return "", err + } + + var zonesRes zonesResponse + err = json.NewDecoder(resp).Decode(&zonesRes) + if err != nil { + return "", err + } + + if len(zonesRes.Zones) < 1 { + return "", fmt.Errorf("zone %s not found", zone) + } + + if len(zonesRes.Zones) > 1 { + return "", fmt.Errorf("to many zones found") + } + + if zonesRes.Zones[0].ID == "" { + return "", fmt.Errorf("id not found") + } + + return zonesRes.Zones[0].ID, nil +} + +func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) { + resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn) + resp, err := d.sendRequest(http.MethodGet, resource, nil) + if err != nil { + return "", err + } + + var recordSetsRes recordSetsResponse + err = json.NewDecoder(resp).Decode(&recordSetsRes) + if err != nil { + return "", err + } + + if len(recordSetsRes.RecordSets) < 1 { + return "", fmt.Errorf("record not found") + } + + if len(recordSetsRes.RecordSets) > 1 { + return "", fmt.Errorf("to many records found") + } + + if recordSetsRes.RecordSets[0].ID == "" { + return "", fmt.Errorf("id not found") + } + + return recordSetsRes.RecordSets[0].ID, nil +} + +func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error { + resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID) + + _, err := d.sendRequest(http.MethodDelete, resource, nil) + return err +} + +func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (io.Reader, error) { + url := fmt.Sprintf("%s/%s", d.baseURL, resource) + + body, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + if len(d.token) > 0 { + req.Header.Set("X-Auth-Token", d.token) + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("OTC API request %s failed with HTTP status code %d", url, resp.StatusCode) + } + + body1, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return bytes.NewReader(body1), nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go b/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go index fd0c40dff..4eeca22fe 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go +++ b/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go @@ -1,19 +1,14 @@ -// Package otc implements a DNS provider for solving the DNS-01 challenge -// using Open Telekom Cloud Managed DNS. +// Package otc implements a DNS provider for solving the DNS-01 challenge using Open Telekom Cloud Managed DNS. package otc import ( - "bytes" - "encoding/json" "errors" "fmt" - "io" - "io/ioutil" "net" "net/http" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -39,8 +34,8 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ IdentityEndpoint: env.GetOrDefaultString("OTC_IDENTITY_ENDPOINT", defaultIdentityEndpoint), - PropagationTimeout: env.GetOrDefaultSecond("OTC_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("OTC_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("OTC_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("OTC_POLLING_INTERVAL", dns01.DefaultPollingInterval), TTL: env.GetOrDefaultInt("OTC_TTL", minTTL), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("OTC_HTTP_TIMEOUT", 10*time.Second), @@ -89,20 +84,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for OTC DNS. -// Deprecated -func NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.IdentityEndpoint = identityEndpoint - config.DomainName = domainName - config.UserName = userName - config.Password = password - config.ProjectName = projectName - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for OTC DNS. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -126,9 +107,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("otc: %v", err) } @@ -162,9 +143,9 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return fmt.Errorf("otc: %v", err) } @@ -196,184 +177,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -// sendRequest send request -func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (io.Reader, error) { - url := fmt.Sprintf("%s/%s", d.baseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - if len(d.token) > 0 { - req.Header.Set("X-Auth-Token", d.token) - } - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return nil, fmt.Errorf("OTC API request %s failed with HTTP status code %d", url, resp.StatusCode) - } - - body1, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - return bytes.NewReader(body1), nil -} - -func (d *DNSProvider) loginRequest() error { - userResp := userResponse{ - Name: d.config.UserName, - Password: d.config.Password, - Domain: nameResponse{ - Name: d.config.DomainName, - }, - } - - loginResp := loginResponse{ - Auth: authResponse{ - Identity: identityResponse{ - Methods: []string{"password"}, - Password: passwordResponse{ - User: userResp, - }, - }, - Scope: scopeResponse{ - Project: nameResponse{ - Name: d.config.ProjectName, - }, - }, - }, - } - - body, err := json.Marshal(loginResp) - if err != nil { - return err - } - - req, err := http.NewRequest(http.MethodPost, d.config.IdentityEndpoint, bytes.NewReader(body)) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - - client := &http.Client{Timeout: d.config.HTTPClient.Timeout} - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return fmt.Errorf("OTC API request failed with HTTP status code %d", resp.StatusCode) - } - - d.token = resp.Header.Get("X-Subject-Token") - - if d.token == "" { - return fmt.Errorf("unable to get auth token") - } - - var endpointResp endpointResponse - - err = json.NewDecoder(resp.Body).Decode(&endpointResp) - if err != nil { - return err - } - - for _, v := range endpointResp.Token.Catalog { - if v.Type == "dns" { - for _, endpoint := range v.Endpoints { - d.baseURL = fmt.Sprintf("%s/v2", endpoint.URL) - continue - } - } - } - - if d.baseURL == "" { - return fmt.Errorf("unable to get dns endpoint") - } - - return nil -} - -// Starts a new OTC API Session. Authenticates using userName, password -// and receives a token to be used in for subsequent requests. -func (d *DNSProvider) login() error { - return d.loginRequest() -} - -func (d *DNSProvider) getZoneID(zone string) (string, error) { - resource := fmt.Sprintf("zones?name=%s", zone) - resp, err := d.sendRequest(http.MethodGet, resource, nil) - if err != nil { - return "", err - } - - var zonesRes zonesResponse - err = json.NewDecoder(resp).Decode(&zonesRes) - if err != nil { - return "", err - } - - if len(zonesRes.Zones) < 1 { - return "", fmt.Errorf("zone %s not found", zone) - } - - if len(zonesRes.Zones) > 1 { - return "", fmt.Errorf("to many zones found") - } - - if zonesRes.Zones[0].ID == "" { - return "", fmt.Errorf("id not found") - } - - return zonesRes.Zones[0].ID, nil -} - -func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) { - resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn) - resp, err := d.sendRequest(http.MethodGet, resource, nil) - if err != nil { - return "", err - } - - var recordSetsRes recordSetsResponse - err = json.NewDecoder(resp).Decode(&recordSetsRes) - if err != nil { - return "", err - } - - if len(recordSetsRes.RecordSets) < 1 { - return "", fmt.Errorf("record not found") - } - - if len(recordSetsRes.RecordSets) > 1 { - return "", fmt.Errorf("to many records found") - } - - if recordSetsRes.RecordSets[0].ID == "" { - return "", fmt.Errorf("id not found") - } - - return recordSetsRes.RecordSets[0].ID, nil -} - -func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error { - resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID) - - _, err := d.sendRequest(http.MethodDelete, resource, nil) - return err -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go index e5e092443..ee01c009d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go +++ b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go @@ -1,5 +1,4 @@ -// Package ovh implements a DNS provider for solving the DNS-01 -// challenge using OVH DNS. +// Package ovh implements a DNS provider for solving the DNS-01 challenge using OVH DNS. package ovh import ( @@ -11,13 +10,23 @@ import ( "time" "github.com/ovh/go-ovh/ovh" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) // OVH API reference: https://eu.api.ovh.com/ // Create a Token: https://eu.api.ovh.com/createToken/ +// Record a DNS record +type Record struct { + ID int `json:"id,omitempty"` + FieldType string `json:"fieldType,omitempty"` + SubDomain string `json:"subDomain,omitempty"` + Target string `json:"target,omitempty"` + TTL int `json:"ttl,omitempty"` + Zone string `json:"zone,omitempty"` +} + // Config is used to configure the creation of the DNSProvider type Config struct { APIEndpoint string @@ -33,9 +42,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("OVH_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("OVH_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("OVH_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("OVH_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("OVH_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("OVH_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("OVH_HTTP_TIMEOUT", ovh.DefaultTimeout), }, @@ -72,19 +81,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for OVH. -// Deprecated -func NewDNSProviderCredentials(apiEndpoint, applicationKey, applicationSecret, consumerKey string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIEndpoint = apiEndpoint - config.ApplicationKey = applicationKey - config.ApplicationSecret = applicationSecret - config.ConsumerKey = consumerKey - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for OVH. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -116,32 +112,32 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) // Parse domain name - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return fmt.Errorf("ovh: could not determine zone for domain: '%s'. %s", domain, err) } - authZone = acme.UnFqdn(authZone) + authZone = dns01.UnFqdn(authZone) subDomain := d.extractRecordName(fqdn, authZone) reqURL := fmt.Sprintf("/domain/zone/%s/record", authZone) - reqData := txtRecordRequest{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: d.config.TTL} - var respData txtRecordResponse + reqData := Record{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: d.config.TTL} // Create TXT record + var respData Record err = d.client.Post(reqURL, reqData, &respData) if err != nil { - return fmt.Errorf("ovh: error when call api to add record: %v", err) + return fmt.Errorf("ovh: error when call api to add record (%s): %v", reqURL, err) } // Apply the change reqURL = fmt.Sprintf("/domain/zone/%s/refresh", authZone) err = d.client.Post(reqURL, nil, nil) if err != nil { - return fmt.Errorf("ovh: error when call api to refresh zone: %v", err) + return fmt.Errorf("ovh: error when call api to refresh zone (%s): %v", reqURL, err) } d.recordIDsMu.Lock() @@ -153,7 +149,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() @@ -163,18 +159,18 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("ovh: unknown record ID for '%s'", fqdn) } - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return fmt.Errorf("ovh: could not determine zone for domain: '%s'. %s", domain, err) } - authZone = acme.UnFqdn(authZone) + authZone = dns01.UnFqdn(authZone) reqURL := fmt.Sprintf("/domain/zone/%s/record/%d", authZone, recordID) err = d.client.Delete(reqURL, nil) if err != nil { - return fmt.Errorf("ovh: error when call OVH api to delete challenge record: %v", err) + return fmt.Errorf("ovh: error when call OVH api to delete challenge record (%s): %v", reqURL, err) } // Delete record ID from map @@ -192,27 +188,9 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { } func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } return name } - -// txtRecordRequest represents the request body to DO's API to make a TXT record -type txtRecordRequest struct { - FieldType string `json:"fieldType"` - SubDomain string `json:"subDomain"` - Target string `json:"target"` - TTL int `json:"ttl"` -} - -// txtRecordResponse represents a response from DO's API after making a TXT record -type txtRecordResponse struct { - ID int `json:"id"` - FieldType string `json:"fieldType"` - SubDomain string `json:"subDomain"` - Target string `json:"target"` - TTL int `json:"ttl"` - Zone string `json:"zone"` -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/pdns/client.go b/vendor/github.com/xenolf/lego/providers/dns/pdns/client.go new file mode 100644 index 000000000..88f892c3b --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/pdns/client.go @@ -0,0 +1,220 @@ +package pdns + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" + "strings" + + "github.com/xenolf/lego/challenge/dns01" +) + +type Record struct { + Content string `json:"content"` + Disabled bool `json:"disabled"` + + // pre-v1 API + Name string `json:"name"` + Type string `json:"type"` + TTL int `json:"ttl,omitempty"` +} + +type hostedZone struct { + ID string `json:"id"` + Name string `json:"name"` + URL string `json:"url"` + RRSets []rrSet `json:"rrsets"` + + // pre-v1 API + Records []Record `json:"records"` +} + +type rrSet struct { + Name string `json:"name"` + Type string `json:"type"` + Kind string `json:"kind"` + ChangeType string `json:"changetype"` + Records []Record `json:"records"` + TTL int `json:"ttl,omitempty"` +} + +type rrSets struct { + RRSets []rrSet `json:"rrsets"` +} + +type apiError struct { + ShortMsg string `json:"error"` +} + +func (a apiError) Error() string { + return a.ShortMsg +} + +type apiVersion struct { + URL string `json:"url"` + Version int `json:"version"` +} + +func (d *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { + var zone hostedZone + authZone, err := dns01.FindZoneByFqdn(fqdn) + if err != nil { + return nil, err + } + + u := "/servers/localhost/zones" + result, err := d.sendRequest(http.MethodGet, u, nil) + if err != nil { + return nil, err + } + + var zones []hostedZone + err = json.Unmarshal(result, &zones) + if err != nil { + return nil, err + } + + u = "" + for _, zone := range zones { + if dns01.UnFqdn(zone.Name) == dns01.UnFqdn(authZone) { + u = zone.URL + break + } + } + + result, err = d.sendRequest(http.MethodGet, u, nil) + if err != nil { + return nil, err + } + + err = json.Unmarshal(result, &zone) + if err != nil { + return nil, err + } + + // convert pre-v1 API result + if len(zone.Records) > 0 { + zone.RRSets = []rrSet{} + for _, record := range zone.Records { + set := rrSet{ + Name: record.Name, + Type: record.Type, + Records: []Record{record}, + } + zone.RRSets = append(zone.RRSets, set) + } + } + + return &zone, nil +} + +func (d *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { + zone, err := d.getHostedZone(fqdn) + if err != nil { + return nil, err + } + + _, err = d.sendRequest(http.MethodGet, zone.URL, nil) + if err != nil { + return nil, err + } + + for _, set := range zone.RRSets { + if (set.Name == dns01.UnFqdn(fqdn) || set.Name == fqdn) && set.Type == "TXT" { + return &set, nil + } + } + + return nil, fmt.Errorf("no existing record found for %s", fqdn) +} + +func (d *DNSProvider) getAPIVersion() (int, error) { + result, err := d.sendRequest(http.MethodGet, "/api", nil) + if err != nil { + return 0, err + } + + var versions []apiVersion + err = json.Unmarshal(result, &versions) + if err != nil { + return 0, err + } + + latestVersion := 0 + for _, v := range versions { + if v.Version > latestVersion { + latestVersion = v.Version + } + } + + return latestVersion, err +} + +func (d *DNSProvider) sendRequest(method, uri string, body io.Reader) (json.RawMessage, error) { + req, err := d.makeRequest(method, uri, body) + if err != nil { + return nil, err + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error talking to PDNS API -> %v", err) + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusUnprocessableEntity && (resp.StatusCode < 200 || resp.StatusCode >= 300) { + return nil, fmt.Errorf("unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, req.URL) + } + + var msg json.RawMessage + err = json.NewDecoder(resp.Body).Decode(&msg) + if err != nil { + if err == io.EOF { + // empty body + return nil, nil + } + // other error + return nil, err + } + + // check for PowerDNS error message + if len(msg) > 0 && msg[0] == '{' { + var errInfo apiError + err = json.Unmarshal(msg, &errInfo) + if err != nil { + return nil, err + } + if errInfo.ShortMsg != "" { + return nil, fmt.Errorf("error talking to PDNS API -> %v", errInfo) + } + } + return msg, nil +} + +func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Request, error) { + var path = "" + if d.config.Host.Path != "/" { + path = d.config.Host.Path + } + + if !strings.HasPrefix(uri, "/") { + uri = "/" + uri + } + + if d.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") { + uri = "/api/v" + strconv.Itoa(d.apiVersion) + uri + } + + u := d.config.Host.Scheme + "://" + d.config.Host.Host + path + uri + req, err := http.NewRequest(method, u, body) + if err != nil { + return nil, err + } + + req.Header.Set("X-API-Key", d.config.APIKey) + + return req, nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go index 8dc9c175e..195102898 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go @@ -1,5 +1,4 @@ -// Package pdns implements a DNS provider for solving the DNS-01 -// challenge using PowerDNS nameserver. +// Package pdns implements a DNS provider for solving the DNS-01 challenge using PowerDNS nameserver. package pdns import ( @@ -7,14 +6,11 @@ import ( "encoding/json" "errors" "fmt" - "io" "net/http" "net/url" - "strconv" - "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" ) @@ -32,7 +28,7 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("PDNS_TTL", 120), + TTL: env.GetOrDefaultInt("PDNS_TTL", dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond("PDNS_PROPAGATION_TIMEOUT", 120*time.Second), PollingInterval: env.GetOrDefaultSecond("PDNS_POLLING_INTERVAL", 2*time.Second), HTTPClient: &http.Client{ @@ -68,17 +64,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for pdns. -// Deprecated -func NewDNSProviderCredentials(host *url.URL, key string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.Host = host - config.APIKey = key - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for pdns. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -112,7 +97,8 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) + zone, err := d.getHostedZone(fqdn) if err != nil { return fmt.Errorf("pdns: %v", err) @@ -122,10 +108,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // pre-v1 API wants non-fqdn if d.apiVersion == 0 { - name = acme.UnFqdn(fqdn) + name = dns01.UnFqdn(fqdn) } - rec := pdnsRecord{ + rec := Record{ Content: "\"" + value + "\"", Disabled: false, @@ -143,7 +129,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { Type: "TXT", Kind: "Master", TTL: d.config.TTL, - Records: []pdnsRecord{rec}, + Records: []Record{rec}, }, }, } @@ -153,7 +139,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("pdns: %v", err) } - _, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) + _, err = d.sendRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) if err != nil { return fmt.Errorf("pdns: %v", err) } @@ -162,7 +148,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZone(fqdn) if err != nil { @@ -188,203 +174,9 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("pdns: %v", err) } - _, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) + _, err = d.sendRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) if err != nil { return fmt.Errorf("pdns: %v", err) } return nil } - -func (d *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { - var zone hostedZone - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return nil, err - } - - u := "/servers/localhost/zones" - result, err := d.makeRequest(http.MethodGet, u, nil) - if err != nil { - return nil, err - } - - var zones []hostedZone - err = json.Unmarshal(result, &zones) - if err != nil { - return nil, err - } - - u = "" - for _, zone := range zones { - if acme.UnFqdn(zone.Name) == acme.UnFqdn(authZone) { - u = zone.URL - } - } - - result, err = d.makeRequest(http.MethodGet, u, nil) - if err != nil { - return nil, err - } - - err = json.Unmarshal(result, &zone) - if err != nil { - return nil, err - } - - // convert pre-v1 API result - if len(zone.Records) > 0 { - zone.RRSets = []rrSet{} - for _, record := range zone.Records { - set := rrSet{ - Name: record.Name, - Type: record.Type, - Records: []pdnsRecord{record}, - } - zone.RRSets = append(zone.RRSets, set) - } - } - - return &zone, nil -} - -func (d *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { - zone, err := d.getHostedZone(fqdn) - if err != nil { - return nil, err - } - - _, err = d.makeRequest(http.MethodGet, zone.URL, nil) - if err != nil { - return nil, err - } - - for _, set := range zone.RRSets { - if (set.Name == acme.UnFqdn(fqdn) || set.Name == fqdn) && set.Type == "TXT" { - return &set, nil - } - } - - return nil, fmt.Errorf("no existing record found for %s", fqdn) -} - -func (d *DNSProvider) getAPIVersion() (int, error) { - type APIVersion struct { - URL string `json:"url"` - Version int `json:"version"` - } - - result, err := d.makeRequest(http.MethodGet, "/api", nil) - if err != nil { - return 0, err - } - - var versions []APIVersion - err = json.Unmarshal(result, &versions) - if err != nil { - return 0, err - } - - latestVersion := 0 - for _, v := range versions { - if v.Version > latestVersion { - latestVersion = v.Version - } - } - - return latestVersion, err -} - -func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - type APIError struct { - Error string `json:"error"` - } - - var path = "" - if d.config.Host.Path != "/" { - path = d.config.Host.Path - } - - if !strings.HasPrefix(uri, "/") { - uri = "/" + uri - } - - if d.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") { - uri = "/api/v" + strconv.Itoa(d.apiVersion) + uri - } - - u := d.config.Host.Scheme + "://" + d.config.Host.Host + path + uri - req, err := http.NewRequest(method, u, body) - if err != nil { - return nil, err - } - - req.Header.Set("X-API-Key", d.config.APIKey) - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("error talking to PDNS API -> %v", err) - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusUnprocessableEntity && (resp.StatusCode < 200 || resp.StatusCode >= 300) { - return nil, fmt.Errorf("unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, u) - } - - var msg json.RawMessage - err = json.NewDecoder(resp.Body).Decode(&msg) - switch { - case err == io.EOF: - // empty body - return nil, nil - case err != nil: - // other error - return nil, err - } - - // check for PowerDNS error message - if len(msg) > 0 && msg[0] == '{' { - var apiError APIError - err = json.Unmarshal(msg, &apiError) - if err != nil { - return nil, err - } - if apiError.Error != "" { - return nil, fmt.Errorf("error talking to PDNS API -> %v", apiError.Error) - } - } - return msg, nil -} - -type pdnsRecord struct { - Content string `json:"content"` - Disabled bool `json:"disabled"` - - // pre-v1 API - Name string `json:"name"` - Type string `json:"type"` - TTL int `json:"ttl,omitempty"` -} - -type hostedZone struct { - ID string `json:"id"` - Name string `json:"name"` - URL string `json:"url"` - RRSets []rrSet `json:"rrsets"` - - // pre-v1 API - Records []pdnsRecord `json:"records"` -} - -type rrSet struct { - Name string `json:"name"` - Type string `json:"type"` - Kind string `json:"kind"` - ChangeType string `json:"changetype"` - Records []pdnsRecord `json:"records"` - TTL int `json:"ttl,omitempty"` -} - -type rrSets struct { - RRSets []rrSet `json:"rrsets"` -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rackspace/client.go b/vendor/github.com/xenolf/lego/providers/dns/rackspace/client.go index 54b05c9db..e1085626a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/rackspace/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/rackspace/client.go @@ -1,5 +1,15 @@ package rackspace +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/xenolf/lego/challenge/dns01" +) + // APIKeyCredentials API credential type APIKeyCredentials struct { Username string `json:"username"` @@ -69,3 +79,127 @@ type Record struct { TTL int `json:"ttl,omitempty"` ID string `json:"id,omitempty"` } + +// getHostedZoneID performs a lookup to get the DNS zone which needs +// modifying for a given FQDN +func (d *DNSProvider) getHostedZoneID(fqdn string) (int, error) { + authZone, err := dns01.FindZoneByFqdn(fqdn) + if err != nil { + return 0, err + } + + result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains?name=%s", dns01.UnFqdn(authZone)), nil) + if err != nil { + return 0, err + } + + var zoneSearchResponse ZoneSearchResponse + err = json.Unmarshal(result, &zoneSearchResponse) + if err != nil { + return 0, err + } + + // If nothing was returned, or for whatever reason more than 1 was returned (the search uses exact match, so should not occur) + if zoneSearchResponse.TotalEntries != 1 { + return 0, fmt.Errorf("found %d zones for %s in Rackspace for domain %s", zoneSearchResponse.TotalEntries, authZone, fqdn) + } + + return zoneSearchResponse.HostedZones[0].ID, nil +} + +// findTxtRecord searches a DNS zone for a TXT record with a specific name +func (d *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) { + result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, dns01.UnFqdn(fqdn)), nil) + if err != nil { + return nil, err + } + + var records Records + err = json.Unmarshal(result, &records) + if err != nil { + return nil, err + } + + switch len(records.Record) { + case 1: + case 0: + return nil, fmt.Errorf("no TXT record found for %s", fqdn) + default: + return nil, fmt.Errorf("more than 1 TXT record found for %s", fqdn) + } + + return &records.Record[0], nil +} + +// makeRequest is a wrapper function used for making DNS API requests +func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { + url := d.cloudDNSEndpoint + uri + + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + + req.Header.Set("X-Auth-Token", d.token) + req.Header.Set("Content-Type", "application/json") + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error querying DNS API: %v", err) + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted { + return nil, fmt.Errorf("request failed for %s %s. Response code: %d", method, url, resp.StatusCode) + } + + var r json.RawMessage + err = json.NewDecoder(resp.Body).Decode(&r) + if err != nil { + return nil, fmt.Errorf("JSON decode failed for %s %s. Response code: %d", method, url, resp.StatusCode) + } + + return r, nil +} + +func login(config *Config) (*Identity, error) { + authData := AuthData{ + Auth: Auth{ + APIKeyCredentials: APIKeyCredentials{ + Username: config.APIUser, + APIKey: config.APIKey, + }, + }, + } + + body, err := json.Marshal(authData) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, config.BaseURL, bytes.NewReader(body)) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := config.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error querying Identity API: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("authentication failed: response code: %d", resp.StatusCode) + } + + var identity Identity + err = json.NewDecoder(resp.Body).Decode(&identity) + if err != nil { + return nil, err + } + + return &identity, nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go index 7b5e73d43..a0ddb4aad 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go +++ b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go @@ -1,5 +1,4 @@ -// Package rackspace implements a DNS provider for solving the DNS-01 -// challenge using rackspace DNS. +// Package rackspace implements a DNS provider for solving the DNS-01 challenge using rackspace DNS. package rackspace import ( @@ -7,11 +6,10 @@ import ( "encoding/json" "errors" "fmt" - "io" "net/http" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -34,8 +32,8 @@ func NewDefaultConfig() *Config { return &Config{ BaseURL: defaultBaseURL, TTL: env.GetOrDefaultInt("RACKSPACE_TTL", 300), - PropagationTimeout: env.GetOrDefaultSecond("RACKSPACE_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("RACKSPACE_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("RACKSPACE_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("RACKSPACE_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("RACKSPACE_HTTP_TIMEOUT", 30*time.Second), }, @@ -66,18 +64,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Rackspace. -// It authenticates against the API, also grabbing the DNS Endpoint. -// Deprecated -func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIUser = user - config.APIKey = key - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Rackspace. // It authenticates against the API, also grabbing the DNS Endpoint. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { @@ -117,7 +103,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zoneID, err := d.getHostedZoneID(fqdn) if err != nil { @@ -126,7 +112,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { rec := Records{ Record: []Record{{ - Name: acme.UnFqdn(fqdn), + Name: dns01.UnFqdn(fqdn), Type: "TXT", Data: value, TTL: d.config.TTL, @@ -147,7 +133,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zoneID, err := d.getHostedZoneID(fqdn) if err != nil { @@ -171,127 +157,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -// getHostedZoneID performs a lookup to get the DNS zone which needs -// modifying for a given FQDN -func (d *DNSProvider) getHostedZoneID(fqdn string) (int, error) { - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return 0, err - } - - result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil) - if err != nil { - return 0, err - } - - var zoneSearchResponse ZoneSearchResponse - err = json.Unmarshal(result, &zoneSearchResponse) - if err != nil { - return 0, err - } - - // If nothing was returned, or for whatever reason more than 1 was returned (the search uses exact match, so should not occur) - if zoneSearchResponse.TotalEntries != 1 { - return 0, fmt.Errorf("found %d zones for %s in Rackspace for domain %s", zoneSearchResponse.TotalEntries, authZone, fqdn) - } - - return zoneSearchResponse.HostedZones[0].ID, nil -} - -// findTxtRecord searches a DNS zone for a TXT record with a specific name -func (d *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) { - result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil) - if err != nil { - return nil, err - } - - var records Records - err = json.Unmarshal(result, &records) - if err != nil { - return nil, err - } - - switch len(records.Record) { - case 1: - case 0: - return nil, fmt.Errorf("no TXT record found for %s", fqdn) - default: - return nil, fmt.Errorf("more than 1 TXT record found for %s", fqdn) - } - - return &records.Record[0], nil -} - -// makeRequest is a wrapper function used for making DNS API requests -func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - url := d.cloudDNSEndpoint + uri - - req, err := http.NewRequest(method, url, body) - if err != nil { - return nil, err - } - - req.Header.Set("X-Auth-Token", d.token) - req.Header.Set("Content-Type", "application/json") - - resp, err := d.config.HTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("error querying DNS API: %v", err) - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted { - return nil, fmt.Errorf("request failed for %s %s. Response code: %d", method, url, resp.StatusCode) - } - - var r json.RawMessage - err = json.NewDecoder(resp.Body).Decode(&r) - if err != nil { - return nil, fmt.Errorf("JSON decode failed for %s %s. Response code: %d", method, url, resp.StatusCode) - } - - return r, nil -} - -func login(config *Config) (*Identity, error) { - authData := AuthData{ - Auth: Auth{ - APIKeyCredentials: APIKeyCredentials{ - Username: config.APIUser, - APIKey: config.APIKey, - }, - }, - } - - body, err := json.Marshal(authData) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, config.BaseURL, bytes.NewReader(body)) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := config.HTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("error querying Identity API: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("authentication failed: response code: %d", resp.StatusCode) - } - - var identity Identity - err = json.NewDecoder(resp.Body).Decode(&identity) - if err != nil { - return nil, err - } - - return &identity, nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go b/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go index 797b90a87..dcc80197f 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go +++ b/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go @@ -1,5 +1,4 @@ -// Package rfc2136 implements a DNS provider for solving the DNS-01 challenge -// using the rfc2136 dynamic update. +// Package rfc2136 implements a DNS provider for solving the DNS-01 challenge using the rfc2136 dynamic update. package rfc2136 import ( @@ -10,12 +9,10 @@ import ( "time" "github.com/miekg/dns" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) -const defaultTimeout = 60 * time.Second - // Config is used to configure the creation of the DNSProvider type Config struct { Nameserver string @@ -31,7 +28,7 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ TSIGAlgorithm: env.GetOrDefaultString("RFC2136_TSIG_ALGORITHM", dns.HmacMD5), - TTL: env.GetOrDefaultInt("RFC2136_TTL", 120), + TTL: env.GetOrDefaultInt("RFC2136_TTL", dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond("RFC2136_PROPAGATION_TIMEOUT", env.GetOrDefaultSecond("RFC2136_TIMEOUT", 60*time.Second)), PollingInterval: env.GetOrDefaultSecond("RFC2136_POLLING_INTERVAL", 2*time.Second), @@ -67,34 +64,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for rfc2136 dynamic update. -// To disable TSIG authentication, leave the TSIG parameters as empty strings. -// nameserver must be a network address in the form "host" or "host:port". -// Deprecated -func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, rawTimeout string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.Nameserver = nameserver - config.TSIGAlgorithm = tsigAlgorithm - config.TSIGKey = tsigKey - config.TSIGSecret = tsigSecret - - timeout := defaultTimeout - if rawTimeout != "" { - t, err := time.ParseDuration(rawTimeout) - if err != nil { - return nil, err - } else if t < 0 { - return nil, fmt.Errorf("rfc2136: invalid/negative RFC2136_TIMEOUT: %v", rawTimeout) - } else { - timeout = t - } - } - config.PropagationTimeout = timeout - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for rfc2136. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -135,7 +104,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) err := d.changeRecord("INSERT", fqdn, value, d.config.TTL) if err != nil { @@ -146,7 +115,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) err := d.changeRecord("REMOVE", fqdn, value, d.config.TTL) if err != nil { @@ -157,7 +126,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { // Find the zone for the given fqdn - zone, err := acme.FindZoneByFqdn(fqdn, []string{d.config.Nameserver}) + zone, err := dns01.FindZoneByFqdnCustom(fqdn, []string{d.config.Nameserver}) if err != nil { return err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go b/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go index 1cace78e9..ad6ab5393 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go +++ b/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go @@ -1,5 +1,4 @@ -// Package route53 implements a DNS provider for solving the DNS-01 challenge -// using AWS Route 53 DNS. +// Package route53 implements a DNS provider for solving the DNS-01 challenge using AWS Route 53 DNS. package route53 import ( @@ -14,8 +13,9 @@ import ( "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/route53" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" + "github.com/xenolf/lego/platform/wait" ) // Config is used to configure the creation of the DNSProvider @@ -107,7 +107,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) hostedZoneID, err := d.getHostedZoneID(fqdn) if err != nil { @@ -148,7 +148,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) hostedZoneID, err := d.getHostedZoneID(fqdn) if err != nil { @@ -197,7 +197,7 @@ func (d *DNSProvider) changeRecord(action, hostedZoneID string, recordSet *route changeID := resp.ChangeInfo.Id - return acme.WaitFor(d.config.PropagationTimeout, d.config.PollingInterval, func() (bool, error) { + return wait.For("route53", d.config.PropagationTimeout, d.config.PollingInterval, func() (bool, error) { reqParams := &route53.GetChangeInput{Id: changeID} resp, err := d.client.GetChange(reqParams) @@ -244,14 +244,14 @@ func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { return d.config.HostedZoneID, nil } - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return "", err } // .DNSName should not have a trailing dot reqParams := &route53.ListHostedZonesByNameInput{ - DNSName: aws.String(acme.UnFqdn(authZone)), + DNSName: aws.String(dns01.UnFqdn(authZone)), } resp, err := d.client.ListHostedZonesByName(reqParams) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go b/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go index b0227f6ce..54c92a9f7 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go +++ b/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go @@ -1,5 +1,4 @@ -// Package sakuracloud implements a DNS provider for solving the DNS-01 challenge -// using sakuracloud DNS. +// Package sakuracloud implements a DNS provider for solving the DNS-01 challenge using SakuraCloud DNS. package sakuracloud import ( @@ -11,7 +10,7 @@ import ( "github.com/sacloud/libsacloud/api" "github.com/sacloud/libsacloud/sacloud" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -27,9 +26,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("SAKURACLOUD_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("SAKURACLOUD_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("SAKURACLOUD_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("SAKURACLOUD_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("SAKURACLOUD_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("SAKURACLOUD_POLLING_INTERVAL", dns01.DefaultPollingInterval), } } @@ -39,7 +38,7 @@ type DNSProvider struct { client *api.Client } -// NewDNSProvider returns a DNSProvider instance configured for sakuracloud. +// NewDNSProvider returns a DNSProvider instance configured for SakuraCloud. // Credentials must be passed in the environment variables: SAKURACLOUD_ACCESS_TOKEN & SAKURACLOUD_ACCESS_TOKEN_SECRET func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("SAKURACLOUD_ACCESS_TOKEN", "SAKURACLOUD_ACCESS_TOKEN_SECRET") @@ -54,18 +53,7 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for sakuracloud. -// Deprecated -func NewDNSProviderCredentials(token, secret string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.Token = token - config.Secret = secret - - return NewDNSProviderConfig(config) -} - -// NewDNSProviderConfig return a DNSProvider instance configured for GleSYS. +// NewDNSProviderConfig return a DNSProvider instance configured for SakuraCloud. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { return nil, errors.New("sakuracloud: the configuration of the DNS provider is nil") @@ -80,14 +68,13 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { } client := api.NewClient(config.Token, config.Secret, "tk1a") - client.UserAgent = acme.UserAgent return &DNSProvider{client: client, config: config}, nil } // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZone(domain) if err != nil { @@ -107,7 +94,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zone, err := d.getHostedZone(domain) if err != nil { @@ -144,12 +131,12 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { } func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) { - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) if err != nil { return nil, err } - zoneName := acme.UnFqdn(authZone) + zoneName := dns01.UnFqdn(authZone) res, err := d.client.GetDNSAPI().WithNameLike(zoneName).Find() if err != nil { @@ -181,7 +168,7 @@ func (d *DNSProvider) findTxtRecords(fqdn string, zone *sacloud.DNS) ([]sacloud. } func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } diff --git a/vendor/github.com/xenolf/lego/providers/dns/vscale/client.go b/vendor/github.com/xenolf/lego/providers/dns/selectel/internal/client.go similarity index 99% rename from vendor/github.com/xenolf/lego/providers/dns/vscale/client.go rename to vendor/github.com/xenolf/lego/providers/dns/selectel/internal/client.go index 1f4826f29..129d2b953 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/vscale/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/selectel/internal/client.go @@ -1,4 +1,4 @@ -package vscale +package internal import ( "bytes" diff --git a/vendor/github.com/xenolf/lego/providers/dns/selectel/selectel.go b/vendor/github.com/xenolf/lego/providers/dns/selectel/selectel.go index 9e48b85dc..f1e239653 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/selectel/selectel.go +++ b/vendor/github.com/xenolf/lego/providers/dns/selectel/selectel.go @@ -9,8 +9,9 @@ import ( "net/http" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" + "github.com/xenolf/lego/providers/dns/selectel/internal" ) const ( @@ -54,7 +55,7 @@ func NewDefaultConfig() *Config { // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { config *Config - client *Client + client *internal.Client } // NewDNSProvider returns a DNSProvider instance configured for Selectel Domains API. @@ -85,10 +86,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, fmt.Errorf("selectel: invalid TTL, TTL (%d) must be greater than %d", config.TTL, minTTL) } - client := NewClient(ClientOpts{ + client := internal.NewClient(internal.ClientOpts{ BaseURL: config.BaseURL, Token: config.Token, - UserAgent: acme.UserAgent, HTTPClient: config.HTTPClient, }) @@ -103,14 +103,14 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill DNS-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) domainObj, err := d.client.GetDomainByName(domain) if err != nil { return fmt.Errorf("selectel: %v", err) } - txtRecord := Record{ + txtRecord := internal.Record{ Type: "TXT", TTL: d.config.TTL, Name: fqdn, @@ -126,7 +126,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes a TXT record used for DNS-01 challenge. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) domainObj, err := d.client.GetDomainByName(domain) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/stackpath/client.go b/vendor/github.com/xenolf/lego/providers/dns/stackpath/client.go index 495d8c555..4665ca56c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/stackpath/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/stackpath/client.go @@ -8,7 +8,7 @@ import ( "net/http" "path" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "golang.org/x/net/publicsuffix" ) @@ -49,7 +49,7 @@ func (e *ErrorResponse) Error() string { // https://developer.stackpath.com/en/api/dns/#operation/GetZones func (d *DNSProvider) getZones(domain string) (*Zone, error) { - domain = acme.UnFqdn(domain) + domain = dns01.UnFqdn(domain) tld, err := publicsuffix.EffectiveTLDPlusOne(domain) if err != nil { return nil, err diff --git a/vendor/github.com/xenolf/lego/providers/dns/stackpath/stackpath.go b/vendor/github.com/xenolf/lego/providers/dns/stackpath/stackpath.go index 4c247def7..df05aee7b 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/stackpath/stackpath.go +++ b/vendor/github.com/xenolf/lego/providers/dns/stackpath/stackpath.go @@ -6,13 +6,13 @@ import ( "context" "errors" "fmt" - "log" "net/http" "net/url" "strings" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" + "github.com/xenolf/lego/log" "github.com/xenolf/lego/platform/config/env" "golang.org/x/oauth2/clientcredentials" ) @@ -36,8 +36,8 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ TTL: env.GetOrDefaultInt("STACKPATH_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("STACKPATH_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("STACKPATH_POLLING_INTERVAL", acme.DefaultPollingInterval), + PropagationTimeout: env.GetOrDefaultSecond("STACKPATH_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("STACKPATH_POLLING_INTERVAL", dns01.DefaultPollingInterval), } } @@ -105,7 +105,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("stackpath: %v", err) } - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) parts := strings.Split(fqdn, ".") record := Record{ @@ -125,7 +125,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("stackpath: %v", err) } - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) parts := strings.Split(fqdn, ".") records, err := d.getZoneRecords(parts[0], zone) diff --git a/vendor/github.com/xenolf/lego/providers/dns/transip/transip.go b/vendor/github.com/xenolf/lego/providers/dns/transip/transip.go index aeb155f8f..062fc91aa 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/transip/transip.go +++ b/vendor/github.com/xenolf/lego/providers/dns/transip/transip.go @@ -9,7 +9,7 @@ import ( "github.com/transip/gotransip" transipdomain "github.com/transip/gotransip/domain" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -78,17 +78,17 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return err } - domainName := acme.UnFqdn(authZone) + domainName := dns01.UnFqdn(authZone) // get the subDomain - subDomain := strings.TrimSuffix(acme.UnFqdn(fqdn), "."+domainName) + subDomain := strings.TrimSuffix(dns01.UnFqdn(fqdn), "."+domainName) // get all DNS entries info, err := transipdomain.GetInfo(d.client, domainName) @@ -114,17 +114,17 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { return err } - domainName := acme.UnFqdn(authZone) + domainName := dns01.UnFqdn(authZone) // get the subDomain - subDomain := strings.TrimSuffix(acme.UnFqdn(fqdn), "."+domainName) + subDomain := strings.TrimSuffix(dns01.UnFqdn(fqdn), "."+domainName) // get all DNS entries info, err := transipdomain.GetInfo(d.client, domainName) diff --git a/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go b/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go index 6b4cf7d14..c1e218dac 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go @@ -1,5 +1,4 @@ -// Package vegadns implements a DNS provider for solving the DNS-01 -// challenge using VegaDNS. +// Package vegadns implements a DNS provider for solving the DNS-01 challenge using VegaDNS. package vegadns import ( @@ -9,7 +8,7 @@ import ( "time" vegaClient "github.com/OpenDNS/vegadns2client" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -55,18 +54,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for VegaDNS. -// Deprecated -func NewDNSProviderCredentials(vegaDNSURL string, key string, secret string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.BaseURL = vegaDNSURL - config.APIKey = key - config.APISecret = secret - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for VegaDNS. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -88,7 +75,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) _, domainID, err := d.client.GetAuthZone(fqdn) if err != nil { @@ -104,7 +91,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) _, domainID, err := d.client.GetAuthZone(fqdn) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/selectel/client.go b/vendor/github.com/xenolf/lego/providers/dns/vscale/internal/client.go similarity index 99% rename from vendor/github.com/xenolf/lego/providers/dns/selectel/client.go rename to vendor/github.com/xenolf/lego/providers/dns/vscale/internal/client.go index cb02230a0..129d2b953 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/selectel/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/vscale/internal/client.go @@ -1,4 +1,4 @@ -package selectel +package internal import ( "bytes" diff --git a/vendor/github.com/xenolf/lego/providers/dns/vscale/vscale.go b/vendor/github.com/xenolf/lego/providers/dns/vscale/vscale.go index b44b159d9..69060be3d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/vscale/vscale.go +++ b/vendor/github.com/xenolf/lego/providers/dns/vscale/vscale.go @@ -1,4 +1,4 @@ -// Package selectel implements a DNS provider for solving the DNS-01 challenge using Vscale Domains API. +// Package vscale implements a DNS provider for solving the DNS-01 challenge using Vscale Domains API. // Vscale Domain API reference: https://developers.vscale.io/documentation/api/v1/#api-Domains // Token: https://vscale.io/panel/settings/tokens/ package vscale @@ -9,7 +9,9 @@ import ( "net/http" "time" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" + "github.com/xenolf/lego/providers/dns/vscale/internal" + "github.com/xenolf/lego/platform/config/env" ) @@ -54,7 +56,7 @@ func NewDefaultConfig() *Config { // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { config *Config - client *Client + client *internal.Client } // NewDNSProvider returns a DNSProvider instance configured for Vscale Domains API. @@ -85,10 +87,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, fmt.Errorf("vscale: invalid TTL, TTL (%d) must be greater than %d", config.TTL, minTTL) } - client := NewClient(ClientOpts{ + client := internal.NewClient(internal.ClientOpts{ BaseURL: config.BaseURL, Token: config.Token, - UserAgent: acme.UserAgent, HTTPClient: config.HTTPClient, }) @@ -103,14 +104,14 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record to fulfill DNS-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) domainObj, err := d.client.GetDomainByName(domain) if err != nil { return fmt.Errorf("vscale: %v", err) } - txtRecord := Record{ + txtRecord := internal.Record{ Type: "TXT", TTL: d.config.TTL, Name: fqdn, @@ -126,7 +127,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes a TXT record used for DNS-01 challenge. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) domainObj, err := d.client.GetDomainByName(domain) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go b/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go index 80feb9f6d..9faa3d601 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go +++ b/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go @@ -1,5 +1,4 @@ -// Package vultr implements a DNS provider for solving the DNS-01 challenge using -// the vultr DNS. +// Package vultr implements a DNS provider for solving the DNS-01 challenge using the vultr DNS. // See https://www.vultr.com/api/#dns package vultr @@ -12,7 +11,7 @@ import ( "time" vultr "github.com/JamesClonk/vultr/lib" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/challenge/dns01" "github.com/xenolf/lego/platform/config/env" ) @@ -28,9 +27,9 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt("VULTR_TTL", 120), - PropagationTimeout: env.GetOrDefaultSecond("VULTR_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), - PollingInterval: env.GetOrDefaultSecond("VULTR_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("VULTR_TTL", dns01.DefaultTTL), + PropagationTimeout: env.GetOrDefaultSecond("VULTR_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("VULTR_POLLING_INTERVAL", dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond("VULTR_HTTP_TIMEOUT", 0), // from Vultr Client @@ -61,16 +60,6 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials -// to return a DNSProvider instance configured for Vultr. -// Deprecated -func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - config := NewDefaultConfig() - config.APIKey = apiKey - - return NewDNSProviderConfig(config) -} - // NewDNSProviderConfig return a DNSProvider instance configured for Vultr. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { @@ -83,7 +72,6 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { options := &vultr.Options{ HTTPClient: config.HTTPClient, - UserAgent: acme.UserAgent, } client := vultr.NewClient(config.APIKey, options) @@ -92,7 +80,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { // Present creates a TXT record to fulfill the DNS-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + fqdn, value := dns01.GetRecord(domain, keyAuth) zoneDomain, err := d.getHostedZone(domain) if err != nil { @@ -111,7 +99,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + fqdn, _ := dns01.GetRecord(domain, keyAuth) zoneDomain, records, err := d.findTxtRecords(domain, fqdn) if err != nil { @@ -183,7 +171,7 @@ func (d *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRe } func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) + name := dns01.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } diff --git a/vendor/github.com/xenolf/lego/registration/registar.go b/vendor/github.com/xenolf/lego/registration/registar.go new file mode 100644 index 000000000..a1b850ec3 --- /dev/null +++ b/vendor/github.com/xenolf/lego/registration/registar.go @@ -0,0 +1,146 @@ +package registration + +import ( + "errors" + "net/http" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/acme/api" + "github.com/xenolf/lego/log" +) + +// Resource represents all important information about a registration +// of which the client needs to keep track itself. +// Deprecated: will be remove in the future (acme.ExtendedAccount). +type Resource struct { + Body acme.Account `json:"body,omitempty"` + URI string `json:"uri,omitempty"` +} + +type RegisterOptions struct { + TermsOfServiceAgreed bool +} + +type RegisterEABOptions struct { + TermsOfServiceAgreed bool + Kid string + HmacEncoded string +} + +type Registrar struct { + core *api.Core + user User +} + +func NewRegistrar(core *api.Core, user User) *Registrar { + return &Registrar{ + core: core, + user: user, + } +} + +// Register the current account to the ACME server. +func (r *Registrar) Register(options RegisterOptions) (*Resource, error) { + if r == nil || r.user == nil { + return nil, errors.New("acme: cannot register a nil client or user") + } + + accMsg := acme.Account{ + TermsOfServiceAgreed: options.TermsOfServiceAgreed, + Contact: []string{}, + } + + if r.user.GetEmail() != "" { + log.Infof("acme: Registering account for %s", r.user.GetEmail()) + accMsg.Contact = []string{"mailto:" + r.user.GetEmail()} + } + + account, err := r.core.Accounts.New(accMsg) + if err != nil { + // FIXME seems impossible + errorDetails, ok := err.(acme.ProblemDetails) + if !ok || errorDetails.HTTPStatus != http.StatusConflict { + return nil, err + } + } + + return &Resource{URI: account.Location, Body: account.Account}, nil +} + +// RegisterWithExternalAccountBinding Register the current account to the ACME server. +func (r *Registrar) RegisterWithExternalAccountBinding(options RegisterEABOptions) (*Resource, error) { + accMsg := acme.Account{ + TermsOfServiceAgreed: options.TermsOfServiceAgreed, + Contact: []string{}, + } + + if r.user.GetEmail() != "" { + log.Infof("acme: Registering account for %s", r.user.GetEmail()) + accMsg.Contact = []string{"mailto:" + r.user.GetEmail()} + } + + account, err := r.core.Accounts.NewEAB(accMsg, options.Kid, options.HmacEncoded) + if err != nil { + errorDetails, ok := err.(acme.ProblemDetails) + // FIXME seems impossible + if !ok || errorDetails.HTTPStatus != http.StatusConflict { + return nil, err + } + } + + return &Resource{URI: account.Location, Body: account.Account}, 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 (r *Registrar) QueryRegistration() (*Resource, error) { + if r == nil || r.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 + log.Infof("acme: Querying account for %s", r.user.GetRegistration().URI) + + account, err := r.core.Accounts.Get(r.user.GetRegistration().URI) + if err != nil { + return nil, err + } + + return &Resource{ + Body: account, + // Location: header is not returned so this needs to be populated off of existing URI + URI: r.user.GetRegistration().URI, + }, nil +} + +// DeleteRegistration deletes the client's user registration from the ACME server. +func (r *Registrar) DeleteRegistration() error { + if r == nil || r.user == nil { + return errors.New("acme: cannot unregister a nil client or user") + } + + log.Infof("acme: Deleting account for %s", r.user.GetEmail()) + + return r.core.Accounts.Deactivate(r.user.GetRegistration().URI) +} + +// ResolveAccountByKey will attempt to look up an account using the given account key +// and return its registration resource. +func (r *Registrar) ResolveAccountByKey() (*Resource, error) { + log.Infof("acme: Trying to resolve account by key") + + accMsg := acme.Account{OnlyReturnExisting: true} + accountTransit, err := r.core.Accounts.New(accMsg) + if err != nil { + return nil, err + } + + account, err := r.core.Accounts.Get(accountTransit.Location) + if err != nil { + return nil, err + } + + return &Resource{URI: accountTransit.Location, Body: account}, nil +} diff --git a/vendor/github.com/xenolf/lego/registration/user.go b/vendor/github.com/xenolf/lego/registration/user.go new file mode 100644 index 000000000..1e29300e2 --- /dev/null +++ b/vendor/github.com/xenolf/lego/registration/user.go @@ -0,0 +1,13 @@ +package registration + +import ( + "crypto" +) + +// 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() *Resource + GetPrivateKey() crypto.PrivateKey +} diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519.go b/vendor/golang.org/x/crypto/ed25519/ed25519.go index 4f26b49b6..d6f683ba3 100644 --- a/vendor/golang.org/x/crypto/ed25519/ed25519.go +++ b/vendor/golang.org/x/crypto/ed25519/ed25519.go @@ -6,7 +6,10 @@ // https://ed25519.cr.yp.to/. // // These functions are also compatible with the “Ed25519” function defined in -// RFC 8032. +// RFC 8032. However, unlike RFC 8032's formulation, this package's private key +// representation includes a public key suffix to make multiple signing +// operations with the same key more efficient. This package refers to the RFC +// 8032 private key as the “seed”. package ed25519 // This code is a port of the public domain, “ref10” implementation of ed25519 @@ -31,6 +34,8 @@ const ( PrivateKeySize = 64 // SignatureSize is the size, in bytes, of signatures generated and verified by this package. SignatureSize = 64 + // SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. + SeedSize = 32 ) // PublicKey is the type of Ed25519 public keys. @@ -46,6 +51,15 @@ func (priv PrivateKey) Public() crypto.PublicKey { return PublicKey(publicKey) } +// Seed returns the private key seed corresponding to priv. It is provided for +// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds +// in this package. +func (priv PrivateKey) Seed() []byte { + seed := make([]byte, SeedSize) + copy(seed, priv[:32]) + return seed +} + // Sign signs the given message with priv. // Ed25519 performs two passes over messages to be signed and therefore cannot // handle pre-hashed messages. Thus opts.HashFunc() must return zero to @@ -61,19 +75,33 @@ func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOp // GenerateKey generates a public/private key pair using entropy from rand. // If rand is nil, crypto/rand.Reader will be used. -func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) { +func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { if rand == nil { rand = cryptorand.Reader } - privateKey = make([]byte, PrivateKeySize) - publicKey = make([]byte, PublicKeySize) - _, err = io.ReadFull(rand, privateKey[:32]) - if err != nil { + seed := make([]byte, SeedSize) + if _, err := io.ReadFull(rand, seed); err != nil { return nil, nil, err } - digest := sha512.Sum512(privateKey[:32]) + privateKey := NewKeyFromSeed(seed) + publicKey := make([]byte, PublicKeySize) + copy(publicKey, privateKey[32:]) + + return publicKey, privateKey, nil +} + +// NewKeyFromSeed calculates a private key from a seed. It will panic if +// len(seed) is not SeedSize. This function is provided for interoperability +// with RFC 8032. RFC 8032's private keys correspond to seeds in this +// package. +func NewKeyFromSeed(seed []byte) PrivateKey { + if l := len(seed); l != SeedSize { + panic("ed25519: bad seed length: " + strconv.Itoa(l)) + } + + digest := sha512.Sum512(seed) digest[0] &= 248 digest[31] &= 127 digest[31] |= 64 @@ -85,10 +113,11 @@ func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, er var publicKeyBytes [32]byte A.ToBytes(&publicKeyBytes) + privateKey := make([]byte, PrivateKeySize) + copy(privateKey, seed) copy(privateKey[32:], publicKeyBytes[:]) - copy(publicKey, publicKeyBytes[:]) - return publicKey, privateKey, nil + return privateKey } // Sign signs the message with privateKey and returns a signature. It will @@ -171,9 +200,16 @@ func Verify(publicKey PublicKey, message, sig []byte) bool { edwards25519.ScReduce(&hReduced, &digest) var R edwards25519.ProjectiveGroupElement - var b [32]byte - copy(b[:], sig[32:]) - edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b) + var s [32]byte + copy(s[:], sig[32:]) + + // https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in + // the range [0, order) in order to prevent signature malleability. + if !edwards25519.ScMinimal(&s) { + return false + } + + edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s) var checkR [32]byte R.ToBytes(&checkR) diff --git a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go index 5f8b99478..fd03c252a 100644 --- a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go +++ b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go @@ -4,6 +4,8 @@ package edwards25519 +import "encoding/binary" + // This code is a port of the public domain, “ref10” implementation of ed25519 // from SUPERCOP. @@ -1769,3 +1771,23 @@ func ScReduce(out *[32]byte, s *[64]byte) { out[30] = byte(s11 >> 9) out[31] = byte(s11 >> 17) } + +// order is the order of Curve25519 in little-endian form. +var order = [4]uint64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0, 0x1000000000000000} + +// ScMinimal returns true if the given scalar is less than the order of the +// curve. +func ScMinimal(scalar *[32]byte) bool { + for i := 3; ; i-- { + v := binary.LittleEndian.Uint64(scalar[i*8:]) + if v > order[i] { + return false + } else if v < order[i] { + break + } else if i == 0 { + return false + } + } + + return true +} diff --git a/vendor/golang.org/x/crypto/ocsp/ocsp.go b/vendor/golang.org/x/crypto/ocsp/ocsp.go index 589dfd35f..f079d9eab 100644 --- a/vendor/golang.org/x/crypto/ocsp/ocsp.go +++ b/vendor/golang.org/x/crypto/ocsp/ocsp.go @@ -63,7 +63,7 @@ func (r ResponseStatus) String() string { } // ResponseError is an error that may be returned by ParseResponse to indicate -// that the response itself is an error, not just that its indicating that a +// that the response itself is an error, not just that it's indicating that a // certificate is revoked, unknown, etc. type ResponseError struct { Status ResponseStatus @@ -488,10 +488,6 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon return nil, err } - if len(basicResp.Certificates) > 1 { - return nil, ParseError("OCSP response contains bad number of certificates") - } - if n := len(basicResp.TBSResponseData.Responses); n == 0 || cert == nil && n > 1 { return nil, ParseError("OCSP response contains bad number of responses") } @@ -544,6 +540,13 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon } if len(basicResp.Certificates) > 0 { + // Responders should only send a single certificate (if they + // send any) that connects the responder's certificate to the + // original issuer. We accept responses with multiple + // certificates due to a number responders sending them[1], but + // ignore all but the first. + // + // [1] https://github.com/golang/go/issues/21527 ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes) if err != nil { return nil, err diff --git a/vendor/golang.org/x/crypto/scrypt/scrypt.go b/vendor/golang.org/x/crypto/scrypt/scrypt.go index ff28aaef6..3362afd11 100644 --- a/vendor/golang.org/x/crypto/scrypt/scrypt.go +++ b/vendor/golang.org/x/crypto/scrypt/scrypt.go @@ -29,7 +29,7 @@ func blockXOR(dst, src []uint32, n int) { } // salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in, -// and puts the result into both both tmp and out. +// and puts the result into both tmp and out. func salsaXOR(tmp *[16]uint32, in, out []uint32) { w0 := tmp[0] ^ in[0] w1 := tmp[1] ^ in[1] @@ -218,7 +218,7 @@ func smix(b []byte, r, N int, v, xy []uint32) { // For example, you can get a derived key for e.g. AES-256 (which needs a // 32-byte key) by doing: // -// dk, err := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32) +// dk, err := scrypt.Key([]byte("some password"), salt, 32768, 8, 1, 32) // // The recommended parameters for interactive logins as of 2017 are N=32768, r=8 // and p=1. The parameters N, r, and p should be increased as memory latency and diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go index 02dad484e..391104084 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd +// +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd // Package terminal provides support functions for dealing with terminals, as // commonly found on UNIX systems. @@ -25,7 +25,7 @@ type State struct { termios unix.Termios } -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) return err == nil @@ -108,9 +108,7 @@ func ReadPassword(fd int) ([]byte, error) { return nil, err } - defer func() { - unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) - }() + defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) return readPasswordLine(passwordReader(fd)) } diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go b/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go new file mode 100644 index 000000000..dfcd62785 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go @@ -0,0 +1,12 @@ +// 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. + +// +build aix + +package terminal + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS +const ioctlWriteTermios = unix.TCSETS diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go index 799f049f0..9317ac7ed 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go @@ -21,7 +21,7 @@ import ( type State struct{} -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { return false } diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go index a2e1b57dc..3d5f06a9f 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go @@ -14,10 +14,10 @@ import ( // State contains the state of a terminal. type State struct { - state *unix.Termios + termios unix.Termios } -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { _, err := unix.IoctlGetTermio(fd, unix.TCGETA) return err == nil @@ -75,47 +75,43 @@ func ReadPassword(fd int) ([]byte, error) { // restored. // see http://cr.illumos.org/~webrev/andy_js/1060/ func MakeRaw(fd int) (*State, error) { - oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS) + termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) if err != nil { return nil, err } - oldTermios := *oldTermiosPtr - newTermios := oldTermios - newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON - newTermios.Oflag &^= syscall.OPOST - newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN - newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB - newTermios.Cflag |= syscall.CS8 - newTermios.Cc[unix.VMIN] = 1 - newTermios.Cc[unix.VTIME] = 0 + oldState := State{termios: *termios} - if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil { + termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON + termios.Oflag &^= unix.OPOST + termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN + termios.Cflag &^= unix.CSIZE | unix.PARENB + termios.Cflag |= unix.CS8 + termios.Cc[unix.VMIN] = 1 + termios.Cc[unix.VTIME] = 0 + + if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil { return nil, err } - return &State{ - state: oldTermiosPtr, - }, nil + return &oldState, nil } // Restore restores the terminal connected to the given file descriptor to a // previous state. func Restore(fd int, oldState *State) error { - return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state) + return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios) } // GetState returns the current state of a terminal which may be useful to // restore the terminal after a signal. func GetState(fd int) (*State, error) { - oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS) + termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) if err != nil { return nil, err } - return &State{ - state: oldTermiosPtr, - }, nil + return &State{termios: *termios}, nil } // GetSize returns the dimensions of the given terminal. diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go index 4933ac361..6cb8a9503 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go @@ -26,7 +26,7 @@ type State struct { mode uint32 } -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { var st uint32 err := windows.GetConsoleMode(windows.Handle(fd), &st) @@ -89,9 +89,7 @@ func ReadPassword(fd int) ([]byte, error) { return nil, err } - defer func() { - windows.SetConsoleMode(windows.Handle(fd), old) - }() + defer windows.SetConsoleMode(windows.Handle(fd), old) var h windows.Handle p, _ := windows.GetCurrentProcess()