diff --git a/go.mod b/go.mod index fb33c357..f5c2aaf7 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.14 require ( github.com/RoaringBitmap/roaring v0.4.23 // indirect github.com/blevesearch/bleve v1.0.7 + github.com/caddyserver/certmagic v0.10.12 + github.com/cenkalti/backoff/v4 v4.0.2 // indirect github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect @@ -12,38 +14,37 @@ require ( github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect - github.com/fsnotify/fsnotify v1.4.7 + github.com/fsnotify/fsnotify v1.4.9 github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect - github.com/go-acme/lego v2.6.0+incompatible // indirect + github.com/go-acme/lego/v3 v3.6.0 // indirect github.com/golang/protobuf v1.4.0 // indirect - github.com/gorilla/websocket v1.4.0 + github.com/gorilla/websocket v1.4.2 github.com/jmhodges/levigo v1.0.0 // indirect - github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 + github.com/jpillora/backoff v1.0.0 github.com/kjk/betterguid v0.0.0-20170621091430-c442874ba63a - github.com/klauspost/cpuid v1.2.1 - github.com/magiconair/properties v1.8.1 // indirect - github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 - github.com/mholt/certmagic v0.5.1 - github.com/miekg/dns v1.1.13 // indirect + github.com/klauspost/cpuid v1.2.3 + github.com/mailru/easyjson v0.7.1 + github.com/miekg/dns v1.1.29 // indirect github.com/mitchellh/go-homedir v1.1.0 + github.com/mitchellh/mapstructure v1.3.0 // indirect github.com/onsi/ginkgo v1.8.0 // indirect github.com/onsi/gomega v1.5.0 // indirect - github.com/pelletier/go-toml v1.4.0 // indirect + github.com/pelletier/go-toml v1.7.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20190512091148-babf20351dd7 // indirect github.com/spf13/afero v1.2.2 // indirect + github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v1.0.0 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.4.0 - github.com/stretchr/testify v1.4.0 - github.com/tdewolff/minify/v2 v2.5.0 + github.com/spf13/viper v1.6.3 + github.com/stretchr/testify v1.5.1 + github.com/tdewolff/minify/v2 v2.7.4 github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tinylib/msgp v1.1.2 // indirect go.etcd.io/bbolt v1.3.4 - golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 // indirect - golang.org/x/net v0.0.0-20190607181551-461777fb6f67 - golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect - golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect - golang.org/x/text v0.3.2 // indirect - gopkg.in/square/go-jose.v2 v2.3.1 // indirect + golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc // indirect + golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 + golang.org/x/sys v0.0.0-20200428200454-593003d681fa // indirect + gopkg.in/ini.v1 v1.55.0 // indirect + gopkg.in/square/go-jose.v2 v2.5.0 // indirect ) diff --git a/go.sum b/go.sum index 55373f7f..e7235af7 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,64 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= +github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw= +github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= +github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= +github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= +github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= +github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo= github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= +github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.8/go.mod h1:aVvklgKsPENRkl29bNwrHISa1F+YLGTHArMxZMBqWM8= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.112/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blevesearch/bleve v1.0.7 h1:4PspZE7XABMSKcVpzAKp0E05Yer1PIYmTWk+1ngNr/c= github.com/blevesearch/bleve v1.0.7/go.mod h1:3xvmBtaw12Y4C9iA1RTzwWCof5j5HjydjCTiDE2TeE0= github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040 h1:SjYVcfJVZoCfBlg+fkaq2eoZHTf5HaJfaTeTkOtyfHQ= @@ -26,13 +75,24 @@ github.com/blevesearch/zap/v11 v11.0.7 h1:nnmAOP6eXBkqEa1Srq1eqA5Wmn4w+BZjLdjynN github.com/blevesearch/zap/v11 v11.0.7/go.mod h1:bJoY56fdU2m/IP4LLz/1h4jY2thBoREvoqbuJ8zhm9k= github.com/blevesearch/zap/v12 v12.0.7 h1:y8FWSAYkdc4p1dn4YLxNNr1dxXlSUsakJh2Fc/r6cj4= github.com/blevesearch/zap/v12 v12.0.7/go.mod h1:70DNK4ZN4tb42LubeDbfpp6xnm8g3ROYVvvZ6pEoXD8= -github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/caddyserver/certmagic v0.10.12 h1:aZtgzcIssiMSlP0jDdpDBbBzQ5INf5eKL9T6Nf3YzKM= +github.com/caddyserver/certmagic v0.10.12/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ= +github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU= +github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= +github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4eamEDs= +github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= +github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -43,7 +103,10 @@ github.com/couchbase/moss v0.1.0 h1:HCL+xxHUwmOaL44kMM/gU08OW6QGCui1WVFO58bjhNI= github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs= github.com/couchbase/vellum v1.0.1 h1:qrj9ohvZedvc51S5KzPfJ6P6z0Vqzv7Lx7k3mVc2WOk= github.com/couchbase/vellum v1.0.1/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4= +github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= +github.com/cpu/goacmedns v0.0.2/go.mod h1:4MipLkI+qScwqtVxcNO6okBhbgRrr7/tKXUSgSL0teQ= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= @@ -56,18 +119,30 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -75,23 +150,42 @@ github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a h1:FQqo github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/go-acme/lego v2.6.0+incompatible h1:KxcEWOF5hKtgou4xIqPaXSRF9DoO4OJ90ndwdK6YH/k= -github.com/go-acme/lego v2.6.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= +github.com/go-acme/lego/v3 v3.4.0 h1:deB9NkelA+TfjGHVw8J7iKl/rMtffcGMWSMmptvMv0A= +github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M= +github.com/go-acme/lego/v3 v3.6.0 h1:Rv0MrX3DpVp9Xg77yR7x+PCksLLph3Ut/69/9Kim8ac= +github.com/go-acme/lego/v3 v3.6.0/go.mod h1:sB/T7hfyz0HYIBvPmz/C8jIaxF6scbbiGKTzbQ22V6A= +github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -102,30 +196,64 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pO github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -136,41 +264,65 @@ github.com/kjk/betterguid v0.0.0-20170621091430-c442874ba63a/go.mod h1:uxRAhHE1n github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= +github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/kljensen/snowball v0.6.0 h1:6DZLCcZeL0cLfodx+Md4/OLC6b/bfurWUOUGs1ydfOU= github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw= +github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= +github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= +github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= +github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 h1:wL11wNW7dhKIcRCHSm4sHKPWz0tt4mwBsVodG7+Xyqg= -github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mholt/certmagic v0.5.1 h1:8Pf6Hwwlh5sbT3nwn3ovXyXWxHCEM54wvfLzTrQ+UiM= -github.com/mholt/certmagic v0.5.1/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= -github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM= -github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.13 h1:x7DQtkU0cedzeS8TD36tT/w1Hm4rDtfCaYYAHE7TTBI= -github.com/miekg/dns v1.1.13/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI= +github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= +github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.0 h1:iDwIio/3gk2QtLLEsqU5lInaMzos0hDTz8a6lazSFVw= +github.com/mitchellh/mapstructure v1.3.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= +github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI= +github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= +github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= +github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= @@ -181,32 +333,60 @@ github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= -github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20190512091148-babf20351dd7 h1:FUL3b97ZY2EPqg2NbXKuMHs5pXJB9hjj1fDHnF2vl28= github.com/remyoudompheng/bigfft v0.0.0-20190512091148-babf20351dd7/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= @@ -215,6 +395,8 @@ github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= @@ -230,6 +412,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= +github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM= github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -240,93 +424,276 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tdewolff/minify/v2 v2.5.0 h1:OWPdsMnomzKoL5tzgW3HK3t1zVxsHF6SlGxJjUnoMdw= -github.com/tdewolff/minify/v2 v2.5.0/go.mod h1:ZLQDMgUYkaNxM5YLYnp151Lb6Ff+rAxNIzLxEkrKImQ= -github.com/tdewolff/parse/v2 v2.3.7 h1:DXoTUgrUE2Eap0m7zg1ljCO5C78vhEi7HTc4YnJWrRk= -github.com/tdewolff/parse/v2 v2.3.7/go.mod h1:HansaqmN4I/U7L6/tUp0NcwT2tFO0F4EAWYGSDzkYNk= -github.com/tdewolff/test v1.0.0 h1:jOwzqCXr5ePXEPGJaq2ivoR6HOCi+D5TPfpoyg8yvmU= -github.com/tdewolff/test v1.0.0/go.mod h1:DiQUlutnqlEvdvhSn2LPGy4TFwRauAaYDsL+683RNX4= +github.com/tdewolff/minify/v2 v2.7.4 h1:r0OZQ3QzWeDS5cXq53Bk4IFIBDZ7fiXIkw1a4bHONsw= +github.com/tdewolff/minify/v2 v2.7.4/go.mod h1:BkDSm8aMMT0ALGmpt7j3Ra7nLUgZL0qhyrAHXwxcy5w= +github.com/tdewolff/parse/v2 v2.4.2 h1:Bu2Qv6wepkc+Ou7iB/qHjAhEImlAP5vedzlQRUdj3BI= +github.com/tdewolff/parse/v2 v2.4.2/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= +github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= +github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= +github.com/transip/gotransip/v6 v6.0.2/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= +github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc h1:ZGI/fILM2+ueot/UixBSoj9188jCAxVHEZEGhqq67I4= +golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190607181551-461777fb6f67 h1:rJJxsykSlULwd2P2+pg/rtnwN2FrWp4IuCxOSyS0V00= -golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a h1:Yu34BogBivvmu7SAzHHaB9nZWH5D1C+z3F1jyIaYZSQ= +golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U= +golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181031143558-9b800f95dbbc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200428200454-593003d681fa h1:yMbJOvnfYkO1dSAviTu/ZguZWLBTXx4xE3LYrxUCCiA= +golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -338,13 +705,24 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ= +gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= +gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo= +gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -352,4 +730,16 @@ gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/https/https.go b/pkg/https/https.go index 9c2a9164..f4b2d246 100644 --- a/pkg/https/https.go +++ b/pkg/https/https.go @@ -2,14 +2,15 @@ package https import ( "crypto/tls" + "fmt" "net" "net/http" "net/url" "time" + "github.com/caddyserver/certmagic" "github.com/khlieng/dispatch/pkg/netutil" "github.com/klauspost/cpuid" - "github.com/mholt/certmagic" ) type Config struct { @@ -65,24 +66,29 @@ func Serve(handler http.Handler, cfg Config) error { } } - certmagic.Default.Agreed = true - certmagic.Default.Email = cfg.Email certmagic.Default.MustStaple = true magic := certmagic.NewDefault() + acme := certmagic.NewACMEManager(magic, certmagic.ACMEManager{ + Agreed: true, + Email: cfg.Email, + }) + + magic.Issuer = acme + domains := []string{cfg.Domain} if cfg.Domain == "" { domains = []string{} - magic.OnDemand = &certmagic.OnDemandConfig{MaxObtain: 3} + magic.OnDemand = maxObtain(3) } - err := magic.Manage(domains) + err := magic.ManageSync(domains) if err != nil { return err } - httpSrv.Handler = magic.HTTPChallengeHandler(redirect) + httpSrv.Handler = acme.HTTPChallengeHandler(redirect) httpsSrv.TLSConfig = TLSConfig(magic.TLSConfig()) go func() { @@ -167,3 +173,24 @@ func defaultCipherSuites() []uint16 { tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256} } + +func maxObtain(limit int) *certmagic.OnDemandConfig { + requested := []string{} + + return &certmagic.OnDemandConfig{ + DecisionFunc: func(name string) error { + for _, n := range requested { + if name == n { + return nil + } + } + + if len(requested) == limit { + return fmt.Errorf("OnDemand cert limit reached") + } + + requested = append(requested, name) + return nil + }, + } +} diff --git a/vendor/github.com/mholt/certmagic/.gitignore b/vendor/github.com/caddyserver/certmagic/.gitignore similarity index 100% rename from vendor/github.com/mholt/certmagic/.gitignore rename to vendor/github.com/caddyserver/certmagic/.gitignore diff --git a/vendor/github.com/mholt/certmagic/LICENSE.txt b/vendor/github.com/caddyserver/certmagic/LICENSE.txt similarity index 100% rename from vendor/github.com/mholt/certmagic/LICENSE.txt rename to vendor/github.com/caddyserver/certmagic/LICENSE.txt diff --git a/vendor/github.com/mholt/certmagic/README.md b/vendor/github.com/caddyserver/certmagic/README.md similarity index 72% rename from vendor/github.com/mholt/certmagic/README.md rename to vendor/github.com/caddyserver/certmagic/README.md index 4d3b5f42..f8f09bc7 100644 --- a/vendor/github.com/mholt/certmagic/README.md +++ b/vendor/github.com/caddyserver/certmagic/README.md @@ -1,19 +1,18 @@

- CertMagic + CertMagic

Easy and Powerful TLS Automation

The same library used by the Caddy Web Server

- - - - + + +

-Caddy's automagic TLS features, now for your own Go programs, in one powerful and easy-to-use library! +Caddy's automagic TLS features—now for your own Go programs—in one powerful and easy-to-use library! -CertMagic is the most mature, robust, and capable ACME client integration for Go. +CertMagic is the most mature, robust, and capable ACME client integration for Go... and perhaps ever. With CertMagic, you can add one line to your Go application to serve securely over TLS, without ever having to touch certificates. @@ -57,6 +56,7 @@ CertMagic - Automatic HTTPS using Let's Encrypt - [The `Config` type](#the-config-type) - [Defaults](#defaults) - [Providing an email address](#providing-an-email-address) + - [Rate limiting](#rate-limiting) - [Development and testing](#development-and-testing) - [Examples](#examples) - [Serving HTTP handlers with HTTPS](#serving-http-handlers-with-https) @@ -82,24 +82,34 @@ CertMagic - Automatic HTTPS using Let's Encrypt - Fully automated certificate management including issuance and renewal - One-liner, fully managed HTTPS servers - Full control over almost every aspect of the system -- HTTP->HTTPS redirects (for HTTP applications) +- HTTP->HTTPS redirects - Solves all 3 ACME challenges: HTTP, TLS-ALPN, and DNS +- Most robust error handling of _any_ ACME client + - Challenges are randomized to avoid accidental dependence + - Challenges are rotated to overcome certain network blockages + - Robust retries for up to 30 days + - Exponential backoff with carefully-tuned intervals + - Retries with optional test/staging CA endpoint instead of production, to avoid rate limits - Over 50 DNS providers work out-of-the-box (powered by [lego](https://github.com/go-acme/lego)!) +- Written in Go, a language with memory-safety guarantees - Pluggable storage implementations (default: file system) -- Wildcard certificates (requires DNS challenge) -- OCSP stapling for each qualifying certificate ([done right](https://gist.github.com/sleevi/5efe9ef98961ecfb4da8#gistcomment-2336055)) +- Wildcard certificates +- Automatic OCSP stapling ([done right](https://gist.github.com/sleevi/5efe9ef98961ecfb4da8#gistcomment-2336055)) [keeps your sites online!](https://twitter.com/caddyserver/status/1234874273724084226) + - Will [automatically attempt](https://twitter.com/mholt6/status/1235577699541762048) to replace [revoked certificates](https://community.letsencrypt.org/t/2020-02-29-caa-rechecking-bug/114591/3?u=mholt)! + - Staples stored to disk in case of responder outages - Distributed solving of all challenges (works behind load balancers) + - Highly efficient, coordinated management in a fleet + - Active locking + - Smart queueing - Supports "on-demand" issuance of certificates (during TLS handshakes!) - - Custom decision functions - - Hostname whitelist - - Ask an external URL - - Rate limiting -- Optional event hooks to observe internal behaviors + - Caddy / CertMagic pioneered this technology + - Custom decision functions to regulate and throttle on-demand behavior +- Optional event hooks for observation - Works with any certificate authority (CA) compliant with the ACME specification - Certificate revocation (please, only if private key is compromised) - Must-Staple (optional; not default) - Cross-platform support! Mac, Windows, Linux, BSD, Android... -- Scales well to thousands of names/certificates per instance +- Scales to hundreds of thousands of names/certificates per instance - Use in conjunction with your own certificates @@ -122,7 +132,7 @@ CertMagic - Automatic HTTPS using Let's Encrypt ## Installation ```bash -$ go get -u github.com/mholt/certmagic +$ go get github.com/caddyserver/certmagic ``` @@ -142,14 +152,25 @@ The `certmagic.Config` struct is how you can wield the power of this fully armed The default `Config` value is called `certmagic.Default`. Change its fields to suit your needs, then call `certmagic.NewDefault()` when you need a valid `Config` value. In other words, `certmagic.Default` is a template and is not valid for use directly. -You can set the default values easily, for example: `certmagic.Default.Email = ...`. +You can set the default values easily, for example: `certmagic.Default.Issuer = ...`. -The high-level functions in this package (`HTTPS()`, `Listen()`, and `Manage()`) use the default config exclusively. This is how most of you will interact with the package. This is suitable when all your certificates are managed the same way. However, if you need to manage certificates differently depending on their name, you will need to make your own cache and configs (keep reading). +Similarly, to configure ACME-specific defaults, use `certmagic.DefaultACME`. + +The high-level functions in this package (`HTTPS()`, `Listen()`, `ManageSync()`, and `ManageAsync()`) use the default config exclusively. This is how most of you will interact with the package. This is suitable when all your certificates are managed the same way. However, if you need to manage certificates differently depending on their name, you will need to make your own cache and configs (keep reading). #### Providing an email address -Although not strictly required, this is highly recommended best practice. It allows you to receive expiration emails if your certificates are expiring for some reason, and also allows the CA's engineers to potentially get in touch with you if something is wrong. I recommend setting `certmagic.Default.Email` or always setting the `Email` field of a new `Config` struct. +Although not strictly required, this is highly recommended best practice. It allows you to receive expiration emails if your certificates are expiring for some reason, and also allows the CA's engineers to potentially get in touch with you if something is wrong. I recommend setting `certmagic.DefaultACME.Email` or always setting the `Email` field of a new `Config` struct. + + +#### Rate limiting + +To avoid firehosing the CA's servers, CertMagic has built-in rate limiting. Currently, its default limit is up to 10 transactions (obtain or renew) every 1 minute (sliding window). This can be changed by setting the `RateLimitEvents` and `RateLimitEventsWindow` variables, if desired. + +The CA may still enforce their own rate limits, and there's nothing (well, nothing ethical) CertMagic can do to bypass them for you. + +Additionally, CertMagic will retry failed validations with exponential backoff for up to 30 days, with a reasonable maximum interval between attempts (an "attempt" means trying each enabled challenge type once). ### Development and Testing @@ -158,7 +179,7 @@ Note that Let's Encrypt imposes [strict rate limits](https://letsencrypt.org/doc While developing your application and testing it, use [their staging endpoint](https://letsencrypt.org/docs/staging-environment/) which has much higher rate limits. Even then, don't hammer it: but it's much safer for when you're testing. When deploying, though, use their production CA because their staging CA doesn't issue trusted certificates. -To use staging, set `certmagic.Default.CA = certmagic.LetsEncryptStagingCA` or set `CA` of every `Config` struct. +To use staging, set `certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA` or set `CA` of every `ACMEManager` struct. @@ -166,19 +187,19 @@ To use staging, set `certmagic.Default.CA = certmagic.LetsEncryptStagingCA` or s There are many ways to use this library. We'll start with the highest-level (simplest) and work down (more control). -All these high-level examples use `certmagic.Default` for the config and the default cache and storage for serving up certificates. +All these high-level examples use `certmagic.Default` and `certmagic.DefaultACME` for the config and the default cache and storage for serving up certificates. First, we'll follow best practices and do the following: ```go // read and agree to your CA's legal documents -certmagic.Default.Agreed = true +certmagic.DefaultACME.Agreed = true // provide an email address -certmagic.Default.Email = "you@yours.com" +certmagic.DefaultACME.Email = "you@yours.com" // use the staging endpoint while we're developing -certmagic.Default.CA = certmagic.LetsEncryptStagingCA +certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA ``` For fully-functional program examples, check out [this Twitter thread](https://twitter.com/mholt6/status/1073103805112147968) (or read it [unrolled into a single post](https://threadreaderapp.com/thread/1073103805112147968.html)). (Note that the package API has changed slightly since these posts.) @@ -235,14 +256,20 @@ cache := certmagic.NewCache(certmagic.CacheOptions{ }) magic := certmagic.New(cache, certmagic.Config{ + // any customizations you need go here +}) + +myACME := certmagic.NewACMEManager(magic, ACMEManager{ CA: certmagic.LetsEncryptStagingCA, Email: "you@yours.com", Agreed: true, - // plus any other customization you want + // plus any other customizations you need }) +magic.Issuer = myACME + // this obtains certificates or renews them if necessary -err := magic.Manage([]string{"example.com", "sub.example.com"}) +err := magic.ManageSync([]string{"example.com", "sub.example.com"}) if err != nil { return err } @@ -262,7 +289,7 @@ myTLSConfig.NextProtos = append(myTLSConfig.NextProtos, tlsalpn01.ACMETLS1Protoc // the HTTP challenge has to be handled by your HTTP server; // if you don't have one, you should have disabled it earlier // when you made the certmagic.Config -httpMux = magic.HTTPChallengeHandler(httpMux) +httpMux = myACME.HTTPChallengeHandler(httpMux) ``` Great! This example grants you much more flexibility for advanced programs. However, _the vast majority of you will only use the high-level functions described earlier_, especially since you can still customize them by setting the package-level `Default` config. @@ -281,7 +308,7 @@ To do so, simply ensure that each instance is using the same Storage. That is th The default Storage is implemented using the file system, so mounting the same shared folder is sufficient (see [Storage](#storage) for more on that)! If you need an alternate Storage implementation, feel free to use one, provided that all the instances use the _same_ one. :) -See [Storage](#storage) and the associated [godoc](https://godoc.org/github.com/mholt/certmagic#Storage) for more information! +See [Storage](#storage) and the associated [pkg.go.dev](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#Storage) for more information! ## The ACME Challenges @@ -292,7 +319,7 @@ If you're using the high-level convenience functions like `HTTPS()`, `Listen()`, The HTTP and TLS-ALPN challenges are the defaults because they don't require configuration from you, but they require that your server is accessible from external IPs on low ports. If that is not possible in your situation, you can enable the DNS challenge, which will disable the HTTP and TLS-ALPN challenges and use the DNS challenge exclusively. -Technically, only one challenge needs to be enabled for things to work, but using multiple is good for reliability in case a challenge is discontinued by the CA. This happened to the TLS-SNI challenge in early 2018—many popular ACME clients such as Traefik and Autocert broke, resulting in downtime for some sites, until new releases were made and patches deployed, because they used only one challenge; Caddy, however—this library's forerunner—was unaffected because it also used the HTTP challenge. If multiple challenges are enabled, they are chosen randomly to help prevent false reliance on a single challenge type. +Technically, only one challenge needs to be enabled for things to work, but using multiple is good for reliability in case a challenge is discontinued by the CA. This happened to the TLS-SNI challenge in early 2018—many popular ACME clients such as Traefik and Autocert broke, resulting in downtime for some sites, until new releases were made and patches deployed, because they used only one challenge; Caddy, however—this library's forerunner—was unaffected because it also used the HTTP challenge. If multiple challenges are enabled, they are chosen randomly to help prevent false reliance on a single challenge type. And if one fails, any remaining enabled challenges are tried before giving up. ### HTTP Challenge @@ -309,16 +336,17 @@ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Lookit my cool website over HTTPS!") }) -http.ListenAndServe(":80", magic.HTTPChallengeHandler(mux)) +http.ListenAndServe(":80", myACME.HTTPChallengeHandler(mux)) ``` If wrapping your handler is not a good solution, try this inside your `ServeHTTP()` instead: ```go magic := certmagic.NewDefault() +myACME := certmagic.NewACMEManager(magic, certmagic.DefaultACME) func ServeHTTP(w http.ResponseWriter, req *http.Request) { - if magic.HandleHTTPChallenge(w, r) { + if myACME.HandleHTTPChallenge(w, r) { return // challenge handled; nothing else to do } ... @@ -362,17 +390,17 @@ This challenge works by setting a special record in the domain's zone. To do thi To enable it, just set the `DNSProvider` field on a `certmagic.Config` struct, or set the default `certmagic.DNSProvider` variable. For example, if my domains' DNS was served by DNSimple and I set my DNSimple API credentials in environment variables: ```go -import "github.com/go-acme/lego/providers/dns/dnsimple" +import "github.com/go-acme/lego/v3/providers/dns/dnsimple" provider, err := dnsimple.NewDNSProvider() if err != nil { return err } -certmagic.Default.DNSProvider = provider +certmagic.DefaultACME.DNSProvider = provider ``` -Now the DNS challenge will be used by default, and I can obtain certificates for wildcard domains. See the [godoc documentation for the provider you're using](https://godoc.org/github.com/go-acme/lego/providers/dns#pkg-subdirectories) to learn how to configure it. Most can be configured by env variables or by passing in a config struct. If you pass a config struct instead of using env variables, you will probably need to set some other defaults (that's just how lego works, currently): +Now the DNS challenge will be used by default, and I can obtain certificates for wildcard domains. See the [pkg.go.dev documentation for the provider you're using](https://pkg.go.dev/github.com/go-acme/lego/providers/dns?tab=subdirectories) to learn how to configure it. Most can be configured by env variables or by passing in a config struct. If you pass a config struct instead of using env variables, you will probably need to set some other defaults (that's just how lego works, currently): ```go PropagationTimeout: dns01.DefaultPollingInterval, @@ -385,30 +413,36 @@ Enabling the DNS challenge disables the other challenges for that `certmagic.Con ## On-Demand TLS -Normally, certificates are obtained and renewed before a listener starts serving, and then those certificates are maintained throughout the lifetime of the program. In other words, the certificate names are static. But sometimes you don't know all the names ahead of time. This is where On-Demand TLS shines. +Normally, certificates are obtained and renewed before a listener starts serving, and then those certificates are maintained throughout the lifetime of the program. In other words, the certificate names are static. But sometimes you don't know all the names ahead of time, or you don't want to manage all the certificates up front. This is where On-Demand TLS shines. -Originally invented for use in Caddy (which was the first program to use such technology), On-Demand TLS makes it possible and easy to serve certificates for arbitrary names during the lifetime of the server. When a TLS handshake is received, CertMagic will read the Server Name Indication (SNI) value and either load and present that certificate in the ServerHello, or if one does not exist, it will obtain it from a CA right then-and-there. +Originally invented for use in Caddy (which was the first program to use such technology), On-Demand TLS makes it possible and easy to serve certificates for arbitrary or specific names during the lifetime of the server. When a TLS handshake is received, CertMagic will read the Server Name Indication (SNI) value and either load and present that certificate in the ServerHello, or if one does not exist, it will obtain it from a CA right then-and-there. -Of course, this has some obvious security implications. You don't want to DoS a CA or allow arbitrary clients to fill your storage with spammy TLS handshakes. That's why, in order to enable On-Demand issuance, you'll need to set some limits or some policy to allow getting a certificate. +Of course, this has some obvious security implications. You don't want to DoS a CA or allow arbitrary clients to fill your storage with spammy TLS handshakes. That's why, when you enable On-Demand issuance, you should set limits or policy to allow getting certificates. CertMagic has an implicit whitelist built-in which is sufficient for nearly everyone, but also has a more advanced way to control on-demand issuance. -CertMagic provides several ways to enforce decision policies for On-Demand TLS, in descending order of priority: - -- A generic function that you write which will decide whether to allow the certificate request -- A name whitelist -- The ability to make an HTTP request to a URL for permission -- Rate limiting - -The simplest way to enable On-Demand issuance is to set the OnDemand field of a Config (or the default package-level value): +The simplest way to enable on-demand issuance is to set the OnDemand field of a Config (or the default package-level value): ```go -certmagic.Default.OnDemand = &certmagic.OnDemandConfig{MaxObtain: 5} +certmagic.Default.OnDemand = new(certmagic.OnDemandConfig) ``` -This allows only 5 certificates to be requested and is the simplest way to enable On-Demand TLS, but is the least recommended. It prevents abuse, but only in the least helpful way. +By setting this to a non-nil value, on-demand TLS is enabled for that config. For convenient security, CertMagic's high-level abstraction functions such as `HTTPS()`, `TLS()`, `ManageSync()`, `ManageAsync()`, and `Listen()` (which all accept a list of domain names) will whitelist those names automatically so only certificates for those names can be obtained when using the Default config. Usually this is sufficient for most users. -The [godoc](https://godoc.org/github.com/mholt/certmagic#OnDemandConfig) describes how to use the other policies, all of which are much more recommended! :) +However, if you require advanced control over which domains can be issued certificates on-demand (for example, if you do not know which domain names you are managing, or just need to defer their operations until later), you should implement your own DecisionFunc: -If `OnDemand` is set and `Manage()` is called, then the names given to `Manage()` will be whitelisted rather than obtained right away. +```go +// if the decision function returns an error, a certificate +// may not be obtained for that name at that time +certmagic.Default.OnDemand = &certmagic.OnDemandConfig{ + DecisionFunc: func(name string) error { + if name != "example.com" { + return fmt.Errorf("not allowed") + } + return nil + }, +} +``` + +The [pkg.go.dev](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#OnDemandConfig) describes how to use this in full detail, so please check it out! ## Storage @@ -419,9 +453,9 @@ By default, CertMagic stores assets on the local file system in `$HOME/.local/sh The notion of a "cluster" or "fleet" of instances that may be serving the same site and sharing certificates, etc, is tied to storage. Simply, any instances that use the same storage facilities are considered part of the cluster. So if you deploy 100 instances of CertMagic behind a load balancer, they are all part of the same cluster if they share the same storage configuration. Sharing storage could be mounting a shared folder, or implementing some other distributed storage system such as a database server or KV store. -The easiest way to change the storage being used is to set `certmagic.DefaultStorage` to a value that satisfies the [Storage interface](https://godoc.org/github.com/mholt/certmagic#Storage). Keep in mind that a valid `Storage` must be able to implement some operations atomically in order to provide locking and synchronization. +The easiest way to change the storage being used is to set `certmagic.DefaultStorage` to a value that satisfies the [Storage interface](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#Storage). Keep in mind that a valid `Storage` must be able to implement some operations atomically in order to provide locking and synchronization. -If you write a Storage implementation, please add it to the [project wiki](https://github.com/mholt/certmagic/wiki/Storage-Implementations) so people can find it! +If you write a Storage implementation, please add it to the [project wiki](https://github.com/caddyserver/certmagic/wiki/Storage-Implementations) so people can find it! ## Cache @@ -439,9 +473,9 @@ Again, if you're needing to do this, you've probably over-complicated your appli Yes, just call the relevant method on the `Config` to add your own certificate to the cache: -- [`CacheUnmanagedCertificatePEMBytes()`](https://godoc.org/github.com/mholt/certmagic#Config.CacheUnmanagedCertificatePEMBytes) -- [`CacheUnmanagedCertificatePEMFile()`](https://godoc.org/github.com/mholt/certmagic#Config.CacheUnmanagedCertificatePEMFile) -- [`CacheUnmanagedTLSCertificate()`](https://godoc.org/github.com/mholt/certmagic#Config.CacheUnmanagedTLSCertificate) +- [`CacheUnmanagedCertificatePEMBytes()`](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#Config.CacheUnmanagedCertificatePEMBytes) +- [`CacheUnmanagedCertificatePEMFile()`](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#Config.CacheUnmanagedCertificatePEMFile) +- [`CacheUnmanagedTLSCertificate()`](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#Config.CacheUnmanagedTLSCertificate) Keep in mind that unmanaged certificates are (obviously) not renewed for you, so you'll have to replace them when you do. However, OCSP stapling is performed even for unmanaged certificates that qualify. @@ -464,7 +498,7 @@ and then you will not need to run with root privileges. ## Contributing -We welcome your contributions! Please see our **[contributing guidelines](https://github.com/mholt/certmagic/blob/master/.github/CONTRIBUTING.md)** for instructions. +We welcome your contributions! Please see our **[contributing guidelines](https://github.com/caddyserver/certmagic/blob/master/.github/CONTRIBUTING.md)** for instructions. ## Project History diff --git a/vendor/github.com/caddyserver/certmagic/acmeclient.go b/vendor/github.com/caddyserver/certmagic/acmeclient.go new file mode 100644 index 00000000..ea06e968 --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/acmeclient.go @@ -0,0 +1,402 @@ +// Copyright 2015 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package certmagic + +import ( + "context" + "crypto/tls" + "encoding/base64" + "fmt" + "log" + weakrand "math/rand" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "sync" + "time" + + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/certificate" + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/lego" + "github.com/go-acme/lego/v3/registration" +) + +func init() { + weakrand.Seed(time.Now().UnixNano()) +} + +// acmeClient is a wrapper over lego's acme.Client with +// some custom state attached. It is used to obtain, +// renew, and revoke certificates with ACME. Use +// ACMEManager.newACMEClient() or +// ACMEManager.newACMEClientWithRetry() to get a valid +// one for real use. +type acmeClient struct { + caURL string + mgr *ACMEManager + acmeClient *lego.Client + challenges []challenge.Type +} + +// newACMEClientWithRetry is the same as newACMEClient, but with +// automatic retry capabilities. Sometimes network connections or +// HTTP requests fail intermittently, even when requesting the +// directory endpoint for example, so we can avoid that by just +// retrying once. Failures here are rare and sporadic, usually, +// so a simple retry is an easy fix. +func (am *ACMEManager) newACMEClientWithRetry(useTestCA bool) (*acmeClient, error) { + var client *acmeClient + var err error + const maxTries = 2 + for i := 0; i < maxTries; i++ { + if i > 0 { + time.Sleep(2 * time.Second) + } + client, err = am.newACMEClient(useTestCA, false) // TODO: move logic that requires interactivity to way before this part of the process... + if err == nil { + break + } + if acmeErr, ok := err.(acme.ProblemDetails); ok { + if acmeErr.HTTPStatus == http.StatusTooManyRequests { + return nil, fmt.Errorf("too many requests making new ACME client: %+v - aborting", acmeErr) + } + } + log.Printf("[ERROR] Making new ACME client: %v (attempt %d/%d)", err, i+1, maxTries) + } + return client, err +} + +// newACMEClient creates the underlying ACME library client type. +// If useTestCA is true, am.TestCA will be used if it is set; +// otherwise, the primary CA will still be used. +func (am *ACMEManager) newACMEClient(useTestCA, interactive bool) (*acmeClient, error) { + acmeClientsMu.Lock() + defer acmeClientsMu.Unlock() + + // ensure defaults are filled in + certObtainTimeout := am.CertObtainTimeout + if certObtainTimeout == 0 { + certObtainTimeout = DefaultACME.CertObtainTimeout + } + var caURL string + if useTestCA { + caURL = am.TestCA + // only use the default test CA if the CA is also + // the default CA; no point in testing against + // Let's Encrypt's staging server if we are not + // using their production server too + if caURL == "" && am.CA == DefaultACME.CA { + caURL = DefaultACME.TestCA + } + } + if caURL == "" { + caURL = am.CA + } + if caURL == "" { + caURL = DefaultACME.CA + } + + // ensure endpoint is secure (assume HTTPS if scheme is missing) + if !strings.Contains(caURL, "://") { + caURL = "https://" + caURL + } + u, err := url.Parse(caURL) + if err != nil { + return nil, err + } + if u.Scheme != "https" && !isLoopback(u.Host) && !isInternal(u.Host) { + return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) + } + + // look up or create the user account + leUser, err := am.getUser(caURL, am.Email) + if err != nil { + return nil, err + } + + // if a lego client with this configuration already exists, reuse it + clientKey := caURL + leUser.Email + client, ok := acmeClients[clientKey] + if !ok { + // the client facilitates our communication with the CA server + legoCfg := lego.NewConfig(leUser) + legoCfg.CADirURL = caURL + legoCfg.UserAgent = buildUAString() + legoCfg.HTTPClient.Timeout = HTTPTimeout + legoCfg.Certificate = lego.CertificateConfig{ + Timeout: am.CertObtainTimeout, + } + if am.TrustedRoots != nil { + if ht, ok := legoCfg.HTTPClient.Transport.(*http.Transport); ok { + if ht.TLSClientConfig == nil { + ht.TLSClientConfig = new(tls.Config) + ht.ForceAttemptHTTP2 = true + } + ht.TLSClientConfig.RootCAs = am.TrustedRoots + } + } + client, err = lego.NewClient(legoCfg) + if err != nil { + return nil, err + } + acmeClients[clientKey] = client + } + + // if not registered, the user must register an account + // with the CA and agree to terms + if leUser.Registration == nil { + if interactive { // can't prompt a user who isn't there + termsURL := client.GetToSURL() + if !am.Agreed && termsURL != "" { + am.Agreed = am.askUserAgreement(client.GetToSURL()) + } + if !am.Agreed && termsURL != "" { + return nil, fmt.Errorf("user must agree to CA terms") + } + } + + var reg *registration.Resource + if am.ExternalAccount != nil { + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: am.Agreed, + Kid: am.ExternalAccount.KeyID, + HmacEncoded: base64.StdEncoding.EncodeToString(am.ExternalAccount.HMAC), + }) + } else { + reg, err = client.Registration.Register(registration.RegisterOptions{ + TermsOfServiceAgreed: am.Agreed, + }) + } + if err != nil { + return nil, err + } + leUser.Registration = reg + + // persist the user to storage + err = am.saveUser(caURL, leUser) + if err != nil { + return nil, fmt.Errorf("could not save user: %v", err) + } + } + + c := &acmeClient{ + caURL: caURL, + mgr: am, + acmeClient: client, + } + + return c, nil +} + +// initialChallenges returns the initial set of challenges +// to try using c.config as a basis. +func (c *acmeClient) initialChallenges() []challenge.Type { + // if configured, use DNS challenge exclusively + if c.mgr.DNSProvider != nil { + return []challenge.Type{challenge.DNS01} + } + + // otherwise, use HTTP and TLS-ALPN challenges if enabled + var chal []challenge.Type + if !c.mgr.DisableHTTPChallenge { + chal = append(chal, challenge.HTTP01) + } + if !c.mgr.DisableTLSALPNChallenge { + chal = append(chal, challenge.TLSALPN01) + } + return chal +} + +// nextChallenge chooses a challenge randomly from the given list of +// available challenges and configures c.acmeClient to use that challenge +// according to c.config. It pops the chosen challenge from the list and +// returns that challenge along with the new list without that challenge. +// If len(available) == 0, this is a no-op. +// +// Don't even get me started on how dumb it is we need to do this here +// instead of the upstream lego library doing it for us. Lego used to +// randomize the challenge order, thus allowing another one to be used +// if the first one failed. https://github.com/go-acme/lego/issues/842 +// (It also has an awkward API for adjusting the available challenges.) +// At time of writing, lego doesn't try anything other than the TLS-ALPN +// challenge, even if the HTTP challenge is also enabled. So we take +// matters into our own hands and enable only one challenge at a time +// in the underlying client, randomly selected by us. +func (c *acmeClient) nextChallenge(available []challenge.Type) (challenge.Type, []challenge.Type) { + if len(available) == 0 { + return "", available + } + + // make sure we choose a challenge randomly, which lego used to do but + // the critical feature was surreptitiously removed in ~2018 in a commit + // too large to review, oh well - choose one, then remove it from the + // list of available challenges so it doesn't get retried + randIdx := weakrand.Intn(len(available)) + randomChallenge := available[randIdx] + available = append(available[:randIdx], available[randIdx+1:]...) + + // clean the slate, since we reuse clients + c.acmeClient.Challenge.Remove(challenge.HTTP01) + c.acmeClient.Challenge.Remove(challenge.TLSALPN01) + c.acmeClient.Challenge.Remove(challenge.DNS01) + + switch randomChallenge { + case challenge.HTTP01: + useHTTPPort := HTTPChallengePort + if HTTPPort > 0 && HTTPPort != HTTPChallengePort { + useHTTPPort = HTTPPort + } + if c.mgr.AltHTTPPort > 0 { + useHTTPPort = c.mgr.AltHTTPPort + } + + c.acmeClient.Challenge.SetHTTP01Provider(distributedSolver{ + acmeManager: c.mgr, + providerServer: &httpSolver{ + acmeManager: c.mgr, + address: net.JoinHostPort(c.mgr.ListenHost, strconv.Itoa(useHTTPPort)), + }, + caURL: c.caURL, + }) + + case challenge.TLSALPN01: + useTLSALPNPort := TLSALPNChallengePort + if HTTPSPort > 0 && HTTPSPort != TLSALPNChallengePort { + useTLSALPNPort = HTTPSPort + } + if c.mgr.AltTLSALPNPort > 0 { + useTLSALPNPort = c.mgr.AltTLSALPNPort + } + + c.acmeClient.Challenge.SetTLSALPN01Provider(distributedSolver{ + acmeManager: c.mgr, + providerServer: &tlsALPNSolver{ + config: c.mgr.config, + address: net.JoinHostPort(c.mgr.ListenHost, strconv.Itoa(useTLSALPNPort)), + }, + caURL: c.caURL, + }) + + case challenge.DNS01: + if c.mgr.DNSChallengeOption != nil { + c.acmeClient.Challenge.SetDNS01Provider(c.mgr.DNSProvider, c.mgr.DNSChallengeOption) + } else { + c.acmeClient.Challenge.SetDNS01Provider(c.mgr.DNSProvider) + } + } + + return randomChallenge, available +} + +func (c *acmeClient) throttle(ctx context.Context, names []string) error { + // throttling is scoped to CA + account email + rateLimiterKey := c.caURL + "," + c.mgr.Email + rateLimitersMu.Lock() + rl, ok := rateLimiters[rateLimiterKey] + if !ok { + rl = NewRateLimiter(RateLimitEvents, RateLimitEventsWindow) + rateLimiters[rateLimiterKey] = rl + // TODO: stop rate limiter when it is garbage-collected... + } + rateLimitersMu.Unlock() + log.Printf("[INFO]%v Waiting on rate limiter...", names) + err := rl.Wait(ctx) + if err != nil { + return err + } + log.Printf("[INFO]%v Done waiting", names) + return nil +} + +func (c *acmeClient) usingTestCA() bool { + return c.mgr.TestCA != "" && c.caURL == c.mgr.TestCA +} + +func (c *acmeClient) revoke(_ context.Context, certRes certificate.Resource) error { + return c.acmeClient.Certificate.Revoke(certRes.Certificate) +} + +func buildUAString() string { + ua := "CertMagic" + if UserAgent != "" { + ua += " " + UserAgent + } + return ua +} + +// These internal rate limits are designed to prevent accidentally +// firehosing a CA's ACME endpoints. They are not intended to +// replace or replicate the CA's actual rate limits. +// +// Let's Encrypt's rate limits can be found here: +// https://letsencrypt.org/docs/rate-limits/ +// +// Currently (as of December 2019), Let's Encrypt's most relevant +// rate limit for large deployments is 300 new orders per account +// per 3 hours (on average, or best case, that's about 1 every 36 +// seconds, or 2 every 72 seconds, etc.); but it's not reasonable +// to try to assume that our internal state is the same as the CA's +// (due to process restarts, config changes, failed validations, +// etc.) and ultimately, only the CA's actual rate limiter is the +// authority. Thus, our own rate limiters do not attempt to enforce +// external rate limits. Doing so causes problems when the domains +// are not in our control (i.e. serving customer sites) and/or lots +// of domains fail validation: they clog our internal rate limiter +// and nearly starve out (or at least slow down) the other domains +// that need certificates. Failed transactions are already retried +// with exponential backoff, so adding in rate limiting can slow +// things down even more. +// +// Instead, the point of our internal rate limiter is to avoid +// hammering the CA's endpoint when there are thousands or even +// millions of certificates under management. Our goal is to +// allow small bursts in a relatively short timeframe so as to +// not block any one domain for too long, without unleashing +// thousands of requests to the CA at once. +var ( + rateLimiters = make(map[string]*RingBufferRateLimiter) + rateLimitersMu sync.RWMutex + + // RateLimitEvents is how many new events can be allowed + // in RateLimitEventsWindow. + RateLimitEvents = 10 + + // RateLimitEventsWindow is the size of the sliding + // window that throttles events. + RateLimitEventsWindow = 1 * time.Minute +) + +// Some default values passed down to the underlying lego client. +var ( + UserAgent string + HTTPTimeout = 30 * time.Second +) + +// We keep a global cache of ACME clients so that they +// can be reused. Since the number of CAs, accounts, +// and key types should be fairly limited under best +// practices, this map will hardly ever have more than +// a few entries at the most. The associated lock +// protects access to the map but also ensures that only +// one ACME client is created at a time. +// TODO: consider using storage for a distributed lock +// TODO: consider evicting clients after some time +var ( + acmeClients = make(map[string]*lego.Client) + acmeClientsMu sync.Mutex +) diff --git a/vendor/github.com/caddyserver/certmagic/acmemanager.go b/vendor/github.com/caddyserver/certmagic/acmemanager.go new file mode 100644 index 00000000..8e566fa2 --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/acmemanager.go @@ -0,0 +1,359 @@ +package certmagic + +import ( + "context" + "crypto/x509" + "errors" + "fmt" + "log" + "net/http" + "net/url" + "strings" + "time" + + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/certificate" + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/challenge/dns01" +) + +// ACMEManager gets certificates using ACME. It implements the PreChecker, +// Issuer, and Revoker interfaces. +// +// It is NOT VALID to use an ACMEManager without calling NewACMEManager(). +// It fills in default values from DefaultACME as well as setting up +// internal state that is necessary for valid use. Always call +// NewACMEManager() to get a valid ACMEManager value. +type ACMEManager struct { + // The endpoint of the directory for the ACME + // CA we are to use + CA string + + // TestCA is the endpoint of the directory for + // an ACME CA to use to test domain validation, + // but any certs obtained from this CA are + // discarded + TestCA string + + // The email address to use when creating or + // selecting an existing ACME server account + Email string + + // Set to true if agreed to the CA's + // subscriber agreement + Agreed bool + + // An optional external account to associate + // with this ACME account + ExternalAccount *ExternalAccountBinding + + // Disable all HTTP challenges + DisableHTTPChallenge bool + + // Disable all TLS-ALPN challenges + DisableTLSALPNChallenge bool + + // The host (ONLY the host, not port) to listen + // on if necessary to start a listener to solve + // an ACME challenge + ListenHost string + + // The alternate port to use for the ACME HTTP + // challenge; if non-empty, this port will be + // used instead of HTTPChallengePort to spin up + // a listener for the HTTP challenge + AltHTTPPort int + + // The alternate port to use for the ACME + // TLS-ALPN challenge; the system must forward + // TLSALPNChallengePort to this port for + // challenge to succeed + AltTLSALPNPort int + + // The DNS provider to use when solving the + // ACME DNS challenge + DNSProvider challenge.Provider + + // The ChallengeOption struct to provide + // custom precheck or name resolution options + // for DNS challenge validation and execution + DNSChallengeOption dns01.ChallengeOption + + // TrustedRoots specifies a pool of root CA + // certificates to trust when communicating + // over a network to a peer. + TrustedRoots *x509.CertPool + + // The maximum amount of time to allow for + // obtaining a certificate. If empty, the + // default from the underlying lego lib is + // used. If set, it must not be too low so + // as to cancel orders too early, running + // the risk of rate limiting. + CertObtainTimeout time.Duration + + config *Config +} + +// NewACMEManager constructs a valid ACMEManager based on a template +// configuration; any empty values will be filled in by defaults in +// DefaultACME. The associated config is also required. +// +// Typically, you'll create the Config first, then call NewACMEManager(), +// then assign the return value to the Issuer/Revoker fields of the Config. +func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager { + if cfg == nil { + panic("cannot make valid ACMEManager without an associated CertMagic config") + } + if template.CA == "" { + template.CA = DefaultACME.CA + } + if template.TestCA == "" { + template.TestCA = DefaultACME.TestCA + } + if template.Email == "" { + template.Email = DefaultACME.Email + } + if !template.Agreed { + template.Agreed = DefaultACME.Agreed + } + if !template.DisableHTTPChallenge { + template.DisableHTTPChallenge = DefaultACME.DisableHTTPChallenge + } + if !template.DisableTLSALPNChallenge { + template.DisableTLSALPNChallenge = DefaultACME.DisableTLSALPNChallenge + } + if template.ListenHost == "" { + template.ListenHost = DefaultACME.ListenHost + } + if template.AltHTTPPort == 0 { + template.AltHTTPPort = DefaultACME.AltHTTPPort + } + if template.AltTLSALPNPort == 0 { + template.AltTLSALPNPort = DefaultACME.AltTLSALPNPort + } + if template.DNSProvider == nil { + template.DNSProvider = DefaultACME.DNSProvider + } + if template.DNSChallengeOption == nil { + template.DNSChallengeOption = DefaultACME.DNSChallengeOption + } + if template.TrustedRoots == nil { + template.TrustedRoots = DefaultACME.TrustedRoots + } + if template.CertObtainTimeout == 0 { + template.CertObtainTimeout = DefaultACME.CertObtainTimeout + } + template.config = cfg + return &template +} + +// IssuerKey returns the unique issuer key for the +// confgured CA endpoint. +func (am *ACMEManager) IssuerKey() string { + return am.issuerKey(am.CA) +} + +func (am *ACMEManager) issuerKey(ca string) string { + key := ca + if caURL, err := url.Parse(key); err == nil { + key = caURL.Host + if caURL.Path != "" { + // keep the path, but make sure it's a single + // component (i.e. no forward slashes, and for + // good measure, no backward slashes either) + const hyphen = "-" + repl := strings.NewReplacer( + "/", hyphen, + "\\", hyphen, + ) + path := strings.Trim(repl.Replace(caURL.Path), hyphen) + if path != "" { + key += hyphen + path + } + } + } + return key +} + +// PreCheck performs a few simple checks before obtaining or +// renewing a certificate with ACME, and returns whether this +// batch is eligible for certificates if using Let's Encrypt. +// It also ensures that an email address is available. +func (am *ACMEManager) PreCheck(names []string, interactive bool) error { + letsEncrypt := strings.Contains(am.CA, "api.letsencrypt.org") + if letsEncrypt { + for _, name := range names { + if !SubjectQualifiesForPublicCert(name) { + return fmt.Errorf("subject does not qualify for a Let's Encrypt certificate: %s", name) + } + } + } + return am.getEmail(interactive) +} + +// Issue implements the Issuer interface. It obtains a certificate for the given csr using +// the ACME configuration am. +func (am *ACMEManager) Issue(ctx context.Context, csr *x509.CertificateRequest) (*IssuedCertificate, error) { + if am.config == nil { + panic("missing config pointer (must use NewACMEManager)") + } + + var isRetry bool + if attempts, ok := ctx.Value(AttemptsCtxKey).(*int); ok { + isRetry = *attempts > 0 + } + + cert, usedTestCA, err := am.doIssue(ctx, csr, isRetry) + if err != nil { + return nil, err + } + + // important to note that usedTestCA is not necessarily the same as isRetry + // (usedTestCA can be true if the main CA and the test CA happen to be the same) + if isRetry && usedTestCA && am.CA != am.TestCA { + // succeeded with testing endpoint, so try again with production endpoint + // (only if the production endpoint is different from the testing endpoint) + // TODO: This logic is imperfect and could benefit from some refinement. + // The two CA endpoints likely have different states, which could cause one + // to succeed and the other to fail, even if it's not a validation error. + // Two common cases would be: + // 1) Rate limiter state. This is more likely to cause prod to fail while + // staging succeeds, since prod usually has tighter rate limits. Thus, if + // initial attempt failed in prod due to rate limit, first retry (on staging) + // might succeed, and then trying prod again right way would probably still + // fail; normally this would terminate retries but the right thing to do in + // this case is to back off and retry again later. We could refine this logic + // to stick with the production endpoint on retries unless the error changes. + // 2) Cached authorizations state. If a domain validates successfully with + // one endpoint, but then the other endpoint is used, it might fail, e.g. if + // DNS was just changed or is still propagating. In this case, the second CA + // should continue to be retried with backoff, without switching back to the + // other endpoint. This is more likely to happen if a user is testing with + // the staging CA as the main CA, then changes their configuration once they + // think they are ready for the production endpoint. + cert, _, err = am.doIssue(ctx, csr, false) + if err != nil { + // succeeded with test CA but failed just now with the production CA; + // either we are observing differing internal states of each CA that will + // work out with time, or there is a bug/misconfiguration somewhere + // externally; it is hard to tell which! one easy cue is whether the + // error is specifically a 429 (Too Many Requests); if so, we should + // probably keep retrying + var acmeErr acme.ProblemDetails + if errors.As(err, &acmeErr) { + if acmeErr.HTTPStatus == http.StatusTooManyRequests { + // DON'T abort retries; the test CA succeeded (even + // if it's cached, it recently succeeded!) so we just + // need to keep trying (with backoff) until this CA's + // rate limits expire... + // TODO: as mentioned in comment above, we would benefit + // by pinning the main CA at this point instead of + // needlessly retrying with the test CA first each time + return nil, err + } + } + return nil, ErrNoRetry{err} + } + } + + return cert, err +} + +func (am *ACMEManager) doIssue(ctx context.Context, csr *x509.CertificateRequest, useTestCA bool) (*IssuedCertificate, bool, error) { + client, err := am.newACMEClientWithRetry(useTestCA) + if err != nil { + return nil, false, err + } + usingTestCA := client.usingTestCA() + + nameSet := namesFromCSR(csr) + + if !useTestCA { + if err := client.throttle(ctx, nameSet); err != nil { + return nil, usingTestCA, err + } + } + + certRes, err := client.tryAllEnabledChallenges(ctx, csr) + if err != nil { + return nil, usingTestCA, fmt.Errorf("%v %w", nameSet, err) + } + + ic := &IssuedCertificate{ + Certificate: certRes.Certificate, + Metadata: certRes, + } + + return ic, usingTestCA, nil +} + +func (c *acmeClient) tryAllEnabledChallenges(ctx context.Context, csr *x509.CertificateRequest) (*certificate.Resource, error) { + // start with all enabled challenges + challenges := c.initialChallenges() + if len(challenges) == 0 { + return nil, fmt.Errorf("no challenge types enabled") + } + + // try while a challenge type is still available + var cert *certificate.Resource + var err error + for len(challenges) > 0 { + var chosenChallenge challenge.Type + chosenChallenge, challenges = c.nextChallenge(challenges) + cert, err = c.acmeClient.Certificate.ObtainForCSR(*csr, true) + if err == nil { + return cert, nil + } + log.Printf("[ERROR] %s (challenge=%s remaining=%v)", err, chosenChallenge, challenges) + time.Sleep(2 * time.Second) + } + return cert, err +} + +// Revoke implements the Revoker interface. It revokes the given certificate. +func (am *ACMEManager) Revoke(ctx context.Context, cert CertificateResource) error { + client, err := am.newACMEClient(false, false) + if err != nil { + return err + } + + meta := cert.IssuerData.(map[string]interface{}) + cr := certificate.Resource{ + Domain: meta["domain"].(string), + CertURL: meta["certUrl"].(string), + CertStableURL: meta["certStableURL"].(string), + } + + return client.revoke(ctx, cr) +} + +// ExternalAccountBinding contains information for +// binding an external account to an ACME account. +type ExternalAccountBinding struct { + KeyID string + HMAC []byte +} + +// DefaultACME specifies the default settings +// to use for ACMEManagers. +var DefaultACME = ACMEManager{ + CA: LetsEncryptProductionCA, + TestCA: LetsEncryptStagingCA, +} + +// Some well-known CA endpoints available to use. +const ( + LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory" + LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory" +) + +// prefixACME is the storage key prefix used for ACME-specific assets. +const prefixACME = "acme" + +// Interface guards +var ( + _ PreChecker = (*ACMEManager)(nil) + _ Issuer = (*ACMEManager)(nil) + _ Revoker = (*ACMEManager)(nil) +) diff --git a/vendor/github.com/caddyserver/certmagic/async.go b/vendor/github.com/caddyserver/certmagic/async.go new file mode 100644 index 00000000..3f9a01b0 --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/async.go @@ -0,0 +1,162 @@ +package certmagic + +import ( + "context" + "errors" + "log" + "sync" + "time" +) + +var jm = &jobManager{maxConcurrentJobs: 1000} + +type jobManager struct { + mu sync.Mutex + maxConcurrentJobs int + activeWorkers int + queue []namedJob + names map[string]struct{} +} + +type namedJob struct { + name string + job func() error +} + +// Submit enqueues the given job with the given name. If name is non-empty +// and a job with the same name is already enqueued or running, this is a +// no-op. If name is empty, no duplicate prevention will occur. The job +// manager will then run this job as soon as it is able. +func (jm *jobManager) Submit(name string, job func() error) { + jm.mu.Lock() + defer jm.mu.Unlock() + if jm.names == nil { + jm.names = make(map[string]struct{}) + } + if name != "" { + // prevent duplicate jobs + if _, ok := jm.names[name]; ok { + return + } + jm.names[name] = struct{}{} + } + jm.queue = append(jm.queue, namedJob{name, job}) + if jm.activeWorkers < jm.maxConcurrentJobs { + jm.activeWorkers++ + go jm.worker() + } +} + +func (jm *jobManager) worker() { + for { + jm.mu.Lock() + if len(jm.queue) == 0 { + jm.activeWorkers-- + jm.mu.Unlock() + return + } + next := jm.queue[0] + jm.queue = jm.queue[1:] + jm.mu.Unlock() + if err := next.job(); err != nil { + log.Printf("[ERROR] %v", err) + } + if next.name != "" { + jm.mu.Lock() + delete(jm.names, next.name) + jm.mu.Unlock() + } + } +} + +func doWithRetry(ctx context.Context, f func(context.Context) error) error { + var attempts int + ctx = context.WithValue(ctx, AttemptsCtxKey, &attempts) + + // the initial intervalIndex is -1, signaling + // that we should not wait for the first attempt + start, intervalIndex := time.Now(), -1 + var err error + + for time.Since(start) < maxRetryDuration { + var wait time.Duration + if intervalIndex >= 0 { + wait = retryIntervals[intervalIndex] + } + timer := time.NewTimer(wait) + select { + case <-ctx.Done(): + timer.Stop() + return context.Canceled + case <-timer.C: + err = f(ctx) + attempts++ + if err == nil || errors.Is(err, context.Canceled) { + return err + } + var errNoRetry ErrNoRetry + if errors.As(err, &errNoRetry) { + return err + } + if intervalIndex < len(retryIntervals)-1 { + intervalIndex++ + } + if time.Since(start) < maxRetryDuration { + log.Printf("[ERROR] attempt %d: %v - retrying in %s (%s/%s elapsed)...", + attempts, err, retryIntervals[intervalIndex], time.Since(start), maxRetryDuration) + } else { + log.Printf("[ERROR] final attempt: %v - giving up (%s/%s elapsed)...", + err, time.Since(start), maxRetryDuration) + return nil + } + } + } + return err +} + +// ErrNoRetry is an error type which signals +// to stop retries early. +type ErrNoRetry struct{ Err error } + +// Unwrap makes it so that e wraps e.Err. +func (e ErrNoRetry) Unwrap() error { return e.Err } +func (e ErrNoRetry) Error() string { return e.Err.Error() } + +type retryStateCtxKey struct{} + +// AttemptsCtxKey is the context key for the value +// that holds the attempt counter. The value counts +// how many times the operation has been attempted. +// A value of 0 means first attempt. +var AttemptsCtxKey retryStateCtxKey + +// retryIntervals are based on the idea of exponential +// backoff, but weighed a little more heavily to the +// front. We figure that intermittent errors would be +// resolved after the first retry, but any errors after +// that would probably require at least a few minutes +// to clear up: either for DNS to propagate, for the +// administrator to fix their DNS or network properties, +// or some other external factor needs to change. We +// chose intervals that we think will be most useful +// without introducing unnecessary delay. The last +// interval in this list will be used until the time +// of maxRetryDuration has elapsed. +var retryIntervals = []time.Duration{ + 1 * time.Minute, + 2 * time.Minute, + 2 * time.Minute, + 5 * time.Minute, // elapsed: 10 min + 10 * time.Minute, + 20 * time.Minute, + 20 * time.Minute, // elapsed: 1 hr + 30 * time.Minute, + 30 * time.Minute, // elapsed: 2 hr + 1 * time.Hour, + 3 * time.Hour, // elapsed: 6 hr + 6 * time.Hour, // for up to maxRetryDuration +} + +// maxRetryDuration is the maximum duration to try +// doing retries using the above intervals. +const maxRetryDuration = 24 * time.Hour * 30 diff --git a/vendor/github.com/caddyserver/certmagic/azure-pipelines.yml b/vendor/github.com/caddyserver/certmagic/azure-pipelines.yml new file mode 100644 index 00000000..fbdaf52c --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/azure-pipelines.yml @@ -0,0 +1,85 @@ +trigger: +- master + +strategy: + matrix: + linux: + imageName: ubuntu-16.04 + gorootDir: /usr/local + mac: + imageName: macos-10.13 + gorootDir: /usr/local + windows: + imageName: windows-2019 + gorootDir: C:\ + +pool: + vmImage: $(imageName) + +variables: + GOROOT: $(gorootDir)/go + GOPATH: $(system.defaultWorkingDirectory)/gopath + GOBIN: $(GOPATH)/bin + modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' + # TODO: modules should be the default in Go 1.13, so this won't be needed + #GO111MODULE: on + +steps: +- bash: | + latestGo=$(curl "https://golang.org/VERSION?m=text") + echo "##vso[task.setvariable variable=LATEST_GO]$latestGo" + echo "Latest Go version: $latestGo" + displayName: "Get latest Go version" + +- bash: | + sudo rm -f $(which go) + echo '##vso[task.prependpath]$(GOBIN)' + echo '##vso[task.prependpath]$(GOROOT)/bin' + mkdir -p '$(modulePath)' + shopt -s extglob + shopt -s dotglob + mv !(gopath) '$(modulePath)' + displayName: Remove old Go, set GOBIN/GOROOT, and move project into GOPATH + +# Install Go (this varies by platform) + +- bash: | + wget "https://dl.google.com/go/$(LATEST_GO).linux-amd64.tar.gz" + sudo tar -C $(gorootDir) -xzf "$(LATEST_GO).linux-amd64.tar.gz" + condition: eq( variables['Agent.OS'], 'Linux' ) + displayName: Install Go on Linux + +- bash: | + wget "https://dl.google.com/go/$(LATEST_GO).darwin-amd64.tar.gz" + sudo tar -C $(gorootDir) -xzf "$(LATEST_GO).darwin-amd64.tar.gz" + condition: eq( variables['Agent.OS'], 'Darwin' ) + displayName: Install Go on macOS + +- powershell: | + Write-Host "Downloading Go... (please be patient, I am very slow)" + (New-Object System.Net.WebClient).DownloadFile("https://dl.google.com/go/$(LATEST_GO).windows-amd64.zip", "$(LATEST_GO).windows-amd64.zip") + Write-Host "Extracting Go... (I'm slow too)" + Expand-Archive "$(LATEST_GO).windows-amd64.zip" -DestinationPath "$(gorootDir)" + condition: eq( variables['Agent.OS'], 'Windows_NT' ) + displayName: Install Go on Windows + +# TODO: When this issue is fixed, replace with installer script: +# https://github.com/golangci/golangci-lint/issues/472 +- script: go get -v github.com/golangci/golangci-lint/cmd/golangci-lint + displayName: Install golangci-lint + +- bash: | + printf "Using go at: $(which go)\n" + printf "Go version: $(go version)\n" + printf "\n\nGo environment:\n\n" + go env + printf "\n\nSystem environment:\n\n" + env + displayName: Print Go version and environment + +- script: | + go get -v -t -d ./... + golangci-lint run -E gofmt -E goimports -E misspell + go test -race ./... + workingDirectory: '$(modulePath)' + displayName: Run tests diff --git a/vendor/github.com/mholt/certmagic/cache.go b/vendor/github.com/caddyserver/certmagic/cache.go similarity index 84% rename from vendor/github.com/mholt/certmagic/cache.go rename to vendor/github.com/caddyserver/certmagic/cache.go index 79641df6..bf7065a3 100644 --- a/vendor/github.com/mholt/certmagic/cache.go +++ b/vendor/github.com/caddyserver/certmagic/cache.go @@ -16,6 +16,8 @@ package certmagic import ( "fmt" + "log" + "strings" "sync" "time" ) @@ -153,10 +155,10 @@ type CacheOptions struct { RenewCheckInterval time.Duration } -// ConfigGetter is a function that returns a config that -// should be used when managing the given certificate -// or its assets. -type ConfigGetter func(Certificate) (Config, error) +// ConfigGetter is a function that returns a prepared, +// valid config that should be used when managing the +// given certificate or its assets. +type ConfigGetter func(Certificate) (*Config, error) // cacheCertificate calls unsyncedCacheCertificate with a write lock. // @@ -175,16 +177,16 @@ func (certCache *Cache) cacheCertificate(cert Certificate) { // a write lock on certCache.mu first. func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) { // no-op if this certificate already exists in the cache - if _, ok := certCache.cache[cert.Hash]; ok { + if _, ok := certCache.cache[cert.hash]; ok { return } // store the certificate - certCache.cache[cert.Hash] = cert + certCache.cache[cert.hash] = cert // update the index so we can access it by name for _, name := range cert.Names { - certCache.cacheIndex[name] = append(certCache.cacheIndex[name], cert.Hash) + certCache.cacheIndex[name] = append(certCache.cacheIndex[name], cert.hash) } } @@ -197,7 +199,7 @@ func (certCache *Cache) removeCertificate(cert Certificate) { for _, name := range cert.Names { keyList := certCache.cacheIndex[name] for i, cacheKey := range keyList { - if cacheKey == cert.Hash { + if cacheKey == cert.hash { keyList = append(keyList[:i], keyList[i+1:]...) } } @@ -209,7 +211,7 @@ func (certCache *Cache) removeCertificate(cert Certificate) { } // delete the actual cert from the cache - delete(certCache.cache, cert.Hash) + delete(certCache.cache, cert.hash) } // replaceCertificate atomically replaces oldCert with newCert in @@ -221,24 +223,18 @@ func (certCache *Cache) replaceCertificate(oldCert, newCert Certificate) { certCache.removeCertificate(oldCert) certCache.unsyncedCacheCertificate(newCert) certCache.mu.Unlock() + log.Printf("[INFO] Replaced certificate in cache for %v (new expiration date: %s)", + newCert.Names, newCert.Leaf.NotAfter.Format("2006-01-02 15:04:05")) } func (certCache *Cache) getFirstMatchingCert(name string) (Certificate, bool) { - certCache.mu.RLock() - defer certCache.mu.RUnlock() - - allCertKeys := certCache.cacheIndex[name] - if len(allCertKeys) == 0 { - return Certificate{}, false + all := certCache.getAllMatchingCerts(name) + if len(all) == 0 { + return all[0], true } - - cert, ok := certCache.cache[allCertKeys[0]] - return cert, ok + return Certificate{}, false } -// TODO: This seems unused (but could be useful if TLS -// handshakes serve up different certs for a single -// name depending on other properties such as key type) func (certCache *Cache) getAllMatchingCerts(name string) []Certificate { certCache.mu.RLock() defer certCache.mu.RUnlock() @@ -253,6 +249,16 @@ func (certCache *Cache) getAllMatchingCerts(name string) []Certificate { return certs } +func (certCache *Cache) getAllCerts() []Certificate { + certCache.mu.RLock() + defer certCache.mu.RUnlock() + certs := make([]Certificate, 0, len(certCache.cache)) + for _, cert := range certCache.cache { + certs = append(certs, cert) + } + return certs +} + func (certCache *Cache) getConfig(cert Certificate) (*Config, error) { cfg, err := certCache.options.GetConfigForCert(cert) if err != nil { @@ -262,7 +268,26 @@ func (certCache *Cache) getConfig(cert Certificate) (*Config, error) { return nil, fmt.Errorf("config returned for certificate %v is not nil and points to different cache; got %p, expected %p (this one)", cert.Names, cfg.certCache, certCache) } - return New(certCache, cfg), nil + return cfg, nil +} + +// AllMatchingCertificates returns a list of all certificates that could +// be used to serve the given SNI name, including exact SAN matches and +// wildcard matches. +func (certCache *Cache) AllMatchingCertificates(name string) []Certificate { + // get exact matches first + certs := certCache.getAllMatchingCerts(name) + + // then look for wildcard matches by replacing each + // label of the domain name with wildcards + labels := strings.Split(name, ".") + for i := range labels { + labels[i] = "*" + candidate := strings.Join(labels, ".") + certs = append(certs, certCache.getAllMatchingCerts(candidate)...) + } + + return certs } var ( diff --git a/vendor/github.com/mholt/certmagic/certificates.go b/vendor/github.com/caddyserver/certmagic/certificates.go similarity index 62% rename from vendor/github.com/mholt/certmagic/certificates.go rename to vendor/github.com/caddyserver/certmagic/certificates.go index 5675a7c8..608ca403 100644 --- a/vendor/github.com/mholt/certmagic/certificates.go +++ b/vendor/github.com/caddyserver/certmagic/certificates.go @@ -29,22 +29,23 @@ import ( // Certificate is a tls.Certificate with associated metadata tacked on. // Even if the metadata can be obtained by parsing the certificate, -// we are more efficient by extracting the metadata onto this struct. +// we are more efficient by extracting the metadata onto this struct, +// but at the cost of slightly higher memory use. type Certificate struct { tls.Certificate - // Names is the list of names this certificate is written for. - // The first is the CommonName (if any), the rest are SAN. + // Names is the list of subject names this + // certificate is signed for. Names []string - // NotAfter is when the certificate expires. - NotAfter time.Time + // Optional; user-provided, and arbitrary. + Tags []string // OCSP contains the certificate's parsed OCSP response. - OCSP *ocsp.Response + ocsp *ocsp.Response // The hex-encoded hash of this cert's chain's bytes. - Hash string + hash string // Whether this certificate is under our management managed bool @@ -53,14 +54,34 @@ type Certificate struct { // NeedsRenewal returns true if the certificate is // expiring soon (according to cfg) or has expired. func (cert Certificate) NeedsRenewal(cfg *Config) bool { - if cert.NotAfter.IsZero() { + return currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio) +} + +// currentlyInRenewalWindow returns true if the current time is +// within the renewal window, according to the given start/end +// dates and the ratio of the renewal window. If true is returned, +// the certificate being considered is due for renewal. +func currentlyInRenewalWindow(notBefore, notAfter time.Time, renewalWindowRatio float64) bool { + if notAfter.IsZero() { return false } - renewDurationBefore := DefaultRenewDurationBefore - if cfg.RenewDurationBefore > 0 { - renewDurationBefore = cfg.RenewDurationBefore + lifetime := notAfter.Sub(notBefore) + if renewalWindowRatio == 0 { + renewalWindowRatio = DefaultRenewalWindowRatio } - return time.Until(cert.NotAfter) < renewDurationBefore + renewalWindow := time.Duration(float64(lifetime) * renewalWindowRatio) + renewalWindowStart := notAfter.Add(-renewalWindow) + return time.Now().After(renewalWindowStart) +} + +// HasTag returns true if cert.Tags has tag. +func (cert Certificate) HasTag(tag string) bool { + for _, t := range cert.Tags { + if t == tag { + return true + } + } + return false } // CacheManagedCertificate loads the certificate for domain into the @@ -76,9 +97,7 @@ func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) { return cert, err } cfg.certCache.cacheCertificate(cert) - if cfg.OnEvent != nil { - cfg.OnEvent("cached_managed_cert", cert.Names) - } + cfg.emit("cached_managed_cert", cert.Names) return cert, nil } @@ -89,7 +108,7 @@ func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) { if err != nil { return Certificate{}, err } - cert, err := makeCertificateWithOCSP(cfg.Storage, certRes.Certificate, certRes.PrivateKey) + cert, err := makeCertificateWithOCSP(cfg.Storage, certRes.CertificatePEM, certRes.PrivateKeyPEM) if err != nil { return cert, err } @@ -102,15 +121,14 @@ func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) { // the in-memory cache. // // This method is safe for concurrent use. -func (cfg *Config) CacheUnmanagedCertificatePEMFile(certFile, keyFile string) error { +func (cfg *Config) CacheUnmanagedCertificatePEMFile(certFile, keyFile string, tags []string) error { cert, err := makeCertificateFromDiskWithOCSP(cfg.Storage, certFile, keyFile) if err != nil { return err } + cert.Tags = tags cfg.certCache.cacheCertificate(cert) - if cfg.OnEvent != nil { - cfg.OnEvent("cached_unmanaged_cert", cert.Names) - } + cfg.emit("cached_unmanaged_cert", cert.Names) return nil } @@ -118,19 +136,18 @@ func (cfg *Config) CacheUnmanagedCertificatePEMFile(certFile, keyFile string) er // It staples OCSP if possible. // // This method is safe for concurrent use. -func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate) error { +func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate, tags []string) error { var cert Certificate err := fillCertFromLeaf(&cert, tlsCert) if err != nil { return err } - err = stapleOCSP(cfg.Storage, &cert, nil) + _, err = stapleOCSP(cfg.Storage, &cert, nil) if err != nil { log.Printf("[WARNING] Stapling OCSP: %v", err) } - if cfg.OnEvent != nil { - cfg.OnEvent("cached_unmanaged_cert", cert.Names) - } + cfg.emit("cached_unmanaged_cert", cert.Names) + cert.Tags = tags cfg.certCache.cacheCertificate(cert) return nil } @@ -139,15 +156,14 @@ func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate) error { // of the certificate and key, then caches it in memory. // // This method is safe for concurrent use. -func (cfg *Config) CacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte) error { +func (cfg *Config) CacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte, tags []string) error { cert, err := makeCertificateWithOCSP(cfg.Storage, certBytes, keyBytes) if err != nil { return err } + cert.Tags = tags cfg.certCache.cacheCertificate(cert) - if cfg.OnEvent != nil { - cfg.OnEvent("cached_unmanaged_cert", cert.Names) - } + cfg.emit("cached_unmanaged_cert", cert.Names) return nil } @@ -174,7 +190,7 @@ func makeCertificateWithOCSP(storage Storage, certPEMBlock, keyPEMBlock []byte) if err != nil { return cert, err } - err = stapleOCSP(storage, &cert, certPEMBlock) + _, err = stapleOCSP(storage, &cert, certPEMBlock) if err != nil { log.Printf("[WARNING] Stapling OCSP: %v", err) } @@ -204,19 +220,25 @@ func makeCertificate(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { return cert, nil } -// fillCertFromLeaf populates metadata fields on cert from tlsCert. +// fillCertFromLeaf populates cert from tlsCert. If it succeeds, it +// guarantees that cert.Leaf is non-nil. func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error { if len(tlsCert.Certificate) == 0 { return fmt.Errorf("certificate is empty") } cert.Certificate = tlsCert - // the leaf cert should be the one for the site; it has what we need + // the leaf cert should be the one for the site; we must set + // the tls.Certificate.Leaf field so that TLS handshakes are + // more efficient leaf, err := x509.ParseCertificate(tlsCert.Certificate[0]) if err != nil { return err } + cert.Certificate.Leaf = leaf + // for convenience, we do want to assemble all the + // subjects on the certificate into one list if leaf.Subject.CommonName != "" { // TODO: CommonName is deprecated cert.Names = []string{strings.ToLower(leaf.Subject.CommonName)} } @@ -235,14 +257,18 @@ func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error { cert.Names = append(cert.Names, strings.ToLower(email)) } } + for _, u := range leaf.URIs { + if u.String() != leaf.Subject.CommonName { // TODO: CommonName is deprecated + cert.Names = append(cert.Names, u.String()) + } + } if len(cert.Names) == 0 { return fmt.Errorf("certificate has no names") } // save the hash of this certificate (chain) and // expiration date, for necessity and efficiency - cert.Hash = hashCertificateChain(cert.Certificate.Certificate) - cert.NotAfter = leaf.NotAfter + cert.hash = hashCertificateChain(cert.Certificate.Certificate) return nil } @@ -261,7 +287,7 @@ func (cfg *Config) managedCertInStorageExpiresSoon(cert Certificate) (bool, erro if err != nil { return false, err } - tlsCert, err := tls.X509KeyPair(certRes.Certificate, certRes.PrivateKey) + tlsCert, err := tls.X509KeyPair(certRes.CertificatePEM, certRes.PrivateKeyPEM) if err != nil { return false, err } @@ -269,15 +295,16 @@ func (cfg *Config) managedCertInStorageExpiresSoon(cert Certificate) (bool, erro if err != nil { return false, err } - timeLeft := leaf.NotAfter.Sub(time.Now().UTC()) - return timeLeft < cfg.RenewDurationBefore, nil + return currentlyInRenewalWindow(leaf.NotBefore, leaf.NotAfter, cfg.RenewalWindowRatio), nil } // reloadManagedCertificate reloads the certificate corresponding to the name(s) // on oldCert into the cache, from storage. This also replaces the old certificate // with the new one, so that all configurations that used the old cert now point -// to the new cert. +// to the new cert. It assumes that the new certificate for oldCert.Names[0] is +// already in storage. func (cfg *Config) reloadManagedCertificate(oldCert Certificate) error { + log.Printf("[INFO] Reloading managed certificate for %v", oldCert.Names) newCert, err := cfg.loadManagedCertificate(oldCert.Names[0]) if err != nil { return fmt.Errorf("loading managed certificate for %v from storage: %v", oldCert.Names, err) @@ -286,29 +313,78 @@ func (cfg *Config) reloadManagedCertificate(oldCert Certificate) error { return nil } -// HostQualifies returns true if the hostname alone -// appears eligible for automagic TLS. For example: -// localhost, empty hostname, and IP addresses are -// not eligible because we cannot obtain certificates -// for those names. Wildcard names are allowed, as long -// as they conform to CABF requirements (only one wildcard -// label, and it must be the left-most label). -func HostQualifies(hostname string) bool { - return hostname != "localhost" && // localhost is ineligible - - // hostname must not be empty - strings.TrimSpace(hostname) != "" && - - // only one wildcard label allowed, and it must be left-most - (!strings.Contains(hostname, "*") || - (strings.Count(hostname, "*") == 1 && - strings.HasPrefix(hostname, "*."))) && +// SubjectQualifiesForCert returns true if subj is a name which, +// as a quick sanity check, looks like it could be the subject +// of a certificate. Requirements are: +// - must not be empty +// - must not start or end with a dot (RFC 1034) +// - must not contain common accidental special characters +func SubjectQualifiesForCert(subj string) bool { + // must not be empty + return strings.TrimSpace(subj) != "" && // must not start or end with a dot - !strings.HasPrefix(hostname, ".") && - !strings.HasSuffix(hostname, ".") && + !strings.HasPrefix(subj, ".") && + !strings.HasSuffix(subj, ".") && - // cannot be an IP address, see - // https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt - net.ParseIP(hostname) == nil + // if it has a wildcard, must be a left-most label + (!strings.Contains(subj, "*") || strings.HasPrefix(subj, "*.")) && + + // must not contain other common special characters + !strings.ContainsAny(subj, "()[]{}<> \t\n\"\\!@#$%^&|;'+=") +} + +// SubjectQualifiesForPublicCert returns true if the subject +// name appears eligible for automagic TLS with a public +// CA such as Let's Encrypt. For example: localhost and IP +// addresses are not eligible because we cannot obtain certs +// for those names with a public CA. Wildcard names are +// allowed, as long as they conform to CABF requirements (only +// one wildcard label, and it must be the left-most label). +func SubjectQualifiesForPublicCert(subj string) bool { + // must at least qualify for certificate + return SubjectQualifiesForCert(subj) && + + // localhost is ineligible + subj != "localhost" && + + // .localhost TLD is ineligible + !strings.HasSuffix(subj, ".localhost") && + + // .local TLD is ineligible + !strings.HasSuffix(subj, ".local") && + + // only one wildcard label allowed, and it must be left-most + (!strings.Contains(subj, "*") || + (strings.Count(subj, "*") == 1 && + len(subj) > 2 && + strings.HasPrefix(subj, "*."))) && + + // cannot be an IP address (as of yet), see + // https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt + net.ParseIP(subj) == nil +} + +// MatchWildcard returns true if subject (a candidate DNS name) +// matches wildcard (a reference DNS name), mostly according to +// RFC-compliant wildcard rules. +func MatchWildcard(subject, wildcard string) bool { + if subject == wildcard { + return true + } + if !strings.Contains(wildcard, "*") { + return false + } + labels := strings.Split(subject, ".") + for i := range labels { + if labels[i] == "" { + continue // invalid label + } + labels[i] = "*" + candidate := strings.Join(labels, ".") + if candidate == wildcard { + return true + } + } + return false } diff --git a/vendor/github.com/mholt/certmagic/certmagic.go b/vendor/github.com/caddyserver/certmagic/certmagic.go similarity index 58% rename from vendor/github.com/mholt/certmagic/certmagic.go rename to vendor/github.com/caddyserver/certmagic/certmagic.go index 4739ad27..eca4537d 100644 --- a/vendor/github.com/mholt/certmagic/certmagic.go +++ b/vendor/github.com/caddyserver/certmagic/certmagic.go @@ -35,18 +35,18 @@ package certmagic import ( + "context" + "crypto" "crypto/tls" + "crypto/x509" "fmt" "log" "net" "net/http" - "net/url" + "sort" "strings" "sync" - "sync/atomic" "time" - - "github.com/go-acme/lego/certcrypto" ) // HTTPS serves mux for all domainNames using the HTTP @@ -70,10 +70,10 @@ func HTTPS(domainNames []string, mux http.Handler) error { mux = http.DefaultServeMux } - Default.Agreed = true + DefaultACME.Agreed = true cfg := NewDefault() - err := cfg.Manage(domainNames) + err := cfg.ManageSync(domainNames) if err != nil { return err } @@ -121,7 +121,9 @@ func HTTPS(domainNames []string, mux http.Handler) error { ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second, IdleTimeout: 5 * time.Second, - Handler: cfg.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler)), + } + if am, ok := cfg.Issuer.(*ACMEManager); ok { + httpServer.Handler = am.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler)) } httpsServer := &http.Server{ ReadHeaderTimeout: 10 * time.Second, @@ -143,10 +145,7 @@ func httpRedirectHandler(w http.ResponseWriter, r *http.Request) { // since we redirect to the standard HTTPS port, we // do not need to include it in the redirect URL - requestHost, _, err := net.SplitHostPort(r.Host) - if err != nil { - requestHost = r.Host // host probably did not contain a port - } + requestHost := hostOnly(r.Host) toURL += requestHost toURL += r.URL.RequestURI() @@ -171,10 +170,10 @@ func httpRedirectHandler(w http.ResponseWriter, r *http.Request) { // Calling this function signifies your acceptance to // the CA's Subscriber Agreement and/or Terms of Service. func TLS(domainNames []string) (*tls.Config, error) { - Default.Agreed = true - Default.DisableHTTPChallenge = true + DefaultACME.Agreed = true + DefaultACME.DisableHTTPChallenge = true cfg := NewDefault() - return cfg.TLSConfig(), cfg.Manage(domainNames) + return cfg.TLSConfig(), cfg.ManageSync(domainNames) } // Listen manages certificates for domainName and returns a @@ -188,17 +187,17 @@ func TLS(domainNames []string) (*tls.Config, error) { // Calling this function signifies your acceptance to // the CA's Subscriber Agreement and/or Terms of Service. func Listen(domainNames []string) (net.Listener, error) { - Default.Agreed = true - Default.DisableHTTPChallenge = true + DefaultACME.Agreed = true + DefaultACME.DisableHTTPChallenge = true cfg := NewDefault() - err := cfg.Manage(domainNames) + err := cfg.ManageSync(domainNames) if err != nil { return nil, err } return tls.Listen("tcp", fmt.Sprintf(":%d", HTTPSPort), cfg.TLSConfig()) } -// Manage obtains certificates for domainNames and keeps them +// ManageSync obtains certificates for domainNames and keeps them // renewed using the Default config. // // This is a slightly lower-level function; you will need to @@ -219,171 +218,78 @@ func Listen(domainNames []string) (net.Listener, error) { // // Calling this function signifies your acceptance to // the CA's Subscriber Agreement and/or Terms of Service. -func Manage(domainNames []string) error { - Default.Agreed = true - return NewDefault().Manage(domainNames) +func ManageSync(domainNames []string) error { + DefaultACME.Agreed = true + return NewDefault().ManageSync(domainNames) } -// OnDemandConfig contains some state relevant for providing -// on-demand TLS. Important note: If you are using the -// MaxObtain property to limit the maximum number of certs -// to be issued, the count of how many certs were issued -// will be reset if this struct gets garbage-collected. +// ManageAsync is the same as ManageSync, except that +// certificates are managed asynchronously. This means +// that the function will return before certificates +// are ready, and errors that occur during certificate +// obtain or renew operations are only logged. It is +// vital that you monitor the logs if using this method, +// which is only recommended for automated/non-interactive +// environments. +func ManageAsync(ctx context.Context, domainNames []string) error { + DefaultACME.Agreed = true + return NewDefault().ManageAsync(ctx, domainNames) +} + +// OnDemandConfig configures on-demand TLS (certificate +// operations as-needed, like during TLS handshakes, +// rather than immediately). +// +// When this package's high-level convenience functions +// are used (HTTPS, Manage, etc., where the Default +// config is used as a template), this struct regulates +// certificate operations using an implicit whitelist +// containing the names passed into those functions if +// no DecisionFunc is set. This ensures some degree of +// control by default to avoid certificate operations for +// aribtrary domain names. To override this whitelist, +// manually specify a DecisionFunc. To impose rate limits, +// specify your own DecisionFunc. type OnDemandConfig struct { - // If set, this function will be the absolute - // authority on whether the hostname (according - // to SNI) is allowed to try to get a cert. + // If set, this function will be called to determine + // whether a certificate can be obtained or renewed + // for the given name. If an error is returned, the + // request will be denied. DecisionFunc func(name string) error - // If no DecisionFunc is set, this whitelist - // is the absolute authority as to whether - // a certificate should be allowed to be tried. - // Names are compared against SNI value. - HostWhitelist []string - - // If no DecisionFunc or HostWhitelist are set, - // then an HTTP request will be made to AskURL - // to determine if a certificate should be - // obtained. If the request fails or the response - // is anything other than 2xx status code, the - // issuance will be denied. - AskURL *url.URL - - // If no DecisionFunc, HostWhitelist, or AskURL - // are set, then only this many certificates may - // be obtained on-demand; this field is required - // if all others are empty, otherwise, all cert - // issuances will fail. - MaxObtain int32 - - // The number of certificates that have been issued on-demand - // by this config. It is only safe to modify this count atomically. - // If it reaches MaxObtain, on-demand issuances must fail. - // Note that this will necessarily be reset to 0 if the - // struct leaves scope and/or gets garbage-collected. - obtainedCount int32 -} - -// Allowed returns whether the issuance for name is allowed according to o. -func (o *OnDemandConfig) Allowed(name string) error { - // The decision function has absolute authority, if set - if o.DecisionFunc != nil { - return o.DecisionFunc(name) - } - - // Otherwise, the host whitelist has decision authority - if len(o.HostWhitelist) > 0 { - return o.checkWhitelistForObtainingNewCerts(name) - } - - // Otherwise, a URL is checked for permission to issue this cert - if o.AskURL != nil { - return o.checkURLForObtainingNewCerts(name) - } - - // Otherwise use the limit defined by the "max_certs" setting - return o.checkLimitsForObtainingNewCerts(name) + // List of whitelisted hostnames (SNI values) for + // deferred (on-demand) obtaining of certificates. + // Used only by higher-level functions in this + // package to persist the list of hostnames that + // the config is supposed to manage. This is done + // because it seems reasonable that if you say + // "Manage [domain names...]", then only those + // domain names should be able to have certs; + // we don't NEED this feature, but it makes sense + // for higher-level convenience functions to be + // able to retain their convenience (alternative + // is: the user manually creates a DecisionFunc + // that whitelists the same names it already + // passed into Manage) and without letting clients + // have their run of any domain names they want. + // Only enforced if len > 0. + hostWhitelist []string } func (o *OnDemandConfig) whitelistContains(name string) bool { - for _, n := range o.HostWhitelist { - if strings.ToLower(n) == strings.ToLower(name) { + for _, n := range o.hostWhitelist { + if strings.EqualFold(n, name) { return true } } return false } -func (o *OnDemandConfig) checkWhitelistForObtainingNewCerts(name string) error { - if !o.whitelistContains(name) { - return fmt.Errorf("%s: name is not whitelisted", name) - } - return nil -} - -func (o *OnDemandConfig) checkURLForObtainingNewCerts(name string) error { - client := http.Client{ - Timeout: 10 * time.Second, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return fmt.Errorf("following http redirects is not allowed") - }, - } - - // Copy the URL from the config in order to modify it for this request - askURL := new(url.URL) - *askURL = *o.AskURL - - query := askURL.Query() - query.Set("domain", name) - askURL.RawQuery = query.Encode() - - resp, err := client.Get(askURL.String()) - if err != nil { - return fmt.Errorf("error checking %v to deterine if certificate for hostname '%s' should be allowed: %v", o.AskURL, name, err) - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode > 299 { - return fmt.Errorf("certificate for hostname '%s' not allowed, non-2xx status code %d returned from %v", name, resp.StatusCode, o.AskURL) - } - - return nil -} - -// checkLimitsForObtainingNewCerts checks to see if name can be issued right -// now according the maximum count defined in the configuration. If a non-nil -// error is returned, do not issue a new certificate for name. -func (o *OnDemandConfig) checkLimitsForObtainingNewCerts(name string) error { - if o.MaxObtain == 0 { - return fmt.Errorf("%s: no certificates allowed to be issued on-demand", name) - } - - // User can set hard limit for number of certs for the process to issue - if o.MaxObtain > 0 && - atomic.LoadInt32(&o.obtainedCount) >= o.MaxObtain { - return fmt.Errorf("%s: maximum certificates issued (%d)", name, o.MaxObtain) - } - - // Make sure name hasn't failed a challenge recently - failedIssuanceMu.RLock() - when, ok := failedIssuance[name] - failedIssuanceMu.RUnlock() - if ok { - return fmt.Errorf("%s: throttled; refusing to issue cert since last attempt on %s failed", name, when.String()) - } - - // Make sure, if we've issued a few certificates already, that we haven't - // issued any recently - lastIssueTimeMu.Lock() - since := time.Since(lastIssueTime) - lastIssueTimeMu.Unlock() - if atomic.LoadInt32(&o.obtainedCount) >= 10 && since < 10*time.Minute { - return fmt.Errorf("%s: throttled; last certificate was obtained %v ago", name, since) - } - - // Good to go 👍 - return nil -} - -// failedIssuance is a set of names that we recently failed to get a -// certificate for from the ACME CA. They are removed after some time. -// When a name is in this map, do not issue a certificate for it on-demand. -var failedIssuance = make(map[string]time.Time) -var failedIssuanceMu sync.RWMutex - -// lastIssueTime records when we last obtained a certificate successfully. -// If this value is recent, do not make any on-demand certificate requests. -var lastIssueTime time.Time -var lastIssueTimeMu sync.Mutex - // isLoopback returns true if the hostname of addr looks // explicitly like a common local hostname. addr must only // be a host or a host:port combination. func isLoopback(addr string) bool { - host, _, err := net.SplitHostPort(strings.ToLower(addr)) - if err != nil { - host = addr // happens if the addr is only a hostname - } + host := hostOnly(addr) return host == "localhost" || strings.Trim(host, "[]") == "::1" || strings.HasPrefix(host, "127.") @@ -400,13 +306,7 @@ func isInternal(addr string) bool { "192.168.0.0/16", "fc00::/7", } - host, _, err := net.SplitHostPort(addr) - if err != nil { - host = addr // happens if the addr is just a hostname, missing port - // if we encounter an error, the brackets need to be stripped - // because SplitHostPort didn't do it for us - host = strings.Trim(host, "[]") - } + host := hostOnly(addr) ip := net.ParseIP(host) if ip == nil { return false @@ -420,6 +320,104 @@ func isInternal(addr string) bool { return false } +// hostOnly returns only the host portion of hostport. +// If there is no port or if there is an error splitting +// the port off, the whole input string is returned. +func hostOnly(hostport string) string { + host, _, err := net.SplitHostPort(hostport) + if err != nil { + return hostport // OK; probably had no port to begin with + } + return host +} + +// PreChecker is an interface that can be optionally implemented by +// Issuers. Pre-checks are performed before each call (or batch of +// identical calls) to Issue(), giving the issuer the option to ensure +// it has all the necessary information/state. +type PreChecker interface { + PreCheck(names []string, interactive bool) error +} + +// Issuer is a type that can issue certificates. +type Issuer interface { + // Issue obtains a certificate for the given CSR. It + // must honor context cancellation if it is long-running. + // It can also use the context to find out if the current + // call is part of a retry, via AttemptsCtxKey. + Issue(ctx context.Context, request *x509.CertificateRequest) (*IssuedCertificate, error) + + // IssuerKey must return a string that uniquely identifies + // this particular configuration of the Issuer such that + // any certificates obtained by this Issuer will be treated + // as identical if they have the same SANs. + // + // Certificates obtained from Issuers with the same IssuerKey + // will overwrite others with the same SANs. For example, an + // Issuer might be able to obtain certificates from different + // CAs, say A and B. It is likely that the CAs have different + // use cases and purposes (e.g. testing and production), so + // their respective certificates should not overwrite eaach + // other. + IssuerKey() string +} + +// Revoker can revoke certificates. +type Revoker interface { + Revoke(ctx context.Context, cert CertificateResource) error +} + +// KeyGenerator can generate a private key. +type KeyGenerator interface { + // GenerateKey generates a private key. The returned + // PrivateKey must be able to expose its associated + // public key. + GenerateKey() (crypto.PrivateKey, error) +} + +// IssuedCertificate represents a certificate that was just issued. +type IssuedCertificate struct { + // The PEM-encoding of DER-encoded ASN.1 data. + Certificate []byte + + // Any extra information to serialize alongside the + // certificate in storage. + Metadata interface{} +} + +// CertificateResource associates a certificate with its private +// key and other useful information, for use in maintaining the +// certificate. +type CertificateResource struct { + // The list of names on the certificate; + // for convenience only. + SANs []string `json:"sans,omitempty"` + + // The PEM-encoding of DER-encoded ASN.1 data + // for the cert or chain. + CertificatePEM []byte `json:"-"` + + // The PEM-encoding of the certificate's private key. + PrivateKeyPEM []byte `json:"-"` + + // Any extra information associated with the certificate, + // usually provided by the issuer implementation. + IssuerData interface{} `json:"issuer_data,omitempty"` +} + +// NamesKey returns the list of SANs as a single string, +// truncated to some ridiculously long size limit. It +// can act as a key for the set of names on the resource. +func (cr *CertificateResource) NamesKey() string { + sort.Strings(cr.SANs) + result := strings.Join(cr.SANs, ",") + if len(result) > 1024 { + const trunc = "_trunc" + result = result[:1024-len(trunc)] + trunc + } + return result +} + // Default contains the package defaults for the // various Config fields. This is used as a template // when creating your own Configs with New(), and it @@ -436,11 +434,9 @@ func isInternal(addr string) bool { // cache). This is the only Config which can access // the default certificate cache. var Default = Config{ - CA: LetsEncryptProductionCA, - RenewDurationBefore: DefaultRenewDurationBefore, - RenewDurationBeforeAtStartup: DefaultRenewDurationBeforeAtStartup, - KeyType: certcrypto.EC256, - Storage: defaultFileStorage, + RenewalWindowRatio: DefaultRenewalWindowRatio, + Storage: defaultFileStorage, + KeySource: DefaultKeyGenerator, } const ( @@ -453,12 +449,6 @@ const ( TLSALPNChallengePort = 443 ) -// Some well-known CA endpoints available to use. -const ( - LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory" - LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory" -) - // Port variables must remain their defaults unless you // forward packets from the defaults to whatever these // are set to; otherwise ACME challenges will fail. diff --git a/vendor/github.com/caddyserver/certmagic/config.go b/vendor/github.com/caddyserver/certmagic/config.go new file mode 100644 index 00000000..06d91545 --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/config.go @@ -0,0 +1,758 @@ +// Copyright 2015 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package certmagic + +import ( + "bytes" + "context" + "crypto" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "fmt" + "log" + weakrand "math/rand" + "net" + "net/url" + "strings" + "time" + + "github.com/go-acme/lego/v3/challenge/tlsalpn01" +) + +// Config configures a certificate manager instance. +// An empty Config is not valid: use New() to obtain +// a valid Config. +type Config struct { + // How much of a certificate's lifetime becomes the + // renewal window, which is the span of time at the + // end of the certificate's validity period in which + // it should be renewed; for most certificates, the + // global default is good, but for exremely short- + // lived certs, you may want to raise this to ~0.5. + RenewalWindowRatio float64 + + // An optional event callback clients can set + // to subscribe to certain things happening + // internally by this config; invocations are + // synchronous, so make them return quickly! + OnEvent func(event string, data interface{}) + + // DefaultServerName specifies a server name + // to use when choosing a certificate if the + // ClientHello's ServerName field is empty + DefaultServerName string + + // The state needed to operate on-demand TLS; + // if non-nil, on-demand TLS is enabled and + // certificate operations are deferred to + // TLS handshakes (or as-needed) + // TODO: Can we call this feature "Reactive/Lazy/Passive TLS" instead? + OnDemand *OnDemandConfig + + // Add the must staple TLS extension to the + // CSR generated by lego/acme + MustStaple bool + + // The type that issues certificates; the + // default Issuer is ACMEManager + Issuer Issuer + + // The type that revokes certificates; must + // be configured in conjunction with the Issuer + // field such that both the Issuer and Revoker + // are related (because issuance information is + // required for revocation) + Revoker Revoker + + // The source of new private keys for certificates; + // the default KeySource is StandardKeyGenerator + KeySource KeyGenerator + + // CertSelection chooses one of the certificates + // with which the ClientHello will be completed; + // if not set, DefaultCertificateSelector will + // be used + CertSelection CertificateSelector + + // The storage to access when storing or + // loading TLS assets + Storage Storage + + // required pointer to the in-memory cert cache + certCache *Cache +} + +// NewDefault makes a valid config based on the package +// Default config. Most users will call this function +// instead of New() since most use cases require only a +// single config for any and all certificates. +// +// If your requirements are more advanced (for example, +// multiple configs depending on the certificate), then use +// New() instead. (You will need to make your own Cache +// first.) If you only need a single Config to manage your +// certs (even if that config changes, as long as it is the +// only one), customize the Default package variable before +// calling NewDefault(). +// +// All calls to NewDefault() will return configs that use the +// same, default certificate cache. All configs returned +// by NewDefault() are based on the values of the fields of +// Default at the time it is called. +func NewDefault() *Config { + defaultCacheMu.Lock() + if defaultCache == nil { + defaultCache = NewCache(CacheOptions{ + // the cache will likely need to renew certificates, + // so it will need to know how to do that, which + // depends on the certificate being managed and which + // can change during the lifetime of the cache; this + // callback makes it possible to get the latest and + // correct config with which to manage the cert, + // but if the user does not provide one, we can only + // assume that we are to use the default config + GetConfigForCert: func(Certificate) (*Config, error) { + return NewDefault(), nil + }, + }) + } + certCache := defaultCache + defaultCacheMu.Unlock() + + return newWithCache(certCache, Default) +} + +// New makes a new, valid config based on cfg and +// uses the provided certificate cache. certCache +// MUST NOT be nil or this function will panic. +// +// Use this method when you have an advanced use case +// that requires a custom certificate cache and config +// that may differ from the Default. For example, if +// not all certificates are managed/renewed the same +// way, you need to make your own Cache value with a +// GetConfigForCert callback that returns the correct +// configuration for each certificate. However, for +// the vast majority of cases, there will be only a +// single Config, thus the default cache (which always +// uses the default Config) and default config will +// suffice, and you should use New() instead. +func New(certCache *Cache, cfg Config) *Config { + if certCache == nil { + panic("a certificate cache is required") + } + if certCache.options.GetConfigForCert == nil { + panic("cache must have GetConfigForCert set in its options") + } + return newWithCache(certCache, cfg) +} + +// newWithCache ensures that cfg is a valid config by populating +// zero-value fields from the Default Config. If certCache is +// nil, this function panics. +func newWithCache(certCache *Cache, cfg Config) *Config { + if certCache == nil { + panic("cannot make a valid config without a pointer to a certificate cache") + } + + if cfg.OnDemand == nil { + cfg.OnDemand = Default.OnDemand + } + if cfg.RenewalWindowRatio == 0 { + cfg.RenewalWindowRatio = Default.RenewalWindowRatio + } + if cfg.OnEvent == nil { + cfg.OnEvent = Default.OnEvent + } + if cfg.KeySource == nil { + cfg.KeySource = Default.KeySource + } + if cfg.DefaultServerName == "" { + cfg.DefaultServerName = Default.DefaultServerName + } + if cfg.OnDemand == nil { + cfg.OnDemand = Default.OnDemand + } + if !cfg.MustStaple { + cfg.MustStaple = Default.MustStaple + } + if cfg.Storage == nil { + cfg.Storage = Default.Storage + } + if cfg.Issuer == nil { + cfg.Issuer = Default.Issuer + if cfg.Issuer == nil { + // okay really, we need an issuer, + // that's kind of the point; most + // people would probably want ACME + cfg.Issuer = NewACMEManager(&cfg, DefaultACME) + } + // issuer and revoker go together; if user + // specifies their own issuer, we don't want + // to override their revoker, hence we only + // do this if Issuer was also nil + if cfg.Revoker == nil { + cfg.Revoker = Default.Revoker + if cfg.Revoker == nil { + cfg.Revoker = NewACMEManager(&cfg, DefaultACME) + } + } + } + + // absolutely don't allow a nil storage, + // because that would make almost anything + // a config can do pointless + if cfg.Storage == nil { + cfg.Storage = defaultFileStorage + } + + // ensure the unexported fields are valid + cfg.certCache = certCache + + return &cfg +} + +// ManageSync causes the certificates for domainNames to be managed +// according to cfg. If cfg.OnDemand is not nil, then this simply +// whitelists the domain names and defers the certificate operations +// to when they are needed. Otherwise, the certificates for each +// name are loaded from storage or obtained from the CA. If loaded +// from storage, they are renewed if they are expiring or expired. +// It then caches the certificate in memory and is prepared to serve +// them up during TLS handshakes. +// +// Note that name whitelisting for on-demand management only takes +// effect if cfg.OnDemand.DecisionFunc is not set (is nil); it will +// not overwrite an existing DecisionFunc, nor will it overwrite +// its decision; i.e. the implicit whitelist is only used if no +// DecisionFunc is set. +// +// This method is synchronous, meaning that certificates for all +// domainNames must be successfully obtained (or renewed) before +// it returns. It returns immediately on the first error for any +// of the given domainNames. This behavior is recommended for +// interactive use (i.e. when an administrator is present) so +// that errors can be reported and fixed immediately. +func (cfg *Config) ManageSync(domainNames []string) error { + return cfg.manageAll(nil, domainNames, false) +} + +// ManageAsync is the same as ManageSync, except that ACME +// operations are performed asynchronously (in the background). +// This method returns before certificates are ready. It is +// crucial that the administrator monitors the logs and is +// notified of any errors so that corrective action can be +// taken as soon as possible. Any errors returned from this +// method occurred before ACME transactions started. +// +// As long as logs are monitored, this method is typically +// recommended for non-interactive environments. +// +// If there are failures loading, obtaining, or renewing a +// certificate, it will be retried with exponential backoff +// for up to about 30 days, with a maximum interval of about +// 24 hours. Cancelling ctx will cancel retries and shut down +// any goroutines spawned by ManageAsync. +func (cfg *Config) ManageAsync(ctx context.Context, domainNames []string) error { + return cfg.manageAll(ctx, domainNames, true) +} + +func (cfg *Config) manageAll(ctx context.Context, domainNames []string, async bool) error { + if ctx == nil { + ctx = context.Background() + } + + for _, domainName := range domainNames { + // if on-demand is configured, defer obtain and renew operations + if cfg.OnDemand != nil { + if !cfg.OnDemand.whitelistContains(domainName) { + cfg.OnDemand.hostWhitelist = append(cfg.OnDemand.hostWhitelist, domainName) + } + continue + } + + // otherwise, begin management immediately + err := cfg.manageOne(ctx, domainName, async) + if err != nil { + return err + } + } + + return nil +} + +func (cfg *Config) manageOne(ctx context.Context, domainName string, async bool) error { + // first try loading existing certificate from storage + cert, err := cfg.CacheManagedCertificate(domainName) + if err != nil { + if _, ok := err.(ErrNotExist); !ok { + return fmt.Errorf("%s: caching certificate: %v", domainName, err) + } + // if we don't have one in storage, obtain one + obtain := func() error { + err := cfg.ObtainCert(ctx, domainName, !async) + if err != nil { + return fmt.Errorf("%s: obtaining certificate: %w", domainName, err) + } + cert, err = cfg.CacheManagedCertificate(domainName) + if err != nil { + return fmt.Errorf("%s: caching certificate after obtaining it: %v", domainName, err) + } + return nil + } + if async { + // Leave the job name empty so as to allow duplicate 'obtain' + // jobs; this is because Caddy calls ManageAsync() before the + // previous config is stopped (and before its context is + // canceled), which means that if an obtain job is still + // running for the same domain, Submit() would not queue the + // new one because it is still running, even though it is + // (probably) about to be canceled (it might not if the new + // config fails to finish loading, however). In any case, we + // presume it is safe to enqueue a duplicate obtain job because + // either the old one (or sometimes the new one) is about to be + // canceled. This seems like reasonable logic for any consumer + // of this lib. See https://github.com/caddyserver/caddy/issues/3202 + jm.Submit("", obtain) + return nil + } + return obtain() + } + + // for an existing certificate, make sure it is renewed + renew := func() error { + err := cfg.RenewCert(ctx, domainName, !async) + if err != nil { + return fmt.Errorf("%s: renewing certificate: %w", domainName, err) + } + // successful renewal, so update in-memory cache + err = cfg.reloadManagedCertificate(cert) + if err != nil { + return fmt.Errorf("%s: reloading renewed certificate into memory: %v", domainName, err) + } + return nil + } + if cert.NeedsRenewal(cfg) { + if async { + jm.Submit("renew_"+domainName, renew) + return nil + } + return renew() + } + + return nil +} + +// ObtainCert obtains a certificate for name using cfg, as long +// as a certificate does not already exist in storage for that +// name. The name must qualify and cfg must be flagged as Managed. +// This function is a no-op if storage already has a certificate +// for name. +// +// It only obtains and stores certificates (and their keys), +// it does not load them into memory. If interactive is true, +// the user may be shown a prompt. +// TODO: consider moving interactive param into the Config struct, +// and maybe retry settings into the Config struct as well? (same for RenewCert) +func (cfg *Config) ObtainCert(ctx context.Context, name string, interactive bool) error { + if cfg.storageHasCertResources(name) { + return nil + } + issuer, err := cfg.getPrecheckedIssuer([]string{name}, interactive) + if err != nil { + return err + } + if issuer == nil { + return nil + } + return cfg.obtainWithIssuer(ctx, issuer, name, interactive) +} + +func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name string, interactive bool) error { + log.Printf("[INFO][%s] Obtain certificate; acquiring lock...", name) + + // ensure idempotency of the obtain operation for this name + lockKey := cfg.lockKey("cert_acme", name) + err := obtainLock(cfg.Storage, lockKey) + if err != nil { + return err + } + defer func() { + log.Printf("[INFO][%s] Obtain: Releasing lock", name) + if err := releaseLock(cfg.Storage, lockKey); err != nil { + log.Printf("[ERROR][%s] Obtain: Unable to unlock '%s': %v", name, lockKey, err) + } + }() + log.Printf("[INFO][%s] Obtain: Lock acquired; proceeding...", name) + + f := func(ctx context.Context) error { + // check if obtain is still needed -- might have been obtained during lock + if cfg.storageHasCertResources(name) { + log.Printf("[INFO][%s] Obtain: Certificate already exists in storage", name) + return nil + } + + privateKey, err := cfg.KeySource.GenerateKey() + if err != nil { + return err + } + privKeyPEM, err := encodePrivateKey(privateKey) + if err != nil { + return err + } + + csr, err := cfg.generateCSR(privateKey, []string{name}) + if err != nil { + return err + } + + issuedCert, err := issuer.Issue(ctx, csr) + if err != nil { + return fmt.Errorf("[%s] Obtain: %w", name, err) + } + + // success - immediately save the certificate resource + certRes := CertificateResource{ + SANs: namesFromCSR(csr), + CertificatePEM: issuedCert.Certificate, + PrivateKeyPEM: privKeyPEM, + IssuerData: issuedCert.Metadata, + } + err = cfg.saveCertResource(certRes) + if err != nil { + return fmt.Errorf("[%s] Obtain: saving assets: %v", name, err) + } + return nil + } + if interactive { + err = f(ctx) + } else { + err = doWithRetry(ctx, f) + } + if err != nil { + return err + } + + cfg.emit("cert_obtained", name) + + log.Printf("[INFO][%s] Certificate obtained successfully", name) + + return nil +} + +// RenewCert renews the certificate for name using cfg. It stows the +// renewed certificate and its assets in storage if successful. It +// DOES NOT update the in-memory cache with the new certificate. +func (cfg *Config) RenewCert(ctx context.Context, name string, interactive bool) error { + issuer, err := cfg.getPrecheckedIssuer([]string{name}, interactive) + if err != nil { + return err + } + if issuer == nil { + return nil + } + return cfg.renewWithIssuer(ctx, issuer, name, interactive) +} + +func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name string, interactive bool) error { + log.Printf("[INFO][%s] Renew certificate; acquiring lock...", name) + + // ensure idempotency of the renew operation for this name + lockKey := cfg.lockKey("cert_acme", name) + err := obtainLock(cfg.Storage, lockKey) + if err != nil { + return err + } + defer func() { + log.Printf("[INFO][%s] Renew: Releasing lock", name) + if err := releaseLock(cfg.Storage, lockKey); err != nil { + log.Printf("[ERROR][%s] Renew: Unable to unlock '%s': %v", name, lockKey, err) + } + }() + log.Printf("[INFO][%s] Renew: Lock acquired; proceeding...", name) + + f := func(ctx context.Context) error { + // prepare for renewal (load PEM cert, key, and meta) + certRes, err := cfg.loadCertResource(name) + if err != nil { + return err + } + + // check if renew is still needed - might have been renewed while waiting for lock + timeLeft, needsRenew := cfg.managedCertNeedsRenewal(certRes) + if !needsRenew { + log.Printf("[INFO][%s] Renew: Certificate appears to have been renewed already (expires in %s)", name, timeLeft) + return nil + } + log.Printf("[INFO][%s] Renew: %s remaining", name, timeLeft) + + privateKey, err := decodePrivateKey(certRes.PrivateKeyPEM) + if err != nil { + return err + } + csr, err := cfg.generateCSR(privateKey, []string{name}) + if err != nil { + return err + } + + issuedCert, err := issuer.Issue(ctx, csr) + if err != nil { + return fmt.Errorf("[%s] Renew: %w", name, err) + } + + // success - immediately save the renewed certificate resource + newCertRes := CertificateResource{ + SANs: namesFromCSR(csr), + CertificatePEM: issuedCert.Certificate, + PrivateKeyPEM: certRes.PrivateKeyPEM, + IssuerData: issuedCert.Metadata, + } + err = cfg.saveCertResource(newCertRes) + if err != nil { + return fmt.Errorf("[%s] Renew: saving assets: %v", name, err) + } + return nil + } + if interactive { + err = f(ctx) + } else { + err = doWithRetry(ctx, f) + } + if err != nil { + return err + } + + cfg.emit("cert_renewed", name) + + log.Printf("[INFO][%s] Certificate renewed successfully", name) + + return nil +} + +func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string) (*x509.CertificateRequest, error) { + csrTemplate := new(x509.CertificateRequest) + + for _, name := range sans { + if ip := net.ParseIP(name); ip != nil { + csrTemplate.IPAddresses = append(csrTemplate.IPAddresses, ip) + } else if strings.Contains(name, "@") { + csrTemplate.EmailAddresses = append(csrTemplate.EmailAddresses, name) + } else if u, err := url.Parse(name); err == nil && strings.Contains(name, "/") { + csrTemplate.URIs = append(csrTemplate.URIs, u) + } else { + csrTemplate.DNSNames = append(csrTemplate.DNSNames, name) + } + } + + if cfg.MustStaple { + csrTemplate.ExtraExtensions = append(csrTemplate.ExtraExtensions, mustStapleExtension) + } + + csrDER, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, privateKey) + if err != nil { + return nil, err + } + + return x509.ParseCertificateRequest(csrDER) +} + +// RevokeCert revokes the certificate for domain via ACME protocol. It requires +// that cfg.Issuer is properly configured with the same issuer that issued the +// certificate being revoked. +func (cfg *Config) RevokeCert(ctx context.Context, domain string, interactive bool) error { + rev := cfg.Revoker + if rev == nil { + rev = Default.Revoker + } + + certRes, err := cfg.loadCertResource(domain) + if err != nil { + return err + } + + issuerKey := cfg.Issuer.IssuerKey() + + if !cfg.Storage.Exists(StorageKeys.SitePrivateKey(issuerKey, domain)) { + return fmt.Errorf("private key not found for %s", certRes.SANs) + } + + err = rev.Revoke(ctx, certRes) + if err != nil { + return err + } + + cfg.emit("cert_revoked", domain) + + err = cfg.Storage.Delete(StorageKeys.SiteCert(issuerKey, domain)) + if err != nil { + return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err) + } + err = cfg.Storage.Delete(StorageKeys.SitePrivateKey(issuerKey, domain)) + if err != nil { + return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err) + } + err = cfg.Storage.Delete(StorageKeys.SiteMeta(issuerKey, domain)) + if err != nil { + return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err) + } + + return nil +} + +// TLSConfig is an opinionated method that returns a +// recommended, modern TLS configuration that can be +// used to configure TLS listeners, which also supports +// the TLS-ALPN challenge and serves up certificates +// managed by cfg. +// +// Unlike the package TLS() function, this method does +// not, by itself, enable certificate management for +// any domain names. +// +// Feel free to further customize the returned tls.Config, +// but do not mess with the GetCertificate or NextProtos +// fields unless you know what you're doing, as they're +// necessary to solve the TLS-ALPN challenge. +func (cfg *Config) TLSConfig() *tls.Config { + return &tls.Config{ + // these two fields necessary for TLS-ALPN challenge + GetCertificate: cfg.GetCertificate, + NextProtos: []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol}, + + // the rest recommended for modern TLS servers + MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{ + tls.X25519, + tls.CurveP256, + }, + CipherSuites: preferredDefaultCipherSuites(), + PreferServerCipherSuites: true, + } +} + +// getPrecheckedIssuer returns an Issuer with pre-checks +// completed, if it is also a PreChecker. It also checks +// that storage is functioning. If a nil Issuer is returned +// with a nil error, that means to skip this operation +// (not an error, just a no-op). +func (cfg *Config) getPrecheckedIssuer(names []string, interactive bool) (Issuer, error) { + // ensure storage is writeable and readable + // TODO: this is not necessary every time; should only + // perform check once every so often for each storage, + // which may require some global state... + err := cfg.checkStorage() + if err != nil { + return nil, fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err) + } + if prechecker, ok := cfg.Issuer.(PreChecker); ok { + err := prechecker.PreCheck(names, interactive) + if err != nil { + return nil, err + } + } + return cfg.Issuer, nil +} + +// checkStorage tests the storage by writing random bytes +// to a random key, and then loading those bytes and +// comparing the loaded value. If this fails, the provided +// cfg.Storage mechanism should not be used. +func (cfg *Config) checkStorage() error { + key := fmt.Sprintf("rw_test_%d", weakrand.Int()) + contents := make([]byte, 1024*10) // size sufficient for one or two ACME resources + _, err := weakrand.Read(contents) + if err != nil { + return err + } + err = cfg.Storage.Store(key, contents) + if err != nil { + return err + } + defer func() { + deleteErr := cfg.Storage.Delete(key) + if deleteErr != nil { + log.Printf("[ERROR] Deleting test key %s from storage: %v", key, err) + } + // if there was no other error, make sure + // to return any error returned from Delete + if err == nil { + err = deleteErr + } + }() + loaded, err := cfg.Storage.Load(key) + if err != nil { + return err + } + if !bytes.Equal(contents, loaded) { + return fmt.Errorf("load yielded different value than was stored; expected %d bytes, got %d bytes of differing elements", len(contents), len(loaded)) + } + return nil +} + +// storageHasCertResources returns true if the storage +// associated with cfg's certificate cache has all the +// resources related to the certificate for domain: the +// certificate, the private key, and the metadata. +func (cfg *Config) storageHasCertResources(domain string) bool { + issuerKey := cfg.Issuer.IssuerKey() + certKey := StorageKeys.SiteCert(issuerKey, domain) + keyKey := StorageKeys.SitePrivateKey(issuerKey, domain) + metaKey := StorageKeys.SiteMeta(issuerKey, domain) + return cfg.Storage.Exists(certKey) && + cfg.Storage.Exists(keyKey) && + cfg.Storage.Exists(metaKey) +} + +// lockKey returns a key for a lock that is specific to the operation +// named op being performed related to domainName and this config's CA. +func (cfg *Config) lockKey(op, domainName string) string { + return fmt.Sprintf("%s_%s_%s", op, domainName, cfg.Issuer.IssuerKey()) +} + +// managedCertNeedsRenewal returns true if certRes is +// expiring soon or already expired, or if the process +// of checking the expiration returned an error. +func (cfg *Config) managedCertNeedsRenewal(certRes CertificateResource) (time.Duration, bool) { + cert, err := makeCertificate(certRes.CertificatePEM, certRes.PrivateKeyPEM) + if err != nil { + return 0, true + } + return time.Until(cert.Leaf.NotAfter), cert.NeedsRenewal(cfg) +} + +func (cfg *Config) emit(eventName string, data interface{}) { + if cfg.OnEvent == nil { + return + } + cfg.OnEvent(eventName, data) +} + +// CertificateSelector is a type which can select a certificate to use given multiple choices. +type CertificateSelector interface { + SelectCertificate(*tls.ClientHelloInfo, []Certificate) (Certificate, error) +} + +// Constants for PKIX MustStaple extension. +var ( + tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24} + ocspMustStapleFeature = []byte{0x30, 0x03, 0x02, 0x01, 0x05} + mustStapleExtension = pkix.Extension{ + Id: tlsFeatureExtensionOID, + Value: ocspMustStapleFeature, + } +) diff --git a/vendor/github.com/mholt/certmagic/crypto.go b/vendor/github.com/caddyserver/certmagic/crypto.go similarity index 52% rename from vendor/github.com/mholt/certmagic/crypto.go rename to vendor/github.com/caddyserver/certmagic/crypto.go index c98878fd..1cc12e85 100644 --- a/vendor/github.com/mholt/certmagic/crypto.go +++ b/vendor/github.com/caddyserver/certmagic/crypto.go @@ -17,6 +17,9 @@ package certmagic import ( "crypto" "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/tls" @@ -25,8 +28,8 @@ import ( "encoding/pem" "fmt" "hash/fnv" + "strings" - "github.com/go-acme/lego/certificate" "github.com/klauspost/cpuid" ) @@ -45,20 +48,48 @@ func encodePrivateKey(key crypto.PrivateKey) ([]byte, error) { case *rsa.PrivateKey: pemType = "RSA" keyBytes = x509.MarshalPKCS1PrivateKey(key) + case *ed25519.PrivateKey: + var err error + pemType = "ED25519" + keyBytes, err = x509.MarshalPKCS8PrivateKey(key) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("unsupported key type: %T", key) } pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes} return pem.EncodeToMemory(&pemKey), nil } // decodePrivateKey loads a PEM-encoded ECC/RSA private key from an array of bytes. +// Borrowed from Go standard library, to handle various private key and PEM block types. +// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308 +// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238) func decodePrivateKey(keyPEMBytes []byte) (crypto.PrivateKey, error) { - keyBlock, _ := pem.Decode(keyPEMBytes) - switch keyBlock.Type { - case "RSA PRIVATE KEY": - return x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - case "EC PRIVATE KEY": - return x509.ParseECPrivateKey(keyBlock.Bytes) + keyBlockDER, _ := pem.Decode(keyPEMBytes) + + if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") { + return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type) } + + if key, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil { + return key, nil + } + + if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: + return key, nil + default: + return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key) + } + } + + if key, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil { + return key, nil + } + return nil, fmt.Errorf("unknown private key type") } @@ -98,23 +129,26 @@ func fastHash(input []byte) string { // saveCertResource saves the certificate resource to disk. This // includes the certificate file itself, the private key, and the // metadata file. -func (cfg *Config) saveCertResource(cert *certificate.Resource) error { - metaBytes, err := json.MarshalIndent(&cert, "", "\t") +func (cfg *Config) saveCertResource(cert CertificateResource) error { + metaBytes, err := json.MarshalIndent(cert, "", "\t") if err != nil { return fmt.Errorf("encoding certificate metadata: %v", err) } + issuerKey := cfg.Issuer.IssuerKey() + certKey := cert.NamesKey() + all := []keyValue{ { - key: StorageKeys.SiteCert(cfg.CA, cert.Domain), - value: cert.Certificate, + key: StorageKeys.SiteCert(issuerKey, certKey), + value: cert.CertificatePEM, }, { - key: StorageKeys.SitePrivateKey(cfg.CA, cert.Domain), - value: cert.PrivateKey, + key: StorageKeys.SitePrivateKey(issuerKey, certKey), + value: cert.PrivateKeyPEM, }, { - key: StorageKeys.SiteMeta(cfg.CA, cert.Domain), + key: StorageKeys.SiteMeta(issuerKey, certKey), value: metaBytes, }, } @@ -122,26 +156,27 @@ func (cfg *Config) saveCertResource(cert *certificate.Resource) error { return storeTx(cfg.Storage, all) } -func (cfg *Config) loadCertResource(domain string) (certificate.Resource, error) { - var certRes certificate.Resource - certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(cfg.CA, domain)) +func (cfg *Config) loadCertResource(certNamesKey string) (CertificateResource, error) { + var certRes CertificateResource + issuerKey := cfg.Issuer.IssuerKey() + certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(issuerKey, certNamesKey)) if err != nil { - return certRes, err + return CertificateResource{}, err } - keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(cfg.CA, domain)) + certRes.CertificatePEM = certBytes + keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(issuerKey, certNamesKey)) if err != nil { - return certRes, err + return CertificateResource{}, err } - metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(cfg.CA, domain)) + certRes.PrivateKeyPEM = keyBytes + metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(issuerKey, certNamesKey)) if err != nil { - return certRes, err + return CertificateResource{}, err } err = json.Unmarshal(metaBytes, &certRes) if err != nil { - return certRes, fmt.Errorf("decoding certificate metadata: %v", err) + return CertificateResource{}, fmt.Errorf("decoding certificate metadata: %v", err) } - certRes.Certificate = certBytes - certRes.PrivateKey = keyBytes return certRes, nil } @@ -156,6 +191,19 @@ func hashCertificateChain(certChain [][]byte) string { return fmt.Sprintf("%x", h.Sum(nil)) } +func namesFromCSR(csr *x509.CertificateRequest) []string { + var nameSet []string + nameSet = append(nameSet, csr.DNSNames...) + nameSet = append(nameSet, csr.EmailAddresses...) + for _, v := range csr.IPAddresses { + nameSet = append(nameSet, v.String()) + } + for _, v := range csr.URIs { + nameSet = append(nameSet, v.String()) + } + return nameSet +} + // preferredDefaultCipherSuites returns an appropriate // cipher suite to use depending on hardware support // for AES-NI. @@ -186,3 +234,46 @@ var ( tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, } ) + +// StandardKeyGenerator is the standard, in-memory key source +// that uses crypto/rand. +type StandardKeyGenerator struct { + // The type of keys to generate. + KeyType KeyType +} + +// GenerateKey generates a new private key according to kg.KeyType. +func (kg StandardKeyGenerator) GenerateKey() (crypto.PrivateKey, error) { + switch kg.KeyType { + case ED25519: + _, priv, err := ed25519.GenerateKey(rand.Reader) + return priv, err + case "", P256: + return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case P384: + 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("unrecognized or unsupported key type: %s", kg.KeyType) +} + +// DefaultKeyGenerator is the default key source. +var DefaultKeyGenerator = StandardKeyGenerator{KeyType: P256} + +// KeyType enumerates the known/supported key types. +type KeyType string + +// Constants for all key types we support. +const ( + ED25519 = KeyType("ed25519") + P256 = KeyType("p256") + P384 = KeyType("p384") + RSA2048 = KeyType("rsa2048") + RSA4096 = KeyType("rsa4096") + RSA8192 = KeyType("rsa8192") +) diff --git a/vendor/github.com/mholt/certmagic/filestorage.go b/vendor/github.com/caddyserver/certmagic/filestorage.go similarity index 61% rename from vendor/github.com/mholt/certmagic/filestorage.go rename to vendor/github.com/caddyserver/certmagic/filestorage.go index 0de8f746..6887d817 100644 --- a/vendor/github.com/mholt/certmagic/filestorage.go +++ b/vendor/github.com/caddyserver/certmagic/filestorage.go @@ -15,7 +15,9 @@ package certmagic import ( + "encoding/json" "fmt" + "io" "io/ioutil" "log" "os" @@ -123,7 +125,6 @@ func (fs *FileStorage) Filename(key string) string { // Lock obtains a lock named by the given key. It blocks // until the lock can be obtained or an error is returned. func (fs *FileStorage) Lock(key string) error { - start := time.Now() filename := fs.lockFilename(key) for { @@ -139,7 +140,16 @@ func (fs *FileStorage) Lock(key string) error { // lock file already exists - info, err := os.Stat(filename) + var meta lockMeta + f, err := os.Open(filename) + if err == nil { + err2 := json.NewDecoder(f).Decode(&meta) + f.Close() + if err2 != nil { + return err2 + } + } + switch { case os.IsNotExist(err): // must have just been removed; try again to create it @@ -149,18 +159,13 @@ func (fs *FileStorage) Lock(key string) error { // unexpected error return fmt.Errorf("accessing lock file: %v", err) - case fileLockIsStale(info): + case fileLockIsStale(meta): // lock file is stale - delete it and try again to create one - log.Printf("[INFO][%s] Lock for '%s' is stale; removing then retrying: %s", - fs, key, filename) + log.Printf("[INFO][%s] Lock for '%s' is stale (created: %s, last update: %s); removing then retrying: %s", + fs, key, meta.Created, meta.Updated, filename) removeLockfile(filename) continue - case time.Since(start) > staleLockDuration*2: - // should never happen, hopefully - return fmt.Errorf("possible deadlock: %s passed trying to obtain lock for %s", - time.Since(start), key) - default: // lockfile exists and is not stale; // just wait a moment and try again @@ -186,35 +191,43 @@ func (fs *FileStorage) lockDir() string { return filepath.Join(fs.Path, "locks") } -func fileLockIsStale(info os.FileInfo) bool { - if info == nil { - return true +func fileLockIsStale(meta lockMeta) bool { + ref := meta.Updated + if ref.IsZero() { + ref = meta.Created } - return time.Since(info.ModTime()) > staleLockDuration + // since updates are exactly every lockFreshnessInterval, + // add a grace period for the actual file read+write to + // take place + return time.Since(ref) > lockFreshnessInterval*2 } // createLockfile atomically creates the lockfile // identified by filename. A successfully created // lockfile should be removed with removeLockfile. func createLockfile(filename string) error { - err := atomicallyCreateFile(filename) - if err == nil { - // if the app crashes in removeLockfile(), there is a - // small chance the .unlock file is left behind; it's - // safe to simply remove it as it's a guard against - // double removal of the .lock file. - os.Remove(filename + ".unlock") + err := atomicallyCreateFile(filename, true) + if err != nil { + return err } - return err + + go keepLockfileFresh(filename) + + // if the app crashes in removeLockfile(), there is a + // small chance the .unlock file is left behind; it's + // safe to simply remove it as it's a guard against + // double removal of the .lock file. + _ = os.Remove(filename + ".unlock") + return nil } // removeLockfile atomically removes filename, // which must be a lockfile created by createLockfile. // See discussion in PR #7 for more background: -// https://github.com/mholt/certmagic/pull/7 +// https://github.com/caddyserver/certmagic/pull/7 func removeLockfile(filename string) error { unlockFilename := filename + ".unlock" - if err := atomicallyCreateFile(unlockFilename); err != nil { + if err := atomicallyCreateFile(unlockFilename, false); err != nil { if os.IsExist(err) { // another process is handling the unlocking return nil @@ -225,16 +238,85 @@ func removeLockfile(filename string) error { return os.Remove(filename) } +// keepLockfileFresh continuously updates the lock file +// at filename with the current timestamp. It stops +// when the file disappears (happy path = lock released), +// or when there is an error at any point. Since it polls +// every lockFreshnessInterval, this function might +// not terminate until up to lockFreshnessInterval after +// the lock is released. +func keepLockfileFresh(filename string) { + for { + time.Sleep(lockFreshnessInterval) + done, err := updateLockfileFreshness(filename) + if err != nil { + log.Printf("[ERROR] Keeping lock file fresh: %v - terminating lock maintenance (lockfile: %s)", err, filename) + return + } + if done { + return + } + } +} + +// updateLockfileFreshness updates the lock file at filename +// with the current timestamp. It returns true if the parent +// loop can terminate (i.e. no more need to update the lock). +func updateLockfileFreshness(filename string) (bool, error) { + f, err := os.OpenFile(filename, os.O_RDWR, 0644) + if os.IsNotExist(err) { + return true, nil // lock released + } + if err != nil { + return true, err + } + defer f.Close() + + // read contents + metaBytes, err := ioutil.ReadAll(io.LimitReader(f, 2048)) + if err != nil { + return true, err + } + var meta lockMeta + if err := json.Unmarshal(metaBytes, &meta); err != nil { + return true, err + } + + // truncate file and reset I/O offset to beginning + if err := f.Truncate(0); err != nil { + return true, err + } + if _, err := f.Seek(0, 0); err != nil { + return true, err + } + + // write updated timestamp + meta.Updated = time.Now() + return false, json.NewEncoder(f).Encode(meta) +} + // atomicallyCreateFile atomically creates the file // identified by filename if it doesn't already exist. -func atomicallyCreateFile(filename string) error { - // no need to check this, we only really care about the file creation error - os.MkdirAll(filepath.Dir(filename), 0700) - f, err := os.OpenFile(filename, os.O_CREATE|os.O_EXCL, 0644) - if err == nil { - f.Close() +func atomicallyCreateFile(filename string, writeLockInfo bool) error { + // no need to check this error, we only really care about the file creation error + _ = os.MkdirAll(filepath.Dir(filename), 0700) + f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644) + if err != nil { + return err } - return err + defer f.Close() + if writeLockInfo { + now := time.Now() + meta := lockMeta{ + Created: now, + Updated: now, + } + err := json.NewEncoder(f).Encode(meta) + if err != nil { + return err + } + } + return nil } // homeDir returns the best guess of the current user's home @@ -264,12 +346,22 @@ func dataDir() string { return filepath.Join(baseDir, "certmagic") } -// staleLockDuration is the length of time -// before considering a lock to be stale. -const staleLockDuration = 2 * time.Hour +// lockMeta is written into a lock file. +type lockMeta struct { + Created time.Time `json:"created,omitempty"` + Updated time.Time `json:"updated,omitempty"` +} + +// lockFreshnessInterval is how often to update +// a lock's timestamp. Locks with a timestamp +// more than this duration in the past (plus a +// grace period for latency) can be considered +// stale. +const lockFreshnessInterval = 5 * time.Second // fileLockPollInterval is how frequently // to check the existence of a lock file const fileLockPollInterval = 1 * time.Second +// Interface guard var _ Storage = (*FileStorage)(nil) diff --git a/vendor/github.com/caddyserver/certmagic/go.mod b/vendor/github.com/caddyserver/certmagic/go.mod new file mode 100644 index 00000000..e3c21f22 --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/go.mod @@ -0,0 +1,9 @@ +module github.com/caddyserver/certmagic + +go 1.13 + +require ( + github.com/go-acme/lego/v3 v3.4.0 + github.com/klauspost/cpuid v1.2.3 + golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 +) diff --git a/vendor/github.com/caddyserver/certmagic/go.sum b/vendor/github.com/caddyserver/certmagic/go.sum new file mode 100644 index 00000000..17c0626f --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/go.sum @@ -0,0 +1,380 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= +github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw= +github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= +github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= +github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= +github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= +github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU= +github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= +github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= +github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-acme/lego/v3 v3.4.0 h1:deB9NkelA+TfjGHVw8J7iKl/rMtffcGMWSMmptvMv0A= +github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M= +github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= +github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= +github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= +github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= +github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI= +github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= +github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= +github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= +github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= +github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= +github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= +golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 h1:6KET3Sqa7fkVfD63QnAM81ZeYg5n4HwApOJkufONnHA= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a h1:Yu34BogBivvmu7SAzHHaB9nZWH5D1C+z3F1jyIaYZSQ= +golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= +gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/vendor/github.com/mholt/certmagic/handshake.go b/vendor/github.com/caddyserver/certmagic/handshake.go similarity index 70% rename from vendor/github.com/mholt/certmagic/handshake.go rename to vendor/github.com/caddyserver/certmagic/handshake.go index 51dd39fd..e3fd4368 100644 --- a/vendor/github.com/mholt/certmagic/handshake.go +++ b/vendor/github.com/caddyserver/certmagic/handshake.go @@ -15,6 +15,7 @@ package certmagic import ( + "context" "crypto/tls" "encoding/json" "fmt" @@ -22,10 +23,9 @@ import ( "net" "strings" "sync" - "sync/atomic" "time" - "github.com/go-acme/lego/challenge/tlsalpn01" + "github.com/go-acme/lego/v3/challenge/tlsalpn01" ) // GetCertificate gets a certificate to satisfy clientHello. In getting @@ -37,9 +37,7 @@ import ( // // This method is safe for use as a tls.Config.GetCertificate callback. func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { - if cfg.OnEvent != nil { - cfg.OnEvent("tls_handshake_started", clientHello) - } + cfg.emit("tls_handshake_started", clientHello) // special case: serve up the certificate for a TLS-ALPN ACME challenge // (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05) @@ -55,22 +53,23 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif // should already have taken care of that when we made the tls.Config) challengeCert, ok, err := cfg.tryDistributedChallengeSolver(clientHello) if err != nil { - log.Printf("[ERROR][%s] TLS-ALPN: %v", clientHello.ServerName, err) + log.Printf("[ERROR][%s] TLS-ALPN challenge: %v", clientHello.ServerName, err) } if ok { + log.Printf("[INFO][%s] Served key authentication certificate (distributed TLS-ALPN challenge)", clientHello.ServerName) return &challengeCert.Certificate, nil } - return nil, fmt.Errorf("no certificate to complete TLS-ALPN challenge for SNI name: %s", clientHello.ServerName) } + log.Printf("[INFO][%s] Served key authentication certificate (TLS-ALPN challenge)", clientHello.ServerName) return &challengeCert.Certificate, nil } } // get the certificate and serve it up cert, err := cfg.getCertDuringHandshake(clientHello, true, true) - if err == nil && cfg.OnEvent != nil { - cfg.OnEvent("tls_handshake_completed", clientHello) + if err == nil { + cfg.emit("tls_handshake_completed", clientHello) } return &cert.Certificate, err } @@ -94,8 +93,6 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, matched, defaulted bool) { name := NormalizedName(hello.ServerName) - var ok bool - if name == "" { // if SNI is empty, prefer matching IP address if hello.Conn != nil { @@ -104,8 +101,8 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, if err == nil { addr = ip } - if cert, ok = cfg.certCache.getFirstMatchingCert(addr); ok { - matched = true + cert, matched = cfg.selectCert(hello, addr) + if matched { return } } @@ -113,15 +110,15 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, // fall back to a "default" certificate, if specified if cfg.DefaultServerName != "" { normDefault := NormalizedName(cfg.DefaultServerName) - if cert, ok = cfg.certCache.getFirstMatchingCert(normDefault); ok { - defaulted = true + cert, defaulted = cfg.selectCert(hello, normDefault) + if defaulted { return } } } else { // if SNI is specified, try an exact match first - if cert, ok = cfg.certCache.getFirstMatchingCert(name); ok { - matched = true + cert, matched = cfg.selectCert(hello, name) + if matched { return } @@ -131,8 +128,8 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, for i := range labels { labels[i] = "*" candidate := strings.Join(labels, ".") - if cert, ok = cfg.certCache.getFirstMatchingCert(candidate); ok { - matched = true + cert, matched = cfg.selectCert(hello, candidate) + if matched { return } } @@ -156,7 +153,7 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, } // otherwise, we're bingo on ammo; see issues - // mholt/caddy#2035 and mholt/caddy#1303 (any + // caddyserver/caddy#2035 and caddyserver/caddy#1303 (any // change to certificate matching behavior must // account for hosts defined where the hostname // is empty or a catch-all, like ":443" or @@ -165,6 +162,53 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, return } +// selectCert uses hello to select a certificate from the +// cache for name. If cfg.CertSelection is set, it will be +// used to make the decision. Otherwise, the first matching +// unexpired cert is returned. As a special case, if no +// certificates match name and cfg.CertSelection is set, +// then all certificates in the cache will be passed in +// for the cfg.CertSelection to make the final decision. +func (cfg *Config) selectCert(hello *tls.ClientHelloInfo, name string) (Certificate, bool) { + choices := cfg.certCache.getAllMatchingCerts(name) + if len(choices) == 0 { + if cfg.CertSelection == nil { + return Certificate{}, false + } + choices = cfg.certCache.getAllCerts() + } + if cfg.CertSelection == nil { + cert, err := DefaultCertificateSelector(hello, choices) + return cert, err == nil + } + cert, err := cfg.CertSelection.SelectCertificate(hello, choices) + return cert, err == nil +} + +// DefaultCertificateSelector is the default certificate selection logic +// given a choice of certificates. If there is at least one certificate in +// choices, it always returns a certificate without error. It chooses the +// first non-expired certificate that the client supports if possible, +// otherwise it returns an expired certificate that the client supports, +// otherwise it just returns the first certificate in the list of choices. +func DefaultCertificateSelector(hello *tls.ClientHelloInfo, choices []Certificate) (Certificate, error) { + if len(choices) == 0 { + return Certificate{}, fmt.Errorf("no certificates available") + } + now := time.Now() + best := choices[0] + for _, choice := range choices { + if err := hello.SupportsCertificate(&choice.Certificate); err != nil { + continue + } + best = choice // at least the client supports it... + if now.After(choice.Leaf.NotBefore) && now.Before(choice.Leaf.NotAfter) { + return choice, nil // ...and unexpired, great! "Certificate, I choose you!" + } + } + return best, nil // all matching certs are expired or incompatible, oh well +} + // getCertDuringHandshake will get a certificate for hello. It first tries // the in-memory cache. If no certificate for hello is in the cache, the // config most closely corresponding to hello will be loaded. If that config @@ -177,14 +221,14 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, // // This function is safe for concurrent use. func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) { - name := NormalizedName(hello.ServerName) - // First check our in-memory cache to see if we've already loaded it cert, matched, defaulted := cfg.getCertificate(hello) if matched { return cert, nil } + name := cfg.getNameFromClientHello(hello) + // If OnDemand is enabled, then we might be able to load or // obtain a needed certificate if cfg.OnDemand != nil && loadIfNecessary { @@ -206,11 +250,6 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece return Certificate{}, err } - // Name has to qualify for a certificate - if !HostQualifies(name) { - return cert, fmt.Errorf("hostname '%s' does not qualify for certificate", name) - } - // Obtain certificate from the CA return cfg.obtainOnDemandCertificate(hello) } @@ -224,14 +263,24 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece return Certificate{}, fmt.Errorf("no certificate available for '%s'", name) } -// checkIfCertShouldBeObtained checks to see if an on-demand tls certificate -// should be obtained for a given domain based upon the config settings. If +// checkIfCertShouldBeObtained checks to see if an on-demand TLS certificate +// should be obtained for a given domain based upon the config settings. If // a non-nil error is returned, do not issue a new certificate for name. func (cfg *Config) checkIfCertShouldBeObtained(name string) error { if cfg.OnDemand == nil { return fmt.Errorf("not configured for on-demand certificate issuance") } - return cfg.OnDemand.Allowed(name) + if !SubjectQualifiesForCert(name) { + return fmt.Errorf("subject name does not qualify for certificate: %s", name) + } + if cfg.OnDemand.DecisionFunc != nil { + return cfg.OnDemand.DecisionFunc(name) + } + if len(cfg.OnDemand.hostWhitelist) > 0 && + !cfg.OnDemand.whitelistContains(name) { + return fmt.Errorf("certificate for '%s' is not managed", name) + } + return nil } // obtainOnDemandCertificate obtains a certificate for hello. @@ -240,7 +289,7 @@ func (cfg *Config) checkIfCertShouldBeObtained(name string) error { // // This function is safe for use by multiple concurrent goroutines. func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certificate, error) { - name := NormalizedName(hello.ServerName) + name := cfg.getNameFromClientHello(hello) // We must protect this process from happening concurrently, so synchronize. obtainCertWaitChansMu.Lock() @@ -261,7 +310,10 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif // obtain the certificate log.Printf("[INFO] Obtaining new certificate for %s", name) - err := cfg.ObtainCert(name, false) + // TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false + ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second) + defer cancel() + err := cfg.ObtainCert(ctx, name, false) // immediately unblock anyone waiting for it; doing this in // a defer would risk deadlock because of the recursive call @@ -272,27 +324,12 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif obtainCertWaitChansMu.Unlock() if err != nil { - // Failed to solve challenge, so don't allow another on-demand - // issue for this name to be attempted for a little while. - failedIssuanceMu.Lock() - failedIssuance[name] = time.Now() - go func(name string) { - time.Sleep(5 * time.Minute) - failedIssuanceMu.Lock() - delete(failedIssuance, name) - failedIssuanceMu.Unlock() - }(name) - failedIssuanceMu.Unlock() + // shucks; failed to solve challenge on-demand return Certificate{}, err } - // Success - update counters and stuff - atomic.AddInt32(&cfg.OnDemand.obtainedCount, 1) - lastIssueTimeMu.Lock() - lastIssueTime = time.Now() - lastIssueTimeMu.Unlock() - - // certificate is already on disk; now just start over to load it and serve it + // success; certificate was just placed on disk, so + // we need only restart serving the certificate return cfg.getCertDuringHandshake(hello, true, false) } @@ -301,24 +338,24 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif // This function is safe for use by multiple concurrent goroutines. func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certificate) (Certificate, error) { // Check cert expiration - timeLeft := cert.NotAfter.Sub(time.Now().UTC()) - if timeLeft < cfg.RenewDurationBefore { - log.Printf("[INFO] Certificate for %v expires in %v; attempting renewal", cert.Names, timeLeft) + timeLeft := cert.Leaf.NotAfter.Sub(time.Now().UTC()) + if currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio) { + log.Printf("[INFO] Certificate for %v expires in %s; attempting renewal", cert.Names, timeLeft) return cfg.renewDynamicCertificate(hello, cert) } // Check OCSP staple validity - if cert.OCSP != nil { - refreshTime := cert.OCSP.ThisUpdate.Add(cert.OCSP.NextUpdate.Sub(cert.OCSP.ThisUpdate) / 2) + if cert.ocsp != nil { + refreshTime := cert.ocsp.ThisUpdate.Add(cert.ocsp.NextUpdate.Sub(cert.ocsp.ThisUpdate) / 2) if time.Now().After(refreshTime) { - err := stapleOCSP(cfg.Storage, &cert, nil) + _, err := stapleOCSP(cfg.Storage, &cert, nil) if err != nil { // An error with OCSP stapling is not the end of the world, and in fact, is // quite common considering not all certs have issuer URLs that support it. log.Printf("[ERROR] Getting OCSP for %s: %v", hello.ServerName, err) } cfg.certCache.mu.Lock() - cfg.certCache.cache[cert.Hash] = cert + cfg.certCache.cache[cert.hash] = cert cfg.certCache.mu.Unlock() } } @@ -333,7 +370,7 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi // // This function is safe for use by multiple concurrent goroutines. func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCert Certificate) (Certificate, error) { - name := NormalizedName(hello.ServerName) + name := cfg.getNameFromClientHello(hello) obtainCertWaitChansMu.Lock() wait, ok := obtainCertWaitChans[name] @@ -350,9 +387,22 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe obtainCertWaitChans[name] = wait obtainCertWaitChansMu.Unlock() + // Make sure a certificate for this name should be obtained on-demand + err := cfg.checkIfCertShouldBeObtained(name) + if err != nil { + // if not, remove from cache (it will be deleted from storage later) + cfg.certCache.mu.Lock() + cfg.certCache.removeCertificate(currentCert) + cfg.certCache.mu.Unlock() + return Certificate{}, err + } + // renew and reload the certificate log.Printf("[INFO] Renewing certificate for %s", name) - err := cfg.RenewCert(name, false) + // TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false + ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second) + defer cancel() + err = cfg.RenewCert(ctx, name, false) if err == nil { // even though the recursive nature of the dynamic cert loading // would just call this function anyway, we do it here to @@ -384,10 +434,15 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe // tryDistributedChallengeSolver is to be called when the clientHello pertains to // a TLS-ALPN challenge and a certificate is required to solve it. This method // checks the distributed store of challenge info files and, if a matching ServerName -// is present, it makes a certificate to solve this challenge and returns it. +// is present, it makes a certificate to solve this challenge and returns it. For +// this to succeed, it requires that cfg.Issuer is of type *ACMEManager. // A boolean true is returned if a valid certificate is returned. func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInfo) (Certificate, bool, error) { - tokenKey := distributedSolver{config: cfg}.challengeTokensKey(clientHello.ServerName) + am, ok := cfg.Issuer.(*ACMEManager) + if !ok { + return Certificate{}, false, nil + } + tokenKey := distributedSolver{acmeManager: am, caURL: am.CA}.challengeTokensKey(clientHello.ServerName) chalInfoBytes, err := cfg.Storage.Load(tokenKey) if err != nil { if _, ok := err.(ErrNotExist); ok { @@ -413,6 +468,24 @@ func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInf return Certificate{Certificate: *cert}, true, nil } +// getNameFromClientHello returns a normalized form of hello.ServerName. +// If hello.ServerName is empty (i.e. client did not use SNI), then the +// associated connection's local address is used to extract an IP address. +func (*Config) getNameFromClientHello(hello *tls.ClientHelloInfo) string { + name := NormalizedName(hello.ServerName) + if name != "" || hello.Conn == nil { + return name + } + + // if no SNI, try using IP address on the connection + localAddr := hello.Conn.LocalAddr().String() + localAddrHost, _, err := net.SplitHostPort(localAddr) + if err == nil { + return localAddrHost + } + return localAddr +} + // NormalizedName returns a cleaned form of serverName that is // used for consistency when referring to a SNI value. func NormalizedName(serverName string) string { diff --git a/vendor/github.com/mholt/certmagic/httphandler.go b/vendor/github.com/caddyserver/certmagic/httphandler.go similarity index 70% rename from vendor/github.com/mholt/certmagic/httphandler.go rename to vendor/github.com/caddyserver/certmagic/httphandler.go index b649c303..856e723c 100644 --- a/vendor/github.com/mholt/certmagic/httphandler.go +++ b/vendor/github.com/caddyserver/certmagic/httphandler.go @@ -20,7 +20,7 @@ import ( "net/http" "strings" - "github.com/go-acme/lego/challenge/http01" + "github.com/go-acme/lego/v3/challenge/http01" ) // HTTPChallengeHandler wraps h in a handler that can solve the ACME @@ -28,55 +28,57 @@ import ( // cache backed by a functional storage facility, since that is where // the challenge state is stored between initiation and solution. // -// If a request is not an ACME HTTP challenge, h willl be invoked. -func (cfg *Config) HTTPChallengeHandler(h http.Handler) http.Handler { +// If a request is not an ACME HTTP challenge, h will be invoked. +func (am *ACMEManager) HTTPChallengeHandler(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if cfg.HandleHTTPChallenge(w, r) { + if am.HandleHTTPChallenge(w, r) { return } h.ServeHTTP(w, r) }) } -// HandleHTTPChallenge uses cfg to solve challenge requests from an ACME +// HandleHTTPChallenge uses am to solve challenge requests from an ACME // server that were initiated by this instance or any other instance in -// this cluster (being, any instances using the same storage cfg does). +// this cluster (being, any instances using the same storage am does). // // If the HTTP challenge is disabled, this function is a no-op. // -// If cfg is nil or if cfg does not have a certificate cache backed by +// If am is nil or if am does not have a certificate cache backed by // usable storage, solving the HTTP challenge will fail. // // It returns true if it handled the request; if so, the response has // already been written. If false is returned, this call was a no-op and // the request has not been handled. -func (cfg *Config) HandleHTTPChallenge(w http.ResponseWriter, r *http.Request) bool { - if cfg == nil { +func (am *ACMEManager) HandleHTTPChallenge(w http.ResponseWriter, r *http.Request) bool { + if am == nil { return false } - if cfg.DisableHTTPChallenge { + if am.DisableHTTPChallenge { return false } if !LooksLikeHTTPChallenge(r) { return false } - return cfg.distributedHTTPChallengeSolver(w, r) + return am.distributedHTTPChallengeSolver(w, r) } // distributedHTTPChallengeSolver checks to see if this challenge // request was initiated by this or another instance which uses the -// same storage as cfg does, and attempts to complete the challenge for +// same storage as am does, and attempts to complete the challenge for // it. It returns true if the request was handled; false otherwise. -func (cfg *Config) distributedHTTPChallengeSolver(w http.ResponseWriter, r *http.Request) bool { - if cfg == nil { +func (am *ACMEManager) distributedHTTPChallengeSolver(w http.ResponseWriter, r *http.Request) bool { + if am == nil { return false } - tokenKey := distributedSolver{config: cfg}.challengeTokensKey(r.Host) - chalInfoBytes, err := cfg.Storage.Load(tokenKey) + host := hostOnly(r.Host) + + tokenKey := distributedSolver{acmeManager: am, caURL: am.CA}.challengeTokensKey(host) + chalInfoBytes, err := am.config.Storage.Load(tokenKey) if err != nil { if _, ok := err.(ErrNotExist); !ok { - log.Printf("[ERROR][%s] Opening distributed HTTP challenge token file: %v", r.Host, err) + log.Printf("[ERROR][%s] Opening distributed HTTP challenge token file: %v", host, err) } return false } @@ -84,7 +86,7 @@ func (cfg *Config) distributedHTTPChallengeSolver(w http.ResponseWriter, r *http var chalInfo challengeInfo err = json.Unmarshal(chalInfoBytes, &chalInfo) if err != nil { - log.Printf("[ERROR][%s] Decoding challenge token file %s (corrupted?): %v", r.Host, tokenKey, err) + log.Printf("[ERROR][%s] Decoding challenge token file %s (corrupted?): %v", host, tokenKey, err) return false } @@ -97,12 +99,12 @@ func (cfg *Config) distributedHTTPChallengeSolver(w http.ResponseWriter, r *http func answerHTTPChallenge(w http.ResponseWriter, r *http.Request, chalInfo challengeInfo) bool { challengeReqPath := http01.ChallengePath(chalInfo.Token) if r.URL.Path == challengeReqPath && - strings.HasPrefix(r.Host, chalInfo.Domain) && + strings.EqualFold(hostOnly(r.Host), chalInfo.Domain) && // mitigate DNS rebinding attacks r.Method == "GET" { w.Header().Add("Content-Type", "text/plain") w.Write([]byte(chalInfo.KeyAuth)) r.Close = true - log.Printf("[INFO][%s] Served key authentication (distributed)", chalInfo.Domain) + log.Printf("[INFO][%s] Served key authentication (HTTP challenge)", chalInfo.Domain) return true } return false diff --git a/vendor/github.com/mholt/certmagic/maintain.go b/vendor/github.com/caddyserver/certmagic/maintain.go similarity index 53% rename from vendor/github.com/mholt/certmagic/maintain.go rename to vendor/github.com/caddyserver/certmagic/maintain.go index 75870adf..89741932 100644 --- a/vendor/github.com/mholt/certmagic/maintain.go +++ b/vendor/github.com/caddyserver/certmagic/maintain.go @@ -15,7 +15,13 @@ package certmagic import ( + "context" + "crypto/x509" + "encoding/pem" + "fmt" "log" + "path" + "strings" "time" "golang.org/x/crypto/ocsp" @@ -32,24 +38,22 @@ func (certCache *Cache) maintainAssets() { log.Printf("[INFO][cache:%p] Started certificate maintenance routine", certCache) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + for { select { case <-renewalTicker.C: - log.Printf("[INFO][cache:%p] Scanning for expiring certificates", certCache) - err := certCache.RenewManagedCertificates(false) + err := certCache.RenewManagedCertificates(ctx) if err != nil { log.Printf("[ERROR][cache:%p] Renewing managed certificates: %v", certCache, err) } - log.Printf("[INFO][cache:%p] Done scanning certificates", certCache) case <-ocspTicker.C: - log.Printf("[INFO][cache:%p] Scanning for stale OCSP staples", certCache) - certCache.updateOCSPStaples() - // certCache.deleteOldStapleFiles() - log.Printf("[INFO][cache:%p] Done checking OCSP staples", certCache) + certCache.updateOCSPStaples(ctx) case <-certCache.stopChan: renewalTicker.Stop() ocspTicker.Stop() - // TODO: stop any in-progress maintenance operations and clear locks we made + // TODO: stop any in-progress maintenance operations and clear locks we made (this might be done now with our use of context) log.Printf("[INFO][cache:%p] Stopped certificate maintenance routine", certCache) close(certCache.doneChan) return @@ -60,8 +64,9 @@ func (certCache *Cache) maintainAssets() { // RenewManagedCertificates renews managed certificates, // including ones loaded on-demand. Note that this is done // automatically on a regular basis; normally you will not -// need to call this. -func (certCache *Cache) RenewManagedCertificates(interactive bool) error { +// need to call this. This method assumes non-interactive +// mode (i.e. operating in the background). +func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { // configs will hold a map of certificate name to the config // to use when managing that certificate configs := make(map[string]*Config) @@ -98,10 +103,11 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error { log.Printf("[ERROR] No configuration associated with certificate for names %v; unable to manage", cert.Names) continue } - configs[cert.Names[0]] = cfg // if time is up or expires soon, we need to try to renew it if cert.NeedsRenewal(cfg) { + configs[cert.Names[0]] = cfg + // see if the certificate in storage has already been renewed, possibly by another // instance that didn't coordinate with this one; if so, just load it (this // might happen if another instance already renewed it - kinda sloppy but checking disk @@ -130,71 +136,74 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error { // Reload certificates that merely need to be updated in memory for _, oldCert := range reloadQueue { - timeLeft := oldCert.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO] Certificate for %v expires in %v, but is already renewed in storage; reloading stored certificate", + timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC()) + log.Printf("[INFO] %v Maintenance routine: certificate expires in %s, but is already renewed in storage; reloading stored certificate", oldCert.Names, timeLeft) cfg := configs[oldCert.Names[0]] + // crucially, this happens OUTSIDE a lock on the certCache err := cfg.reloadManagedCertificate(oldCert) if err != nil { - if interactive { - return err // operator is present, so report error immediately - } log.Printf("[ERROR] Loading renewed certificate: %v", err) + continue } } // Renewal queue for _, oldCert := range renewQueue { - timeLeft := oldCert.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO] Certificate for %v expires in %v; attempting renewal", oldCert.Names, timeLeft) - cfg := configs[oldCert.Names[0]] - - // Get the name which we should use to renew this certificate; - // we only support managing certificates with one name per cert, - // so this should be easy. - renewName := oldCert.Names[0] - - // perform renewal - err := cfg.RenewCert(renewName, interactive) + err := certCache.queueRenewalTask(ctx, oldCert, cfg) if err != nil { - if interactive { - // Certificate renewal failed and the operator is present. See a discussion about - // this in issue mholt/caddy#642. For a while, we only stopped if the certificate - // was expired, but in reality, there is no difference between reporting it now - // versus later, except that there's somebody present to deal with it right now. - // Follow-up: See issue mholt/caddy#1680. Only fail in this case if the certificate - // is dangerously close to expiration. - timeLeft := oldCert.NotAfter.Sub(time.Now().UTC()) - if timeLeft < cfg.RenewDurationBeforeAtStartup { - return err - } - } log.Printf("[ERROR] %v", err) + continue + } + } + + // Deletion queue + certCache.mu.Lock() + for _, cert := range deleteQueue { + certCache.removeCertificate(cert) + } + certCache.mu.Unlock() + + return nil +} + +func (certCache *Cache) queueRenewalTask(ctx context.Context, oldCert Certificate, cfg *Config) error { + timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC()) + log.Printf("[INFO] %v Maintenance routine: certificate expires in %v; queueing for renewal", oldCert.Names, timeLeft) + + // Get the name which we should use to renew this certificate; + // we only support managing certificates with one name per cert, + // so this should be easy. + renewName := oldCert.Names[0] + + // queue up this renewal job (is a no-op if already active or queued) + jm.Submit("renew_"+renewName, func() error { + timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC()) + log.Printf("[INFO] %v Maintenance routine: attempting renewal with %v remaining", oldCert.Names, timeLeft) + + // perform renewal - crucially, this happens OUTSIDE a lock on certCache + err := cfg.RenewCert(ctx, renewName, false) + if err != nil { if cfg.OnDemand != nil { // loaded dynamically, remove dynamically - deleteQueue = append(deleteQueue, oldCert) + certCache.mu.Lock() + certCache.removeCertificate(oldCert) + certCache.mu.Unlock() } - continue + return fmt.Errorf("%v %v", oldCert.Names, err) } // successful renewal, so update in-memory cache by loading // renewed certificate so it will be used with handshakes err = cfg.reloadManagedCertificate(oldCert) if err != nil { - if interactive { - return err // operator is present, so report error immediately - } - log.Printf("[ERROR] %v", err) + return ErrNoRetry{fmt.Errorf("%v %v", oldCert.Names, err)} } - } - - // Deletion queue - for _, cert := range deleteQueue { - certCache.removeCertificate(cert) - } + return nil + }) return nil } @@ -205,31 +214,46 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error { // OCSP maintenance strives to abide the relevant points on // Ryan Sleevi's recommendations for good OCSP support: // https://gist.github.com/sleevi/5efe9ef98961ecfb4da8 -func (certCache *Cache) updateOCSPStaples() { - // Create a temporary place to store updates - // until we release the potentially long-lived - // read lock and use a short-lived write lock - // on the certificate cache. +func (certCache *Cache) updateOCSPStaples(ctx context.Context) { + // temporary structures to store updates or tasks + // so that we can keep our locks short-lived type ocspUpdate struct { rawBytes []byte parsed *ocsp.Response } + type updateQueueEntry struct { + cert Certificate + certHash string + lastNextUpdate time.Time + } updated := make(map[string]ocspUpdate) + var updateQueue []updateQueueEntry + var renewQueue []Certificate + configs := make(map[string]*Config) + // obtain brief read lock during our scan to see which staples need updating certCache.mu.RLock() for certHash, cert := range certCache.cache { // no point in updating OCSP for expired certificates - if time.Now().After(cert.NotAfter) { + if time.Now().After(cert.Leaf.NotAfter) { continue } - var lastNextUpdate time.Time - if cert.OCSP != nil { - lastNextUpdate = cert.OCSP.NextUpdate - if freshOCSP(cert.OCSP) { + if cert.ocsp != nil { + lastNextUpdate = cert.ocsp.NextUpdate + if freshOCSP(cert.ocsp) { continue // no need to update staple if ours is still fresh } } + updateQueue = append(updateQueue, updateQueueEntry{cert, certHash, lastNextUpdate}) + } + certCache.mu.RUnlock() + + // perform updates outside of any lock on certCache + for _, qe := range updateQueue { + cert := qe.cert + certHash := qe.certHash + lastNextUpdate := qe.lastNextUpdate cfg, err := certCache.getConfig(cert) if err != nil { @@ -242,9 +266,9 @@ func (certCache *Cache) updateOCSPStaples() { continue } - err = stapleOCSP(cfg.Storage, &cert, nil) + ocspResp, err := stapleOCSP(cfg.Storage, &cert, nil) if err != nil { - if cert.OCSP != nil { + if cert.ocsp != nil { // if there was no staple before, that's fine; otherwise we should log the error log.Printf("[ERROR] Checking OCSP: %v", err) } @@ -254,34 +278,66 @@ func (certCache *Cache) updateOCSPStaples() { // By this point, we've obtained the latest OCSP response. // If there was no staple before, or if the response is updated, make // sure we apply the update to all names on the certificate. - if cert.OCSP != nil && (lastNextUpdate.IsZero() || lastNextUpdate != cert.OCSP.NextUpdate) { + if cert.ocsp != nil && (lastNextUpdate.IsZero() || lastNextUpdate != cert.ocsp.NextUpdate) { log.Printf("[INFO] Advancing OCSP staple for %v from %s to %s", - cert.Names, lastNextUpdate, cert.OCSP.NextUpdate) - updated[certHash] = ocspUpdate{rawBytes: cert.Certificate.OCSPStaple, parsed: cert.OCSP} + cert.Names, lastNextUpdate, cert.ocsp.NextUpdate) + updated[certHash] = ocspUpdate{rawBytes: cert.Certificate.OCSPStaple, parsed: cert.ocsp} + } + + // If a managed certificate was revoked, we should attempt + // to replace it with a new one. If that fails, oh well. + if cert.managed && ocspResp.Status == ocsp.Revoked && len(cert.Names) > 0 { + renewQueue = append(renewQueue, cert) + configs[cert.Names[0]] = cfg } } - certCache.mu.RUnlock() // These write locks should be brief since we have all the info we need now. for certKey, update := range updated { certCache.mu.Lock() cert := certCache.cache[certKey] - cert.OCSP = update.parsed + cert.ocsp = update.parsed cert.Certificate.OCSPStaple = update.rawBytes certCache.cache[certKey] = cert certCache.mu.Unlock() } + + // We attempt to replace any certificates that were revoked. + // Crucially, this happens OUTSIDE a lock on the certCache. + for _, oldCert := range renewQueue { + log.Printf("[INFO] OCSP status for managed certificate %v (expiration=%s) is REVOKED; attempting to replace with new certificate", + oldCert.Names, oldCert.Leaf.NotAfter) + + renewName := oldCert.Names[0] + cfg := configs[renewName] + + // TODO: consider using a new key in this situation, but we don't know if key storage has been compromised... + err := cfg.RenewCert(ctx, renewName, false) + if err != nil { + // probably better to not serve a revoked certificate at all + log.Printf("[ERROR] Obtaining new certificate for %v due to OCSP status of revoked: %v; removing from cache", oldCert.Names, err) + certCache.mu.Lock() + certCache.removeCertificate(oldCert) + certCache.mu.Unlock() + continue + } + err = cfg.reloadManagedCertificate(oldCert) + if err != nil { + log.Printf("[ERROR] After obtaining new certificate due to OCSP status of revoked: %v", err) + continue + } + } } // CleanStorageOptions specifies how to clean up a storage unit. type CleanStorageOptions struct { - OCSPStaples bool - // TODO: long-expired certificates + OCSPStaples bool + ExpiredCerts bool + ExpiredCertGracePeriod time.Duration } -// CleanStorage tidies up the given storage according to opts; this -// generally involves deleting assets which are no longer required. -// TODO: We should do this for long-expired certificates, too. +// CleanStorage removes assets which are no longer useful, +// according to opts. func CleanStorage(storage Storage, opts CleanStorageOptions) { if opts.OCSPStaples { err := deleteOldOCSPStaples(storage) @@ -289,6 +345,13 @@ func CleanStorage(storage Storage, opts CleanStorageOptions) { log.Printf("[ERROR] Deleting old OCSP staples: %v", err) } } + if opts.ExpiredCerts { + err := deleteExpiredCerts(storage, opts.ExpiredCertGracePeriod) + if err != nil { + log.Printf("[ERROR] Deleting expired certificates: %v", err) + } + } + // TODO: delete stale locks? } func deleteOldOCSPStaples(storage Storage) error { @@ -323,16 +386,92 @@ func deleteOldOCSPStaples(storage Storage) error { return nil } +func deleteExpiredCerts(storage Storage, gracePeriod time.Duration) error { + issuerKeys, err := storage.List(prefixCerts, false) + if err != nil { + // maybe just hasn't been created yet; no big deal + return nil + } + + for _, issuerKey := range issuerKeys { + siteKeys, err := storage.List(issuerKey, false) + if err != nil { + log.Printf("[ERROR] Listing contents of %s: %v", issuerKey, err) + continue + } + + for _, siteKey := range siteKeys { + siteAssets, err := storage.List(siteKey, false) + if err != nil { + log.Printf("[ERROR] Listing contents of %s: %v", siteKey, err) + continue + } + + for _, assetKey := range siteAssets { + if path.Ext(assetKey) != ".crt" { + continue + } + + certFile, err := storage.Load(assetKey) + if err != nil { + return fmt.Errorf("loading certificate file %s: %v", assetKey, err) + } + block, _ := pem.Decode(certFile) + if block == nil || block.Type != "CERTIFICATE" { + return fmt.Errorf("certificate file %s does not contain PEM-encoded certificate", assetKey) + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return fmt.Errorf("certificate file %s is malformed; error parsing PEM: %v", assetKey, err) + } + + if expiredTime := time.Since(cert.NotAfter); expiredTime >= gracePeriod { + log.Printf("[INFO] Certificate %s expired %s ago; cleaning up", assetKey, expiredTime) + baseName := strings.TrimSuffix(assetKey, ".crt") + for _, relatedAsset := range []string{ + assetKey, + baseName + ".key", + baseName + ".json", + } { + log.Printf("[INFO] Deleting %s because resource expired", relatedAsset) + err := storage.Delete(relatedAsset) + if err != nil { + log.Printf("[ERROR] Cleaning up asset related to expired certificate for %s: %s: %v", + baseName, relatedAsset, err) + } + } + } + } + + // update listing; if folder is empty, delete it + siteAssets, err = storage.List(siteKey, false) + if err != nil { + continue + } + if len(siteAssets) == 0 { + log.Printf("[INFO] Deleting %s because key is empty", siteKey) + err := storage.Delete(siteKey) + if err != nil { + return fmt.Errorf("deleting empty site folder %s: %v", siteKey, err) + } + } + } + } + return nil +} + const ( - // DefaultRenewCheckInterval is how often to check certificates for renewal. - DefaultRenewCheckInterval = 12 * time.Hour + // DefaultRenewCheckInterval is how often to check certificates for expiration. + // Scans are very lightweight, so this can be semi-frequent. This default should + // be smaller than *DefaultRenewalWindowRatio/3, which + // gives certificates plenty of chance to be renewed on time. + DefaultRenewCheckInterval = 10 * time.Minute - // DefaultRenewDurationBefore is how long before expiration to renew certificates. - DefaultRenewDurationBefore = (24 * time.Hour) * 30 - - // DefaultRenewDurationBeforeAtStartup is how long before expiration to require - // a renewed certificate when the process is first starting up (see mholt/caddy#1680). - DefaultRenewDurationBeforeAtStartup = (24 * time.Hour) * 7 + // DefaultRenewalWindowRatio is how much of a certificate's lifetime becomes the + // renewal window. The renewal window is the span of time at the end of the + // certificate's validity period in which it should be renewed. A default value + // of ~1/3 is pretty safe and recommended for most certificates. + DefaultRenewalWindowRatio = 1.0 / 3.0 // DefaultOCSPCheckInterval is how often to check if OCSP stapling needs updating. DefaultOCSPCheckInterval = 1 * time.Hour diff --git a/vendor/github.com/mholt/certmagic/ocsp.go b/vendor/github.com/caddyserver/certmagic/ocsp.go similarity index 92% rename from vendor/github.com/mholt/certmagic/ocsp.go rename to vendor/github.com/caddyserver/certmagic/ocsp.go index 823b7c86..4163302d 100644 --- a/vendor/github.com/mholt/certmagic/ocsp.go +++ b/vendor/github.com/caddyserver/certmagic/ocsp.go @@ -35,7 +35,10 @@ import ( // // Errors here are not necessarily fatal, it could just be that the // certificate doesn't have an issuer URL. -func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) error { +// +// If a status was received, it returns that status. Note that the +// returned status is not always stapled to the certificate. +func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Response, error) { if pemBundle == nil { // we need a PEM encoding only for some function calls below bundle := new(bytes.Buffer) @@ -85,7 +88,7 @@ func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) error { // not contain a link to an OCSP server. But we should log it anyway. // There's nothing else we can do to get OCSP for this certificate, // so we can return here with the error. - return fmt.Errorf("no OCSP stapling for %v: %v", cert.Names, ocspErr) + return nil, fmt.Errorf("no OCSP stapling for %v: %v", cert.Names, ocspErr) } gotNewOCSP = true } @@ -94,24 +97,24 @@ func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) error { // the certificate. If the OCSP response was not loaded from // storage, we persist it for next time. if ocspResp.Status == ocsp.Good { - if ocspResp.NextUpdate.After(cert.NotAfter) { + if ocspResp.NextUpdate.After(cert.Leaf.NotAfter) { // uh oh, this OCSP response expires AFTER the certificate does, that's kinda bogus. // it was the reason a lot of Symantec-validated sites (not Caddy) went down // in October 2017. https://twitter.com/mattiasgeniar/status/919432824708648961 - return fmt.Errorf("invalid: OCSP response for %v valid after certificate expiration (%s)", - cert.Names, cert.NotAfter.Sub(ocspResp.NextUpdate)) + return ocspResp, fmt.Errorf("invalid: OCSP response for %v valid after certificate expiration (%s)", + cert.Names, cert.Leaf.NotAfter.Sub(ocspResp.NextUpdate)) } cert.Certificate.OCSPStaple = ocspBytes - cert.OCSP = ocspResp + cert.ocsp = ocspResp if gotNewOCSP { err := storage.Store(ocspStapleKey, ocspBytes) if err != nil { - return fmt.Errorf("unable to write OCSP staple file for %v: %v", cert.Names, err) + return ocspResp, fmt.Errorf("unable to write OCSP staple file for %v: %v", cert.Names, err) } } } - return nil + return ocspResp, nil } // getOCSPForCert takes a PEM encoded cert or cert bundle returning the raw OCSP response, diff --git a/vendor/github.com/caddyserver/certmagic/ratelimiter.go b/vendor/github.com/caddyserver/certmagic/ratelimiter.go new file mode 100644 index 00000000..04680458 --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/ratelimiter.go @@ -0,0 +1,219 @@ +package certmagic + +import ( + "context" + "sync" + "time" +) + +// NewRateLimiter returns a rate limiter that allows up to maxEvents +// in a sliding window of size window. If maxEvents and window are +// both 0, or if maxEvents is non-zero and window is 0, rate limiting +// is disabled. This function panics if maxEvents is less than 0 or +// if maxEvents is 0 and window is non-zero, which is considered to be +// an invalid configuration, as it would never allow events. +func NewRateLimiter(maxEvents int, window time.Duration) *RingBufferRateLimiter { + if maxEvents < 0 { + panic("maxEvents cannot be less than zero") + } + if maxEvents == 0 && window != 0 { + panic("invalid configuration: maxEvents = 0 and window != 0 would not allow any events") + } + rbrl := &RingBufferRateLimiter{ + window: window, + ring: make([]time.Time, maxEvents), + started: make(chan struct{}), + stopped: make(chan struct{}), + ticket: make(chan struct{}), + } + go rbrl.loop() + <-rbrl.started // make sure loop is ready to receive before we return + return rbrl +} + +// RingBufferRateLimiter uses a ring to enforce rate limits +// consisting of a maximum number of events within a single +// sliding window of a given duration. An empty value is +// not valid; use NewRateLimiter to get one. +type RingBufferRateLimiter struct { + window time.Duration + ring []time.Time // maxEvents == len(ring) + cursor int // always points to the oldest timestamp + mu sync.Mutex // protects ring, cursor, and window + started chan struct{} + stopped chan struct{} + ticket chan struct{} +} + +// Stop cleans up r's scheduling goroutine. +func (r *RingBufferRateLimiter) Stop() { + close(r.stopped) +} + +func (r *RingBufferRateLimiter) loop() { + for { + // if we've been stopped, return + select { + case <-r.stopped: + return + default: + } + + if len(r.ring) == 0 { + if r.window == 0 { + // rate limiting is disabled; always allow immediately + r.permit() + continue + } + panic("invalid configuration: maxEvents = 0 and window != 0 does not allow any events") + } + + // wait until next slot is available or until we've been stopped + r.mu.Lock() + then := r.ring[r.cursor].Add(r.window) + r.mu.Unlock() + waitDuration := time.Until(then) + waitTimer := time.NewTimer(waitDuration) + select { + case <-waitTimer.C: + r.permit() + case <-r.stopped: + waitTimer.Stop() + return + } + } +} + +// Allow returns true if the event is allowed to +// happen right now. It does not wait. If the event +// is allowed, a ticket is claimed. +func (r *RingBufferRateLimiter) Allow() bool { + select { + case <-r.ticket: + return true + default: + return false + } +} + +// Wait blocks until the event is allowed to occur. It returns an +// error if the context is cancelled. +func (r *RingBufferRateLimiter) Wait(ctx context.Context) error { + select { + case <-ctx.Done(): + return context.Canceled + case <-r.ticket: + return nil + } +} + +// MaxEvents returns the maximum number of events that +// are allowed within the sliding window. +func (r *RingBufferRateLimiter) MaxEvents() int { + r.mu.Lock() + defer r.mu.Unlock() + return len(r.ring) +} + +// SetMaxEvents changes the maximum number of events that are +// allowed in the sliding window. If the new limit is lower, +// the oldest events will be forgotten. If the new limit is +// higher, the window will suddenly have capacity for new +// reservations. It panics if maxEvents is 0 and window size +// is not zero. +func (r *RingBufferRateLimiter) SetMaxEvents(maxEvents int) { + newRing := make([]time.Time, maxEvents) + r.mu.Lock() + defer r.mu.Unlock() + + if r.window != 0 && maxEvents == 0 { + panic("invalid configuration: maxEvents = 0 and window != 0 would not allow any events") + } + + // only make the change if the new limit is different + if maxEvents == len(r.ring) { + return + } + + // the new ring may be smaller; fast-forward to the + // oldest timestamp that will be kept in the new + // ring so the oldest ones are forgotten and the + // newest ones will be remembered + sizeDiff := len(r.ring) - maxEvents + for i := 0; i < sizeDiff; i++ { + r.advance() + } + + if len(r.ring) > 0 { + // copy timestamps into the new ring until we + // have either copied all of them or have reached + // the capacity of the new ring + startCursor := r.cursor + for i := 0; i < len(newRing); i++ { + newRing[i] = r.ring[r.cursor] + r.advance() + if r.cursor == startCursor { + // new ring is larger than old one; + // "we've come full circle" + break + } + } + } + + r.ring = newRing + r.cursor = 0 +} + +// Window returns the size of the sliding window. +func (r *RingBufferRateLimiter) Window() time.Duration { + r.mu.Lock() + defer r.mu.Unlock() + return r.window +} + +// SetWindow changes r's sliding window duration to window. +// Goroutines that are already blocked on a call to Wait() +// will not be affected. It panics if window is non-zero +// but the max event limit is 0. +func (r *RingBufferRateLimiter) SetWindow(window time.Duration) { + r.mu.Lock() + defer r.mu.Unlock() + if window != 0 && len(r.ring) == 0 { + panic("invalid configuration: maxEvents = 0 and window != 0 would not allow any events") + } + r.window = window +} + +// permit allows one event through the throttle. This method +// blocks until a goroutine is waiting for a ticket or until +// the rate limiter is stopped. +func (r *RingBufferRateLimiter) permit() { + for { + select { + case r.started <- struct{}{}: + // notify parent goroutine that we've started; should + // only happen once, before constructor returns + continue + case <-r.stopped: + return + case r.ticket <- struct{}{}: + r.mu.Lock() + defer r.mu.Unlock() + if len(r.ring) > 0 { + r.ring[r.cursor] = time.Now() + r.advance() + } + return + } + } +} + +// advance moves the cursor to the next position. +// It is NOT safe for concurrent use, so it must +// be called inside a lock on r.mu. +func (r *RingBufferRateLimiter) advance() { + r.cursor++ + if r.cursor >= len(r.ring) { + r.cursor = 0 + } +} diff --git a/vendor/github.com/caddyserver/certmagic/solvers.go b/vendor/github.com/caddyserver/certmagic/solvers.go new file mode 100644 index 00000000..50dad4dc --- /dev/null +++ b/vendor/github.com/caddyserver/certmagic/solvers.go @@ -0,0 +1,412 @@ +// Copyright 2015 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package certmagic + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "log" + "net" + "net/http" + "path" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/challenge/tlsalpn01" +) + +// httpSolver solves the HTTP challenge. It must be +// associated with a config and an address to use +// for solving the challenge. If multiple httpSolvers +// are initialized concurrently, the first one to +// begin will start the server, and the last one to +// finish will stop the server. This solver must be +// wrapped by a distributedSolver to work properly, +// because the only way the HTTP challenge handler +// can access the keyAuth material is by loading it +// from storage, which is done by distributedSolver. +type httpSolver struct { + closed int32 // accessed atomically + acmeManager *ACMEManager + address string +} + +// Present starts an HTTP server if none is already listening on s.address. +func (s *httpSolver) Present(domain, token, keyAuth string) error { + solversMu.Lock() + defer solversMu.Unlock() + + si := getSolverInfo(s.address) + si.count++ + if si.listener != nil { + return nil // already be served by us + } + + // notice the unusual error handling here; we + // only continue to start a challenge server if + // we got a listener; in all other cases return + ln, err := robustTryListen(s.address) + if ln == nil { + return err + } + + // successfully bound socket, so save listener and start key auth HTTP server + si.listener = ln + go s.serve(si) + + return nil +} + +// serve is an HTTP server that serves only HTTP challenge responses. +func (s *httpSolver) serve(si *solverInfo) { + defer close(si.done) + httpServer := &http.Server{Handler: s.acmeManager.HTTPChallengeHandler(http.NewServeMux())} + httpServer.SetKeepAlivesEnabled(false) + err := httpServer.Serve(si.listener) + if err != nil && atomic.LoadInt32(&s.closed) != 1 { + log.Printf("[ERROR] key auth HTTP server: %v", err) + } +} + +// CleanUp cleans up the HTTP server if it is the last one to finish. +func (s *httpSolver) CleanUp(domain, token, keyAuth string) error { + solversMu.Lock() + defer solversMu.Unlock() + si := getSolverInfo(s.address) + si.count-- + if si.count == 0 { + // last one out turns off the lights + atomic.StoreInt32(&s.closed, 1) + if si.listener != nil { + si.listener.Close() + <-si.done + } + delete(solvers, s.address) + } + return nil +} + +// tlsALPNSolver is a type that can solve TLS-ALPN challenges. +// It must have an associated config and address on which to +// serve the challenge. +type tlsALPNSolver struct { + config *Config + address string +} + +// Present adds the certificate to the certificate cache and, if +// needed, starts a TLS server for answering TLS-ALPN challenges. +func (s *tlsALPNSolver) Present(domain, token, keyAuth string) error { + // load the certificate into the cache; this isn't strictly necessary + // if we're using the distributed solver since our GetCertificate + // function will check storage for the keyAuth anyway, but it seems + // like loading it into the cache is the right thing to do + cert, err := tlsalpn01.ChallengeCert(domain, keyAuth) + if err != nil { + return err + } + certHash := hashCertificateChain(cert.Certificate) + s.config.certCache.mu.Lock() + s.config.certCache.cache[tlsALPNCertKeyName(domain)] = Certificate{ + Certificate: *cert, + Names: []string{domain}, + hash: certHash, // perhaps not necesssary + } + s.config.certCache.mu.Unlock() + + // the rest of this function increments the + // challenge count for the solver at this + // listener address, and if necessary, starts + // a simple TLS server + + solversMu.Lock() + defer solversMu.Unlock() + + si := getSolverInfo(s.address) + si.count++ + if si.listener != nil { + return nil // already be served by us + } + + // notice the unusual error handling here; we + // only continue to start a challenge server if + // we got a listener; in all other cases return + ln, err := robustTryListen(s.address) + if ln == nil { + return err + } + + // we were able to bind the socket, so make it into a TLS + // listener, store it with the solverInfo, and start the + // challenge server + + si.listener = tls.NewListener(ln, s.config.TLSConfig()) + + go func() { + defer close(si.done) + for { + conn, err := si.listener.Accept() + if err != nil { + if atomic.LoadInt32(&si.closed) == 1 { + return + } + log.Printf("[ERROR] TLS-ALPN challenge server: accept: %v", err) + continue + } + go s.handleConn(conn) + } + }() + + return nil +} + +// handleConn completes the TLS handshake and then closes conn. +func (*tlsALPNSolver) handleConn(conn net.Conn) { + defer conn.Close() + tlsConn, ok := conn.(*tls.Conn) + if !ok { + log.Printf("[ERROR] TLS-ALPN challenge server: expected tls.Conn but got %T: %#v", conn, conn) + return + } + err := tlsConn.Handshake() + if err != nil { + log.Printf("[ERROR] TLS-ALPN challenge server: handshake: %v", err) + return + } +} + +// CleanUp removes the challenge certificate from the cache, and if +// it is the last one to finish, stops the TLS server. +func (s *tlsALPNSolver) CleanUp(domain, token, keyAuth string) error { + s.config.certCache.mu.Lock() + delete(s.config.certCache.cache, tlsALPNCertKeyName(domain)) + s.config.certCache.mu.Unlock() + + solversMu.Lock() + defer solversMu.Unlock() + si := getSolverInfo(s.address) + si.count-- + if si.count == 0 { + // last one out turns off the lights + atomic.StoreInt32(&si.closed, 1) + if si.listener != nil { + si.listener.Close() + <-si.done + } + delete(solvers, s.address) + } + + return nil +} + +// tlsALPNCertKeyName returns the key to use when caching a cert +// for use with the TLS-ALPN ACME challenge. It is simply to help +// avoid conflicts (although at time of writing, there shouldn't +// be, since the cert cache is keyed by hash of certificate chain). +func tlsALPNCertKeyName(sniName string) string { + return sniName + ":acme-tls-alpn" +} + +// distributedSolver allows the ACME HTTP-01 and TLS-ALPN challenges +// to be solved by an instance other than the one which initiated it. +// This is useful behind load balancers or in other cluster/fleet +// configurations. The only requirement is that the instance which +// initiates the challenge shares the same storage and locker with +// the others in the cluster. The storage backing the certificate +// cache in distributedSolver.config is crucial. +// +// Obviously, the instance which completes the challenge must be +// serving on the HTTPChallengePort for the HTTP-01 challenge or the +// TLSALPNChallengePort for the TLS-ALPN-01 challenge (or have all +// the packets port-forwarded) to receive and handle the request. The +// server which receives the challenge must handle it by checking to +// see if the challenge token exists in storage, and if so, decode it +// and use it to serve up the correct response. HTTPChallengeHandler +// in this package as well as the GetCertificate method implemented +// by a Config support and even require this behavior. +// +// In short: the only two requirements for cluster operation are +// sharing sync and storage, and using the facilities provided by +// this package for solving the challenges. +type distributedSolver struct { + // The config with a certificate cache + // with a reference to the storage to + // use which is shared among all the + // instances in the cluster - REQUIRED. + acmeManager *ACMEManager + + // Since the distributedSolver is only a + // wrapper over an actual solver, place + // the actual solver here. + providerServer challenge.Provider + + // The CA endpoint URL associated with + // this solver. + caURL string +} + +// Present invokes the underlying solver's Present method +// and also stores domain, token, and keyAuth to the storage +// backing the certificate cache of dhs.acmeManager. +func (dhs distributedSolver) Present(domain, token, keyAuth string) error { + infoBytes, err := json.Marshal(challengeInfo{ + Domain: domain, + Token: token, + KeyAuth: keyAuth, + }) + if err != nil { + return err + } + + err = dhs.acmeManager.config.Storage.Store(dhs.challengeTokensKey(domain), infoBytes) + if err != nil { + return err + } + + err = dhs.providerServer.Present(domain, token, keyAuth) + if err != nil { + return fmt.Errorf("presenting with embedded provider: %v", err) + } + return nil +} + +// CleanUp invokes the underlying solver's CleanUp method +// and also cleans up any assets saved to storage. +func (dhs distributedSolver) CleanUp(domain, token, keyAuth string) error { + err := dhs.acmeManager.config.Storage.Delete(dhs.challengeTokensKey(domain)) + if err != nil { + return err + } + err = dhs.providerServer.CleanUp(domain, token, keyAuth) + if err != nil { + return fmt.Errorf("cleaning up embedded provider: %v", err) + } + return nil +} + +// challengeTokensPrefix returns the key prefix for challenge info. +func (dhs distributedSolver) challengeTokensPrefix() string { + return path.Join(dhs.acmeManager.storageKeyCAPrefix(dhs.caURL), "challenge_tokens") +} + +// challengeTokensKey returns the key to use to store and access +// challenge info for domain. +func (dhs distributedSolver) challengeTokensKey(domain string) string { + return path.Join(dhs.challengeTokensPrefix(), StorageKeys.Safe(domain)+".json") +} + +type challengeInfo struct { + Domain, Token, KeyAuth string +} + +// solverInfo associates a listener with the +// number of challenges currently using it. +type solverInfo struct { + closed int32 // accessed atomically + count int + listener net.Listener + done chan struct{} // used to signal when our own solver server is done +} + +// getSolverInfo gets a valid solverInfo struct for address. +func getSolverInfo(address string) *solverInfo { + si, ok := solvers[address] + if !ok { + si = &solverInfo{done: make(chan struct{})} + solvers[address] = si + } + return si +} + +// robustTryListen calls net.Listen for a TCP socket at addr. +// This function may return both a nil listener and a nil error! +// If it was able to bind the socket, it returns the listener +// and no error. If it wasn't able to bind the socket because +// the socket is already in use, then it returns a nil listener +// and nil error. If it had any other error, it returns the +// error. The intended error handling logic for this function +// is to proceed if the returned listener is not nil; otherwise +// return err (which may also be nil). In other words, this +// function ignores errors if the socket is already in use, +// which is useful for our challenge servers, where we assume +// that whatever is already listening can solve the challenges. +func robustTryListen(addr string) (net.Listener, error) { + var listenErr error + for i := 0; i < 2; i++ { + // doesn't hurt to sleep briefly before the second + // attempt in case the OS has timing issues + if i > 0 { + time.Sleep(100 * time.Millisecond) + } + + // if we can bind the socket right away, great! + var ln net.Listener + ln, listenErr = net.Listen("tcp", addr) + if listenErr == nil { + return ln, nil + } + + // if it failed just because the socket is already in use, we + // have no choice but to assume that whatever is using the socket + // can answer the challenge already, so we ignore the error + connectErr := dialTCPSocket(addr) + if connectErr == nil { + return nil, nil + } + + // hmm, we couldn't connect to the socket, so something else must + // be wrong, right? wrong!! we've had reports across multiple OSes + // now that sometimes connections fail even though the OS told us + // that the address was already in use; either the listener is + // fluctuating between open and closed very, very quickly, or the + // OS is inconsistent and contradicting itself; I have been unable + // to reproduce this, so I'm now resorting to hard-coding substring + // matching in error messages as a really hacky and unreliable + // safeguard against this, until we can idenify exactly what was + // happening; see the following threads for more info: + // https://caddy.community/t/caddy-retry-error/7317 + // https://caddy.community/t/v2-upgrade-to-caddy2-failing-with-errors/7423 + if strings.Contains(listenErr.Error(), "address already in use") || + strings.Contains(listenErr.Error(), "one usage of each socket address") { + log.Printf("[WARNING] OS reports a contradiction: %v - but we cannot connect to it, with this error: %v; continuing anyway 🤞 (I don't know what causes this... if you do, please help?)", listenErr, connectErr) + return nil, nil + } + } + return nil, fmt.Errorf("could not start listener for challenge server at %s: %v", addr, listenErr) +} + +// dialTCPSocket connects to a TCP address just for the sake of +// seeing if it is open. It returns a nil error if a TCP connection +// can successfully be made to addr within a short timeout. +func dialTCPSocket(addr string) error { + conn, err := net.DialTimeout("tcp", addr, 250*time.Millisecond) + if err == nil { + conn.Close() + } + return err +} + +// The active challenge solvers, keyed by listener address, +// and protected by a mutex. Note that the creation of +// solver listeners and the incrementing of their counts +// are atomic operations guarded by this mutex. +var ( + solvers = make(map[string]*solverInfo) + solversMu sync.Mutex +) diff --git a/vendor/github.com/mholt/certmagic/storage.go b/vendor/github.com/caddyserver/certmagic/storage.go similarity index 64% rename from vendor/github.com/mholt/certmagic/storage.go rename to vendor/github.com/caddyserver/certmagic/storage.go index 927ffef8..4b431bc6 100644 --- a/vendor/github.com/mholt/certmagic/storage.go +++ b/vendor/github.com/caddyserver/certmagic/storage.go @@ -15,10 +15,11 @@ package certmagic import ( - "net/url" + "log" "path" "regexp" "strings" + "sync" "time" ) @@ -31,6 +32,9 @@ import ( // in order to share certificates and other TLS resources // with the cluster. // +// The Load, Delete, List, and Stat methods should return +// ErrNotExist if the key does not exist. +// // Implementations of Storage must be safe for concurrent use. type Storage interface { // Locker provides atomic synchronization @@ -43,7 +47,9 @@ type Storage interface { // Load retrieves the value at key. Load(key string) ([]byte, error) - // Delete deletes key. + // Delete deletes key. An error should be + // returned only if the key still exists + // when the method returns. Delete(key string) error // Exists returns true if the key exists @@ -80,7 +86,7 @@ type Locker interface { // To prevent deadlocks, all implementations (where this concern // is relevant) should put a reasonable expiration on the lock in // case Unlock is unable to be called due to some sort of network - // or system failure or crash. + // failure or system crash. Lock(key string) error // Unlock releases the lock for key. This method must ONLY be @@ -91,6 +97,12 @@ type Locker interface { } // KeyInfo holds information about a key in storage. +// Key and IsTerminal are required; Modified and Size +// are optional if the storage implementation is not +// able to get that information. Setting them will +// make certain operations more consistent or +// predictable, but it is not crucial to basic +// functionality. type KeyInfo struct { Key string Modified time.Time @@ -123,62 +135,39 @@ type keyValue struct { // in a Storage implementation. type KeyBuilder struct{} -// CAPrefix returns the storage key prefix for -// the given certificate authority URL. -func (keys KeyBuilder) CAPrefix(ca string) string { - caURL, err := url.Parse(ca) - if err != nil { - caURL = &url.URL{Host: ca} - } - return path.Join(prefixACME, keys.Safe(caURL.Host)) +// CertsPrefix returns the storage key prefix for +// the given certificate issuer. +func (keys KeyBuilder) CertsPrefix(issuerKey string) string { + return path.Join(prefixCerts, keys.Safe(issuerKey)) } -// SitePrefix returns a key prefix for items associated with -// the site using the given CA URL. -func (keys KeyBuilder) SitePrefix(ca, domain string) string { - return path.Join(keys.CAPrefix(ca), "sites", keys.Safe(domain)) +// CertsSitePrefix returns a key prefix for items associated with +// the site given by domain using the given issuer key. +func (keys KeyBuilder) CertsSitePrefix(issuerKey, domain string) string { + return path.Join(keys.CertsPrefix(issuerKey), keys.Safe(domain)) } -// SiteCert returns the path to the certificate file for domain. -func (keys KeyBuilder) SiteCert(ca, domain string) string { - return path.Join(keys.SitePrefix(ca, domain), keys.Safe(domain)+".crt") +// SiteCert returns the path to the certificate file for domain +// that is associated with the issuer with the given issuerKey. +func (keys KeyBuilder) SiteCert(issuerKey, domain string) string { + safeDomain := keys.Safe(domain) + return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".crt") } -// SitePrivateKey returns the path to domain's private key file. -func (keys KeyBuilder) SitePrivateKey(ca, domain string) string { - return path.Join(keys.SitePrefix(ca, domain), keys.Safe(domain)+".key") +// SitePrivateKey returns the path to the private key file for domain +// that is associated with the certificate from the given issuer with +// the given issuerKey. +func (keys KeyBuilder) SitePrivateKey(issuerKey, domain string) string { + safeDomain := keys.Safe(domain) + return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".key") } -// SiteMeta returns the path to the domain's asset metadata file. -func (keys KeyBuilder) SiteMeta(ca, domain string) string { - return path.Join(keys.SitePrefix(ca, domain), keys.Safe(domain)+".json") -} - -// UsersPrefix returns a key prefix for items related to -// users associated with the given CA URL. -func (keys KeyBuilder) UsersPrefix(ca string) string { - return path.Join(keys.CAPrefix(ca), "users") -} - -// UserPrefix returns a key prefix for items related to -// the user with the given email for the given CA URL. -func (keys KeyBuilder) UserPrefix(ca, email string) string { - if email == "" { - email = emptyEmail - } - return path.Join(keys.UsersPrefix(ca), keys.Safe(email)) -} - -// UserReg gets the path to the registration file for the user -// with the given email address for the given CA URL. -func (keys KeyBuilder) UserReg(ca, email string) string { - return keys.safeUserKey(ca, email, "registration", ".json") -} - -// UserPrivateKey gets the path to the private key file for the -// user with the given email address on the given CA URL. -func (keys KeyBuilder) UserPrivateKey(ca, email string) string { - return keys.safeUserKey(ca, email, "private", ".key") +// SiteMeta returns the path to the metadata file for domain that +// is associated with the certificate from the given issuer with +// the given issuerKey. +func (keys KeyBuilder) SiteMeta(issuerKey, domain string) string { + safeDomain := keys.Safe(domain) + return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".json") } // OCSPStaple returns a key for the OCSP staple associated @@ -194,35 +183,9 @@ func (keys KeyBuilder) OCSPStaple(cert *Certificate, pemBundle []byte) string { return path.Join(prefixOCSP, ocspFileName) } -// safeUserKey returns a key for the given email, with the default -// filename, and the filename ending in the given extension. -func (keys KeyBuilder) safeUserKey(ca, email, defaultFilename, extension string) string { - if email == "" { - email = emptyEmail - } - email = strings.ToLower(email) - filename := keys.emailUsername(email) - if filename == "" { - filename = defaultFilename - } - filename = keys.Safe(filename) - return path.Join(keys.UserPrefix(ca, email), filename+extension) -} - -// emailUsername returns the username portion of an email address (part before -// '@') or the original input if it can't find the "@" symbol. -func (keys KeyBuilder) emailUsername(email string) string { - at := strings.Index(email, "@") - if at == -1 { - return email - } else if at == 0 { - return email[1:] - } - return email[:at] -} - // Safe standardizes and sanitizes str for use as -// a storage key. This method is idempotent. +// a single component of a storage key. This method +// is idempotent. func (keys KeyBuilder) Safe(str string) string { str = strings.ToLower(str) str = strings.TrimSpace(str) @@ -232,6 +195,7 @@ func (keys KeyBuilder) Safe(str string) string { " ", "_", "+", "_plus_", "*", "wildcard_", + ":", "-", "..", "", // prevent directory traversal (regex allows single dots) ) str = repl.Replace(str) @@ -240,6 +204,50 @@ func (keys KeyBuilder) Safe(str string) string { return safeKeyRE.ReplaceAllLiteralString(str, "") } +// CleanUpOwnLocks immediately cleans up all +// current locks obtained by this process. Since +// this does not cancel the operations that +// the locks are synchronizing, this should be +// called only immediately before process exit. +func CleanUpOwnLocks() { + locksMu.Lock() + defer locksMu.Unlock() + for lockKey, storage := range locks { + err := storage.Unlock(lockKey) + if err == nil { + delete(locks, lockKey) + } else { + log.Printf("[ERROR] Unable to clean up lock: %v (lock=%s storage=%s)", + err, lockKey, storage) + } + } +} + +func obtainLock(storage Storage, lockKey string) error { + err := storage.Lock(lockKey) + if err == nil { + locksMu.Lock() + locks[lockKey] = storage + locksMu.Unlock() + } + return err +} + +func releaseLock(storage Storage, lockKey string) error { + err := storage.Unlock(lockKey) + if err == nil { + locksMu.Lock() + delete(locks, lockKey) + locksMu.Unlock() + } + return err +} + +// locks stores a reference to all the current +// locks obtained by this process. +var locks = make(map[string]Storage) +var locksMu sync.Mutex + // StorageKeys provides methods for accessing // keys and key prefixes for items in a Storage. // Typically, you will not need to use this @@ -249,8 +257,8 @@ func (keys KeyBuilder) Safe(str string) string { var StorageKeys KeyBuilder const ( - prefixACME = "acme" - prefixOCSP = "ocsp" + prefixCerts = "certificates" + prefixOCSP = "ocsp" ) // safeKeyRE matches any undesirable characters in storage keys. diff --git a/vendor/github.com/mholt/certmagic/user.go b/vendor/github.com/caddyserver/certmagic/user.go similarity index 60% rename from vendor/github.com/mholt/certmagic/user.go rename to vendor/github.com/caddyserver/certmagic/user.go index 2b541244..c1057123 100644 --- a/vendor/github.com/mholt/certmagic/user.go +++ b/vendor/github.com/caddyserver/certmagic/user.go @@ -29,8 +29,8 @@ import ( "sort" "strings" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/registration" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/registration" ) // user represents a Let's Encrypt user account. @@ -60,8 +60,8 @@ func (u user) GetPrivateKey() crypto.PrivateKey { // user to disk or register it via ACME. If you want to use // a user account that might already exist, call getUser // instead. It does NOT prompt the user. -func (cfg *Config) newUser(email string) (user, error) { - user := user{Email: email} +func (*ACMEManager) newUser(email string) (*user, error) { + user := &user{Email: email} privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) if err != nil { return user, fmt.Errorf("generating private key: %v", err) @@ -77,47 +77,47 @@ func (cfg *Config) newUser(email string) (user, error) { // the consequences of an empty email.) This function MAY prompt // the user for input. If allowPrompts is false, the user // will NOT be prompted and an empty email may be returned. -func (cfg *Config) getEmail(allowPrompts bool) error { - leEmail := cfg.Email +func (am *ACMEManager) getEmail(allowPrompts bool) error { + leEmail := am.Email // First try package default email if leEmail == "" { - leEmail = Default.Email + leEmail = DefaultACME.Email // TODO: racey with line 108 } // Then try to get most recent user email from storage var gotRecentEmail bool if leEmail == "" { - leEmail, gotRecentEmail = cfg.mostRecentUserEmail() + leEmail, gotRecentEmail = am.mostRecentUserEmail(am.CA) } if !gotRecentEmail && leEmail == "" && allowPrompts { // Looks like there is no email address readily available, // so we will have to ask the user if we can. var err error - leEmail, err = cfg.promptUserForEmail() + leEmail, err = am.promptUserForEmail() if err != nil { return err } // User might have just signified their agreement - cfg.Agreed = Default.Agreed + am.Agreed = DefaultACME.Agreed } // save the email for later and ensure it is consistent // for repeated use; then update cfg with the email - Default.Email = strings.TrimSpace(strings.ToLower(leEmail)) - cfg.Email = Default.Email + DefaultACME.Email = strings.TrimSpace(strings.ToLower(leEmail)) // TODO: this is racey with line 85 + am.Email = DefaultACME.Email return nil } -func (cfg *Config) getAgreementURL() (string, error) { +func (am *ACMEManager) getAgreementURL() (string, error) { if agreementTestURL != "" { return agreementTestURL, nil } - caURL := Default.CA - if cfg.CA != "" { - caURL = cfg.CA + caURL := am.CA + if caURL == "" { + caURL = DefaultACME.CA } response, err := http.Get(caURL) if err != nil { @@ -137,14 +137,14 @@ func (cfg *Config) getAgreementURL() (string, error) { // be the empty string). If no error is returned, then Agreed // will also be set to true, since continuing through the // prompt signifies agreement. -func (cfg *Config) promptUserForEmail() (string, error) { - agreementURL, err := cfg.getAgreementURL() +func (am *ACMEManager) promptUserForEmail() (string, error) { + agreementURL, err := am.getAgreementURL() if err != nil { return "", fmt.Errorf("get Agreement URL: %v", err) } // prompt the user for an email address and terms agreement reader := bufio.NewReader(stdin) - cfg.promptUserAgreement(agreementURL) + am.promptUserAgreement(agreementURL) fmt.Println("Please enter your email address to signify agreement and to be notified") fmt.Println("in case of issues. You can leave it blank, but we don't recommend it.") fmt.Print(" Email address: ") @@ -153,7 +153,7 @@ func (cfg *Config) promptUserForEmail() (string, error) { return "", fmt.Errorf("reading email address: %v", err) } leEmail = strings.TrimSpace(leEmail) - Default.Agreed = true + DefaultACME.Agreed = true return leEmail, nil } @@ -162,32 +162,31 @@ func (cfg *Config) promptUserForEmail() (string, error) { // it will create a new one, but it does NOT save new // users to the disk or register them via ACME. It does // NOT prompt the user. -func (cfg *Config) getUser(email string) (user, error) { - var user user - - regBytes, err := cfg.Storage.Load(StorageKeys.UserReg(cfg.CA, email)) +func (am *ACMEManager) getUser(ca, email string) (*user, error) { + regBytes, err := am.config.Storage.Load(am.storageKeyUserReg(ca, email)) if err != nil { if _, ok := err.(ErrNotExist); ok { // create a new user - return cfg.newUser(email) + return am.newUser(email) } - return user, err + return nil, err } - keyBytes, err := cfg.Storage.Load(StorageKeys.UserPrivateKey(cfg.CA, email)) + keyBytes, err := am.config.Storage.Load(am.storageKeyUserPrivateKey(ca, email)) if err != nil { if _, ok := err.(ErrNotExist); ok { // create a new user - return cfg.newUser(email) + return am.newUser(email) } - return user, err + return nil, err } - err = json.Unmarshal(regBytes, &user) + var u *user + err = json.Unmarshal(regBytes, &u) if err != nil { - return user, err + return u, err } - user.key, err = decodePrivateKey(keyBytes) - return user, err + u.key, err = decodePrivateKey(keyBytes) + return u, err } // saveUser persists a user's key and account registration @@ -195,7 +194,7 @@ func (cfg *Config) getUser(email string) (user, error) { // or prompt the user. You must also pass in the storage // wherein the user should be saved. It should be the storage // for the CA with which user has an account. -func (cfg *Config) saveUser(user user) error { +func (am *ACMEManager) saveUser(ca string, user *user) error { regBytes, err := json.MarshalIndent(&user, "", "\t") if err != nil { return err @@ -204,25 +203,23 @@ func (cfg *Config) saveUser(user user) error { if err != nil { return err } - all := []keyValue{ { - key: StorageKeys.UserReg(cfg.CA, user.Email), + key: am.storageKeyUserReg(ca, user.Email), value: regBytes, }, { - key: StorageKeys.UserPrivateKey(cfg.CA, user.Email), + key: am.storageKeyUserPrivateKey(ca, user.Email), value: keyBytes, }, } - - return storeTx(cfg.Storage, all) + return storeTx(am.config.Storage, all) } // promptUserAgreement simply outputs the standard user // agreement prompt with the given agreement URL. // It outputs a newline after the message. -func (cfg *Config) promptUserAgreement(agreementURL string) { +func (am *ACMEManager) promptUserAgreement(agreementURL string) { const userAgreementPrompt = `Your sites will be served over HTTPS automatically using Let's Encrypt. By continuing, you agree to the Let's Encrypt Subscriber Agreement at:` fmt.Printf("\n\n%s\n %s\n", userAgreementPrompt, agreementURL) @@ -231,8 +228,8 @@ By continuing, you agree to the Let's Encrypt Subscriber Agreement at:` // askUserAgreement prompts the user to agree to the agreement // at the given agreement URL via stdin. It returns whether the // user agreed or not. -func (cfg *Config) askUserAgreement(agreementURL string) bool { - cfg.promptUserAgreement(agreementURL) +func (am *ACMEManager) askUserAgreement(agreementURL string) bool { + am.promptUserAgreement(agreementURL) fmt.Print("Do you agree to the terms? (y/n): ") reader := bufio.NewReader(stdin) @@ -245,24 +242,98 @@ func (cfg *Config) askUserAgreement(agreementURL string) bool { return answer == "y" || answer == "yes" } +func (am *ACMEManager) storageKeyCAPrefix(caURL string) string { + return path.Join(prefixACME, StorageKeys.Safe(am.issuerKey(caURL))) +} + +func (am *ACMEManager) storageKeyUsersPrefix(caURL string) string { + return path.Join(am.storageKeyCAPrefix(caURL), "users") +} + +func (am *ACMEManager) storageKeyUserPrefix(caURL, email string) string { + if email == "" { + email = emptyEmail + } + return path.Join(am.storageKeyUsersPrefix(caURL), StorageKeys.Safe(email)) +} + +func (am *ACMEManager) storageKeyUserReg(caURL, email string) string { + return am.storageSafeUserKey(caURL, email, "registration", ".json") +} + +func (am *ACMEManager) storageKeyUserPrivateKey(caURL, email string) string { + return am.storageSafeUserKey(caURL, email, "private", ".key") +} + +// storageSafeUserKey returns a key for the given email, with the default +// filename, and the filename ending in the given extension. +func (am *ACMEManager) storageSafeUserKey(ca, email, defaultFilename, extension string) string { + if email == "" { + email = emptyEmail + } + email = strings.ToLower(email) + filename := am.emailUsername(email) + if filename == "" { + filename = defaultFilename + } + filename = StorageKeys.Safe(filename) + return path.Join(am.storageKeyUserPrefix(ca, email), filename+extension) +} + +// emailUsername returns the username portion of an email address (part before +// '@') or the original input if it can't find the "@" symbol. +func (*ACMEManager) emailUsername(email string) string { + at := strings.Index(email, "@") + if at == -1 { + return email + } else if at == 0 { + return email[1:] + } + return email[:at] +} + // mostRecentUserEmail finds the most recently-written user file -// in s. Since this is part of a complex sequence to get a user +// in storage. Since this is part of a complex sequence to get a user // account, errors here are discarded to simplify code flow in // the caller, and errors are not important here anyway. -func (cfg *Config) mostRecentUserEmail() (string, bool) { - userList, err := cfg.Storage.List(StorageKeys.UsersPrefix(cfg.CA), false) +func (am *ACMEManager) mostRecentUserEmail(caURL string) (string, bool) { + userList, err := am.config.Storage.List(am.storageKeyUsersPrefix(caURL), false) if err != nil || len(userList) == 0 { return "", false } + + // get all the key infos ahead of sorting, because + // we might filter some out + stats := make(map[string]KeyInfo) + for i, u := range userList { + keyInfo, err := am.config.Storage.Stat(u) + if err != nil { + continue + } + if keyInfo.IsTerminal { + // I found a bug when macOS created a .DS_Store file in + // the users folder, and CertMagic tried to use that as + // the user email because it was newer than the other one + // which existed... sure, this isn't a perfect fix but + // frankly one's OS shouldn't mess with the data folder + // in the first place. + userList = append(userList[:i], userList[i+1:]...) + continue + } + stats[u] = keyInfo + } + sort.Slice(userList, func(i, j int) bool { - iInfo, _ := cfg.Storage.Stat(userList[i]) - jInfo, _ := cfg.Storage.Stat(userList[j]) + iInfo := stats[userList[i]] + jInfo := stats[userList[j]] return jInfo.Modified.Before(iInfo.Modified) }) - user, err := cfg.getUser(path.Base(userList[0])) + + user, err := am.getUser(caURL, path.Base(userList[0])) if err != nil { return "", false } + return user.Email, true } diff --git a/vendor/github.com/cenkalti/backoff/.gitignore b/vendor/github.com/cenkalti/backoff/v4/.gitignore similarity index 100% rename from vendor/github.com/cenkalti/backoff/.gitignore rename to vendor/github.com/cenkalti/backoff/v4/.gitignore diff --git a/vendor/github.com/cenkalti/backoff/.travis.yml b/vendor/github.com/cenkalti/backoff/v4/.travis.yml similarity index 95% rename from vendor/github.com/cenkalti/backoff/.travis.yml rename to vendor/github.com/cenkalti/backoff/v4/.travis.yml index 47a6a46e..871150c4 100644 --- a/vendor/github.com/cenkalti/backoff/.travis.yml +++ b/vendor/github.com/cenkalti/backoff/v4/.travis.yml @@ -1,6 +1,6 @@ language: go go: - - 1.7 + - 1.12 - 1.x - tip before_install: diff --git a/vendor/github.com/cenkalti/backoff/LICENSE b/vendor/github.com/cenkalti/backoff/v4/LICENSE similarity index 100% rename from vendor/github.com/cenkalti/backoff/LICENSE rename to vendor/github.com/cenkalti/backoff/v4/LICENSE diff --git a/vendor/github.com/cenkalti/backoff/README.md b/vendor/github.com/cenkalti/backoff/v4/README.md similarity index 87% rename from vendor/github.com/cenkalti/backoff/README.md rename to vendor/github.com/cenkalti/backoff/v4/README.md index 55ebc98f..cabfc9c7 100644 --- a/vendor/github.com/cenkalti/backoff/README.md +++ b/vendor/github.com/cenkalti/backoff/v4/README.md @@ -9,7 +9,10 @@ The retries exponentially increase and stop increasing when a certain threshold ## Usage -See https://godoc.org/github.com/cenkalti/backoff#pkg-examples +Import path is `github.com/cenkalti/backoff/v4`. Please note the version part at the end. + +godoc.org does not support modules yet, +so you can use https://godoc.org/gopkg.in/cenkalti/backoff.v4 to view the documentation. ## Contributing diff --git a/vendor/github.com/cenkalti/backoff/backoff.go b/vendor/github.com/cenkalti/backoff/v4/backoff.go similarity index 100% rename from vendor/github.com/cenkalti/backoff/backoff.go rename to vendor/github.com/cenkalti/backoff/v4/backoff.go diff --git a/vendor/github.com/cenkalti/backoff/context.go b/vendor/github.com/cenkalti/backoff/v4/context.go similarity index 77% rename from vendor/github.com/cenkalti/backoff/context.go rename to vendor/github.com/cenkalti/backoff/v4/context.go index 7706faa2..fcff86c1 100644 --- a/vendor/github.com/cenkalti/backoff/context.go +++ b/vendor/github.com/cenkalti/backoff/v4/context.go @@ -7,7 +7,7 @@ import ( // BackOffContext is a backoff policy that stops retrying after the context // is canceled. -type BackOffContext interface { +type BackOffContext interface { // nolint: golint BackOff Context() context.Context } @@ -20,7 +20,7 @@ type backOffContext struct { // WithContext returns a BackOffContext with context ctx // // ctx must not be nil -func WithContext(b BackOff, ctx context.Context) BackOffContext { +func WithContext(b BackOff, ctx context.Context) BackOffContext { // nolint: golint if ctx == nil { panic("nil context") } @@ -38,11 +38,14 @@ func WithContext(b BackOff, ctx context.Context) BackOffContext { } } -func ensureContext(b BackOff) BackOffContext { +func getContext(b BackOff) context.Context { if cb, ok := b.(BackOffContext); ok { - return cb + return cb.Context() } - return WithContext(b, context.Background()) + if tb, ok := b.(*backOffTries); ok { + return getContext(tb.delegate) + } + return context.Background() } func (b *backOffContext) Context() context.Context { @@ -56,7 +59,7 @@ func (b *backOffContext) NextBackOff() time.Duration { default: } next := b.BackOff.NextBackOff() - if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next { + if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next { // nolint: gosimple return Stop } return next diff --git a/vendor/github.com/cenkalti/backoff/exponential.go b/vendor/github.com/cenkalti/backoff/v4/exponential.go similarity index 89% rename from vendor/github.com/cenkalti/backoff/exponential.go rename to vendor/github.com/cenkalti/backoff/v4/exponential.go index a031a659..3d345321 100644 --- a/vendor/github.com/cenkalti/backoff/exponential.go +++ b/vendor/github.com/cenkalti/backoff/v4/exponential.go @@ -56,9 +56,10 @@ type ExponentialBackOff struct { RandomizationFactor float64 Multiplier float64 MaxInterval time.Duration - // After MaxElapsedTime the ExponentialBackOff stops. + // After MaxElapsedTime the ExponentialBackOff returns Stop. // It never stops if MaxElapsedTime == 0. MaxElapsedTime time.Duration + Stop time.Duration Clock Clock currentInterval time.Duration @@ -87,6 +88,7 @@ func NewExponentialBackOff() *ExponentialBackOff { Multiplier: DefaultMultiplier, MaxInterval: DefaultMaxInterval, MaxElapsedTime: DefaultMaxElapsedTime, + Stop: Stop, Clock: SystemClock, } b.Reset() @@ -103,20 +105,23 @@ func (t systemClock) Now() time.Time { var SystemClock = systemClock{} // Reset the interval back to the initial retry interval and restarts the timer. +// Reset must be called before using b. func (b *ExponentialBackOff) Reset() { b.currentInterval = b.InitialInterval b.startTime = b.Clock.Now() } // NextBackOff calculates the next backoff interval using the formula: -// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval) +// Randomized interval = RetryInterval * (1 ± RandomizationFactor) func (b *ExponentialBackOff) NextBackOff() time.Duration { // Make sure we have not gone over the maximum elapsed time. - if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime { - return Stop + elapsed := b.GetElapsedTime() + next := getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval) + b.incrementCurrentInterval() + if b.MaxElapsedTime != 0 && elapsed+next > b.MaxElapsedTime { + return b.Stop } - defer b.incrementCurrentInterval() - return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval) + return next } // GetElapsedTime returns the elapsed time since an ExponentialBackOff instance @@ -140,7 +145,7 @@ func (b *ExponentialBackOff) incrementCurrentInterval() { } // Returns a random value from the following interval: -// [randomizationFactor * currentInterval, randomizationFactor * currentInterval]. +// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval]. func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration { var delta = randomizationFactor * float64(currentInterval) var minInterval = float64(currentInterval) - delta diff --git a/vendor/github.com/cenkalti/backoff/v4/go.mod b/vendor/github.com/cenkalti/backoff/v4/go.mod new file mode 100644 index 00000000..cef50ea6 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/v4/go.mod @@ -0,0 +1,3 @@ +module github.com/cenkalti/backoff/v4 + +go 1.12 diff --git a/vendor/github.com/cenkalti/backoff/retry.go b/vendor/github.com/cenkalti/backoff/v4/retry.go similarity index 69% rename from vendor/github.com/cenkalti/backoff/retry.go rename to vendor/github.com/cenkalti/backoff/v4/retry.go index e936a506..6c776ccf 100644 --- a/vendor/github.com/cenkalti/backoff/retry.go +++ b/vendor/github.com/cenkalti/backoff/v4/retry.go @@ -21,16 +21,31 @@ type Notify func(error, time.Duration) // // Retry sleeps the goroutine for the duration returned by BackOff after a // failed operation returns. -func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) } +func Retry(o Operation, b BackOff) error { + return RetryNotify(o, b, nil) +} // RetryNotify calls notify function with the error and wait duration // for each failed attempt before sleep. func RetryNotify(operation Operation, b BackOff, notify Notify) error { + return RetryNotifyWithTimer(operation, b, notify, nil) +} + +// RetryNotifyWithTimer calls notify function with the error and wait duration using the given Timer +// for each failed attempt before sleep. +// A default timer that uses system timer is used when nil is passed. +func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer) error { var err error var next time.Duration - var t *time.Timer + if t == nil { + t = &defaultTimer{} + } - cb := ensureContext(b) + defer func() { + t.Stop() + }() + + ctx := getContext(b) b.Reset() for { @@ -42,7 +57,7 @@ func RetryNotify(operation Operation, b BackOff, notify Notify) error { return permanent.Err } - if next = cb.NextBackOff(); next == Stop { + if next = b.NextBackOff(); next == Stop { return err } @@ -50,17 +65,12 @@ func RetryNotify(operation Operation, b BackOff, notify Notify) error { notify(err, next) } - if t == nil { - t = time.NewTimer(next) - defer t.Stop() - } else { - t.Reset(next) - } + t.Start(next) select { - case <-cb.Context().Done(): - return err - case <-t.C: + case <-ctx.Done(): + return ctx.Err() + case <-t.C(): } } } @@ -74,6 +84,10 @@ func (e *PermanentError) Error() string { return e.Err.Error() } +func (e *PermanentError) Unwrap() error { + return e.Err +} + // Permanent wraps the given err in a *PermanentError. func Permanent(err error) *PermanentError { return &PermanentError{ diff --git a/vendor/github.com/cenkalti/backoff/ticker.go b/vendor/github.com/cenkalti/backoff/v4/ticker.go similarity index 74% rename from vendor/github.com/cenkalti/backoff/ticker.go rename to vendor/github.com/cenkalti/backoff/v4/ticker.go index e41084b0..df9d68bc 100644 --- a/vendor/github.com/cenkalti/backoff/ticker.go +++ b/vendor/github.com/cenkalti/backoff/v4/ticker.go @@ -1,6 +1,7 @@ package backoff import ( + "context" "sync" "time" ) @@ -12,7 +13,9 @@ import ( type Ticker struct { C <-chan time.Time c chan time.Time - b BackOffContext + b BackOff + ctx context.Context + timer Timer stop chan struct{} stopOnce sync.Once } @@ -24,12 +27,23 @@ type Ticker struct { // provided backoff policy (notably calling NextBackOff or Reset) // while the ticker is running. func NewTicker(b BackOff) *Ticker { + return NewTickerWithTimer(b, &defaultTimer{}) +} + +// NewTickerWithTimer returns a new Ticker with a custom timer. +// A default timer that uses system timer is used when nil is passed. +func NewTickerWithTimer(b BackOff, timer Timer) *Ticker { + if timer == nil { + timer = &defaultTimer{} + } c := make(chan time.Time) t := &Ticker{ - C: c, - c: c, - b: ensureContext(b), - stop: make(chan struct{}), + C: c, + c: c, + b: b, + ctx: getContext(b), + timer: timer, + stop: make(chan struct{}), } t.b.Reset() go t.run() @@ -59,7 +73,7 @@ func (t *Ticker) run() { case <-t.stop: t.c = nil // Prevent future ticks from being sent to the channel. return - case <-t.b.Context().Done(): + case <-t.ctx.Done(): return } } @@ -78,5 +92,6 @@ func (t *Ticker) send(tick time.Time) <-chan time.Time { return nil } - return time.After(next) + t.timer.Start(next) + return t.timer.C() } diff --git a/vendor/github.com/cenkalti/backoff/v4/timer.go b/vendor/github.com/cenkalti/backoff/v4/timer.go new file mode 100644 index 00000000..8120d021 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/v4/timer.go @@ -0,0 +1,35 @@ +package backoff + +import "time" + +type Timer interface { + Start(duration time.Duration) + Stop() + C() <-chan time.Time +} + +// defaultTimer implements Timer interface using time.Timer +type defaultTimer struct { + timer *time.Timer +} + +// C returns the timers channel which receives the current time when the timer fires. +func (t *defaultTimer) C() <-chan time.Time { + return t.timer.C +} + +// Start starts the timer to fire after the given duration +func (t *defaultTimer) Start(duration time.Duration) { + if t.timer == nil { + t.timer = time.NewTimer(duration) + } else { + t.timer.Reset(duration) + } +} + +// Stop is called when the timer is not used anymore and resources may be freed. +func (t *defaultTimer) Stop() { + if t.timer != nil { + t.timer.Stop() + } +} diff --git a/vendor/github.com/cenkalti/backoff/tries.go b/vendor/github.com/cenkalti/backoff/v4/tries.go similarity index 94% rename from vendor/github.com/cenkalti/backoff/tries.go rename to vendor/github.com/cenkalti/backoff/v4/tries.go index cfeefd9b..28d58ca3 100644 --- a/vendor/github.com/cenkalti/backoff/tries.go +++ b/vendor/github.com/cenkalti/backoff/v4/tries.go @@ -20,6 +20,9 @@ type backOffTries struct { } func (b *backOffTries) NextBackOff() time.Duration { + if b.maxTries == 0 { + return Stop + } if b.maxTries > 0 { if b.maxTries <= b.numTries { return Stop diff --git a/vendor/github.com/fsnotify/fsnotify/.editorconfig b/vendor/github.com/fsnotify/fsnotify/.editorconfig index ba49e3c2..fad89585 100644 --- a/vendor/github.com/fsnotify/fsnotify/.editorconfig +++ b/vendor/github.com/fsnotify/fsnotify/.editorconfig @@ -1,5 +1,12 @@ root = true -[*] +[*.go] indent_style = tab indent_size = 4 +insert_final_newline = true + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/vendor/github.com/fsnotify/fsnotify/.gitattributes b/vendor/github.com/fsnotify/fsnotify/.gitattributes new file mode 100644 index 00000000..32f1001b --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/.gitattributes @@ -0,0 +1 @@ +go.sum linguist-generated diff --git a/vendor/github.com/fsnotify/fsnotify/.travis.yml b/vendor/github.com/fsnotify/fsnotify/.travis.yml index 981d1bb8..a9c30165 100644 --- a/vendor/github.com/fsnotify/fsnotify/.travis.yml +++ b/vendor/github.com/fsnotify/fsnotify/.travis.yml @@ -2,29 +2,35 @@ sudo: false language: go go: - - 1.8.x - - 1.9.x - - tip + - "stable" + - "1.11.x" + - "1.10.x" + - "1.9.x" matrix: + include: + - go: "stable" + env: GOLINT=true allow_failures: - go: tip fast_finish: true -before_script: - - go get -u github.com/golang/lint/golint + +before_install: + - if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi script: - - go test -v --race ./... + - go test --race ./... after_script: - test -z "$(gofmt -s -l -w . | tee /dev/stderr)" - - test -z "$(golint ./... | tee /dev/stderr)" + - if [ ! -z "${GOLINT}" ]; then echo running golint; golint --set_exit_status ./...; else echo skipping golint; fi - go vet ./... os: - linux - osx + - windows notifications: email: false diff --git a/vendor/github.com/fsnotify/fsnotify/LICENSE b/vendor/github.com/fsnotify/fsnotify/LICENSE index f21e5408..e180c8fb 100644 --- a/vendor/github.com/fsnotify/fsnotify/LICENSE +++ b/vendor/github.com/fsnotify/fsnotify/LICENSE @@ -1,5 +1,5 @@ Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012 fsnotify Authors. All rights reserved. +Copyright (c) 2012-2019 fsnotify Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md index 39932074..b2629e52 100644 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -10,16 +10,16 @@ go get -u golang.org/x/sys/... Cross platform: Windows, Linux, BSD and macOS. -|Adapter |OS |Status | -|----------|----------|----------| -|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| -|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| -|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| -|fanotify |Linux 2.6.37+ | | -|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| -|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| +| Adapter | OS | Status | +| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| inotify | Linux 2.6.27 or later, Android\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| kqueue | BSD, macOS, iOS\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| ReadDirectoryChangesW | Windows | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) | +| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) | +| fanotify | Linux 2.6.37+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) | +| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) | +| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) | \* Android and iOS are untested. @@ -33,6 +33,53 @@ All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based o Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. +## Usage + +```go +package main + +import ( + "log" + + "github.com/fsnotify/fsnotify" +) + +func main() { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + + done := make(chan bool) + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + log.Println("event:", event) + if event.Op&fsnotify.Write == fsnotify.Write { + log.Println("modified file:", event.Name) + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Println("error:", err) + } + } + }() + + err = watcher.Add("/tmp/foo") + if err != nil { + log.Fatal(err) + } + <-done +} +``` + ## Contributing Please refer to [CONTRIBUTING][] before opening an issue or pull request. @@ -65,6 +112,10 @@ There are OS-specific limits as to how many watches can be created: * Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. * BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. +**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?** + +fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications. + [#62]: https://github.com/howeyc/fsnotify/issues/62 [#18]: https://github.com/fsnotify/fsnotify/issues/18 [#11]: https://github.com/fsnotify/fsnotify/issues/11 diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go index 190bf0de..89cab046 100644 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -63,4 +63,6 @@ func (e Event) String() string { } // Common errors that can be reported by a watcher -var ErrEventOverflow = errors.New("fsnotify queue overflow") +var ( + ErrEventOverflow = errors.New("fsnotify queue overflow") +) diff --git a/vendor/github.com/fsnotify/fsnotify/go.mod b/vendor/github.com/fsnotify/fsnotify/go.mod new file mode 100644 index 00000000..ff11e13f --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/go.mod @@ -0,0 +1,5 @@ +module github.com/fsnotify/fsnotify + +go 1.13 + +require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 diff --git a/vendor/github.com/fsnotify/fsnotify/go.sum b/vendor/github.com/fsnotify/fsnotify/go.sum new file mode 100644 index 00000000..f60af985 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go index cc7db4b2..b33f2b4d 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go +++ b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go @@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) { poller.fd = fd // Create epoll fd - poller.epfd, errno = unix.EpollCreate1(0) + poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC) if poller.epfd == -1 { return nil, errno } // Create pipe; pipe[0] is the read end, pipe[1] the write end. - errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) + errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC) if errno != nil { return nil, errno } diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go index 7d8de145..2306c462 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go @@ -8,4 +8,4 @@ package fsnotify import "golang.org/x/sys/unix" -const openMode = unix.O_NONBLOCK | unix.O_RDONLY +const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go index 9139e171..870c4d6d 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go @@ -9,4 +9,4 @@ package fsnotify import "golang.org/x/sys/unix" // note: this constant is not defined on BSD -const openMode = unix.O_EVTONLY +const openMode = unix.O_EVTONLY | unix.O_CLOEXEC diff --git a/vendor/github.com/go-acme/lego/LICENSE b/vendor/github.com/go-acme/lego/v3/LICENSE similarity index 100% rename from vendor/github.com/go-acme/lego/LICENSE rename to vendor/github.com/go-acme/lego/v3/LICENSE diff --git a/vendor/github.com/go-acme/lego/acme/api/account.go b/vendor/github.com/go-acme/lego/v3/acme/api/account.go similarity index 78% rename from vendor/github.com/go-acme/lego/acme/api/account.go rename to vendor/github.com/go-acme/lego/v3/acme/api/account.go index fd233142..04e7a6da 100644 --- a/vendor/github.com/go-acme/lego/acme/api/account.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/account.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/go-acme/lego/acme" + "github.com/go-acme/lego/v3/acme" ) type AccountService service @@ -31,12 +31,12 @@ func (a *AccountService) New(req acme.Account) (acme.ExtendedAccount, error) { 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) + return acme.ExtendedAccount{}, fmt.Errorf("acme: could not decode hmac key: %w", 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) + return acme.ExtendedAccount{}, fmt.Errorf("acme: error signing eab content: %w", err) } accMsg.ExternalAccountBinding = eabJWS @@ -57,6 +57,20 @@ func (a *AccountService) Get(accountURL string) (acme.Account, error) { return account, nil } +// Update Updates an account. +func (a *AccountService) Update(accountURL string, req acme.Account) (acme.ExtendedAccount, error) { + if len(accountURL) == 0 { + return acme.ExtendedAccount{}, errors.New("account[update]: empty URL") + } + + var account acme.ExtendedAccount + _, err := a.core.post(accountURL, req, &account) + if err != nil { + return acme.ExtendedAccount{}, err + } + return account, nil +} + // Deactivate Deactivates an account. func (a *AccountService) Deactivate(accountURL string) error { if len(accountURL) == 0 { diff --git a/vendor/github.com/go-acme/lego/acme/api/api.go b/vendor/github.com/go-acme/lego/v3/acme/api/api.go similarity index 86% rename from vendor/github.com/go-acme/lego/acme/api/api.go rename to vendor/github.com/go-acme/lego/v3/acme/api/api.go index 912e7c5a..a4993768 100644 --- a/vendor/github.com/go-acme/lego/acme/api/api.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/api.go @@ -10,12 +10,12 @@ import ( "net/http" "time" - "github.com/cenkalti/backoff" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api/internal/nonces" - "github.com/go-acme/lego/acme/api/internal/secure" - "github.com/go-acme/lego/acme/api/internal/sender" - "github.com/go-acme/lego/log" + "github.com/cenkalti/backoff/v4" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/acme/api/internal/nonces" + "github.com/go-acme/lego/v3/acme/api/internal/secure" + "github.com/go-acme/lego/v3/acme/api/internal/sender" + "github.com/go-acme/lego/v3/log" ) // Core ACME/LE core API. @@ -71,7 +71,7 @@ func (a *Core) post(uri string, reqBody, response interface{}) (*http.Response, } // postAsGet performs an HTTP POST ("POST-as-GET") request. -// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.3 +// https://tools.ietf.org/html/rfc8555#section-6.3 func (a *Core) postAsGet(uri string, response interface{}) (*http.Response, error) { return a.retrievablePost(uri, []byte{}, response) } @@ -93,7 +93,6 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{}) switch err.(type) { // Retry if the nonce was invalidated case *acme.NonceError: - log.Infof("nonce error retry: %s", err) return err default: cancel() @@ -104,7 +103,11 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{}) return nil } - err := backoff.Retry(operation, backoff.WithContext(bo, ctx)) + notify := func(err error, duration time.Duration) { + log.Infof("retry due to: %v", err) + } + + err := backoff.RetryNotify(operation, backoff.WithContext(bo, ctx), notify) if err != nil { return nil, err } @@ -115,7 +118,7 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{}) 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) + return nil, fmt.Errorf("failed to post JWS message: failed to sign content: %w", err) } signedBody := bytes.NewBuffer([]byte(signedContent.FullSerialize())) @@ -152,7 +155,7 @@ func (a *Core) GetDirectory() acme.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) + return dir, fmt.Errorf("get directory at '%s': %w", caDirURL, err) } if dir.NewAccountURL == "" { diff --git a/vendor/github.com/go-acme/lego/acme/api/authorization.go b/vendor/github.com/go-acme/lego/v3/acme/api/authorization.go similarity index 95% rename from vendor/github.com/go-acme/lego/acme/api/authorization.go rename to vendor/github.com/go-acme/lego/v3/acme/api/authorization.go index a59fa0d2..2931cadc 100644 --- a/vendor/github.com/go-acme/lego/acme/api/authorization.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/authorization.go @@ -3,7 +3,7 @@ package api import ( "errors" - "github.com/go-acme/lego/acme" + "github.com/go-acme/lego/v3/acme" ) type AuthorizationService service diff --git a/vendor/github.com/go-acme/lego/acme/api/certificate.go b/vendor/github.com/go-acme/lego/v3/acme/api/certificate.go similarity index 93% rename from vendor/github.com/go-acme/lego/acme/api/certificate.go rename to vendor/github.com/go-acme/lego/v3/acme/api/certificate.go index 65904e76..51e0aae6 100644 --- a/vendor/github.com/go-acme/lego/acme/api/certificate.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/certificate.go @@ -7,9 +7,9 @@ import ( "io/ioutil" "net/http" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/certcrypto" + "github.com/go-acme/lego/v3/log" ) // maxBodySize is the maximum size of body that we will read. @@ -71,7 +71,7 @@ func (c *CertificateService) get(certURL string) ([]byte, string, error) { // 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 + // See https://tools.ietf.org/html/rfc8555#section-7.4.2 up := getLink(resp.Header, "up") return cert, up, err diff --git a/vendor/github.com/go-acme/lego/acme/api/challenge.go b/vendor/github.com/go-acme/lego/v3/acme/api/challenge.go similarity index 97% rename from vendor/github.com/go-acme/lego/acme/api/challenge.go rename to vendor/github.com/go-acme/lego/v3/acme/api/challenge.go index f4e8dbee..f6acf22f 100644 --- a/vendor/github.com/go-acme/lego/acme/api/challenge.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/challenge.go @@ -3,7 +3,7 @@ package api import ( "errors" - "github.com/go-acme/lego/acme" + "github.com/go-acme/lego/v3/acme" ) type ChallengeService service diff --git a/vendor/github.com/go-acme/lego/acme/api/internal/nonces/nonce_manager.go b/vendor/github.com/go-acme/lego/v3/acme/api/internal/nonces/nonce_manager.go similarity index 87% rename from vendor/github.com/go-acme/lego/acme/api/internal/nonces/nonce_manager.go rename to vendor/github.com/go-acme/lego/v3/acme/api/internal/nonces/nonce_manager.go index c08f6d49..8c7fb92e 100644 --- a/vendor/github.com/go-acme/lego/acme/api/internal/nonces/nonce_manager.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/internal/nonces/nonce_manager.go @@ -6,7 +6,7 @@ import ( "net/http" "sync" - "github.com/go-acme/lego/acme/api/internal/sender" + "github.com/go-acme/lego/v3/acme/api/internal/sender" ) // Manager Manages nonces. @@ -57,7 +57,7 @@ func (n *Manager) Nonce() (string, error) { 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 "", fmt.Errorf("failed to get nonce from HTTP HEAD: %w", err) } return GetFromResponse(resp) @@ -71,7 +71,7 @@ func GetFromResponse(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 "", errors.New("server did not respond with a proper nonce header") } return nonce, nil diff --git a/vendor/github.com/go-acme/lego/acme/api/internal/secure/jws.go b/vendor/github.com/go-acme/lego/v3/acme/api/internal/secure/jws.go similarity index 90% rename from vendor/github.com/go-acme/lego/acme/api/internal/secure/jws.go rename to vendor/github.com/go-acme/lego/v3/acme/api/internal/secure/jws.go index 213aeda0..b75c6f31 100644 --- a/vendor/github.com/go-acme/lego/acme/api/internal/secure/jws.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/internal/secure/jws.go @@ -8,7 +8,7 @@ import ( "encoding/base64" "fmt" - "github.com/go-acme/lego/acme/api/internal/nonces" + "github.com/go-acme/lego/v3/acme/api/internal/nonces" jose "gopkg.in/square/go-jose.v2" ) @@ -65,12 +65,12 @@ func (j *JWS) SignContent(url string, content []byte) (*jose.JSONWebSignature, e signer, err := jose.NewSigner(signKey, &options) if err != nil { - return nil, fmt.Errorf("failed to create jose signer -> %v", err) + return nil, fmt.Errorf("failed to create jose signer: %w", err) } signed, err := signer.Sign(content) if err != nil { - return nil, fmt.Errorf("failed to sign content -> %v", err) + return nil, fmt.Errorf("failed to sign content: %w", err) } return signed, nil } @@ -80,7 +80,7 @@ func (j *JWS) SignEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignatu 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) + return nil, fmt.Errorf("acme: error encoding eab jwk key: %w", err) } signer, err := jose.NewSigner( @@ -94,12 +94,12 @@ func (j *JWS) SignEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignatu }, ) if err != nil { - return nil, fmt.Errorf("failed to create External Account Binding jose signer -> %v", err) + return nil, fmt.Errorf("failed to create External Account Binding jose signer: %w", err) } signed, err := signer.Sign(jwkJSON) if err != nil { - return nil, fmt.Errorf("failed to External Account Binding sign content -> %v", err) + return nil, fmt.Errorf("failed to External Account Binding sign content: %w", err) } return signed, nil diff --git a/vendor/github.com/go-acme/lego/acme/api/internal/sender/sender.go b/vendor/github.com/go-acme/lego/v3/acme/api/internal/sender/sender.go similarity index 91% rename from vendor/github.com/go-acme/lego/acme/api/internal/sender/sender.go rename to vendor/github.com/go-acme/lego/v3/acme/api/internal/sender/sender.go index e08f2ffb..7b5c4885 100644 --- a/vendor/github.com/go-acme/lego/acme/api/internal/sender/sender.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/internal/sender/sender.go @@ -9,7 +9,7 @@ import ( "runtime" "strings" - "github.com/go-acme/lego/acme" + "github.com/go-acme/lego/v3/acme" ) type RequestOption func(*http.Request) error @@ -70,7 +70,7 @@ func (d *Doer) Post(url string, body io.Reader, bodyType string, response interf 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) + return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("User-Agent", d.formatUserAgent()) @@ -78,7 +78,7 @@ func (d *Doer) newRequest(method, uri string, body io.Reader, opts ...RequestOpt for _, opt := range opts { err = opt(req) if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) + return nil, fmt.Errorf("failed to create request: %w", err) } } @@ -105,7 +105,7 @@ func (d *Doer) do(req *http.Request, response interface{}) (*http.Response, erro 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, fmt.Errorf("failed to unmarshal %q to type %T: %w", raw, response, err) } } @@ -120,16 +120,15 @@ func (d *Doer) formatUserAgent() string { 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) + return fmt.Errorf("%d :: %s :: %s :: %w", 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)) + return fmt.Errorf("%d ::%s :: %s :: %w :: %s", resp.StatusCode, req.Method, req.URL, err, string(body)) } errorDetails.Method = req.Method diff --git a/vendor/github.com/go-acme/lego/acme/api/internal/sender/useragent.go b/vendor/github.com/go-acme/lego/v3/acme/api/internal/sender/useragent.go similarity index 91% rename from vendor/github.com/go-acme/lego/acme/api/internal/sender/useragent.go rename to vendor/github.com/go-acme/lego/v3/acme/api/internal/sender/useragent.go index 9671434c..d5f046ae 100644 --- a/vendor/github.com/go-acme/lego/acme/api/internal/sender/useragent.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/internal/sender/useragent.go @@ -5,7 +5,7 @@ package sender const ( // ourUserAgent is the User-Agent of this underlying library package. - ourUserAgent = "xenolf-acme/2.6.0" + ourUserAgent = "xenolf-acme/3.6.0" // ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package. // values: detach|release diff --git a/vendor/github.com/go-acme/lego/acme/api/order.go b/vendor/github.com/go-acme/lego/v3/acme/api/order.go similarity index 97% rename from vendor/github.com/go-acme/lego/acme/api/order.go rename to vendor/github.com/go-acme/lego/v3/acme/api/order.go index 11240b4c..4bcc34f4 100644 --- a/vendor/github.com/go-acme/lego/acme/api/order.go +++ b/vendor/github.com/go-acme/lego/v3/acme/api/order.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "errors" - "github.com/go-acme/lego/acme" + "github.com/go-acme/lego/v3/acme" ) type OrderService service diff --git a/vendor/github.com/go-acme/lego/acme/api/service.go b/vendor/github.com/go-acme/lego/v3/acme/api/service.go similarity index 100% rename from vendor/github.com/go-acme/lego/acme/api/service.go rename to vendor/github.com/go-acme/lego/v3/acme/api/service.go diff --git a/vendor/github.com/go-acme/lego/acme/commons.go b/vendor/github.com/go-acme/lego/v3/acme/commons.go similarity index 91% rename from vendor/github.com/go-acme/lego/acme/commons.go rename to vendor/github.com/go-acme/lego/v3/acme/commons.go index c4493696..52a991bb 100644 --- a/vendor/github.com/go-acme/lego/acme/commons.go +++ b/vendor/github.com/go-acme/lego/v3/acme/commons.go @@ -1,5 +1,5 @@ // Package acme contains all objects related the ACME endpoints. -// https://tools.ietf.org/html/draft-ietf-acme-acme-16 +// https://tools.ietf.org/html/rfc8555 package acme import ( @@ -8,7 +8,7 @@ import ( ) // Challenge statuses -// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.6 +// https://tools.ietf.org/html/rfc8555#section-7.1.6 const ( StatusPending = "pending" StatusInvalid = "invalid" @@ -20,7 +20,7 @@ const ( ) // Directory the ACME directory object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.1 +// - https://tools.ietf.org/html/rfc8555#section-7.1.1 type Directory struct { NewNonceURL string `json:"newNonce"` NewAccountURL string `json:"newAccount"` @@ -32,7 +32,7 @@ type Directory struct { } // Meta the ACME meta object (related to Directory). -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.1 +// - https://tools.ietf.org/html/rfc8555#section-7.1.1 type Meta struct { // termsOfService (optional, string): // A URL identifying the current terms of service. @@ -65,8 +65,8 @@ type ExtendedAccount struct { } // 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 +// - https://tools.ietf.org/html/rfc8555#section-7.1.2 +// - https://tools.ietf.org/html/rfc8555#section-7.3 type Account struct { // status (required, string): // The status of this account. @@ -111,7 +111,7 @@ type ExtendedOrder struct { } // Order the ACME order Object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.3 +// - https://tools.ietf.org/html/rfc8555#section-7.1.3 type Order struct { // status (required, string): // The status of this order. @@ -164,7 +164,7 @@ type Order struct { } // Authorization the ACME authorization object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.4 +// - https://tools.ietf.org/html/rfc8555#section-7.1.4 type Authorization struct { // status (required, string): // The status of this authorization. @@ -206,8 +206,8 @@ type ExtendedChallenge struct { } // 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 +// - https://tools.ietf.org/html/rfc8555#section-7.1.5 +// - https://tools.ietf.org/html/rfc8555#section-8 type Challenge struct { // type (required, string): // The type of challenge encoded in the object. @@ -240,23 +240,23 @@ type Challenge struct { // 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 + // https://tools.ietf.org/html/rfc8555#section-8.3 + // https://tools.ietf.org/html/rfc8555#section-8.4 Token string `json:"token"` - // https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.1 + // https://tools.ietf.org/html/rfc8555#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 +// - https://tools.ietf.org/html/rfc8555#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 +// - https://tools.ietf.org/html/rfc8555#section-7.4 type CSRMessage struct { // csr (required, string): // A CSR encoding the parameters for the certificate being requested [RFC2986]. @@ -266,7 +266,7 @@ type CSRMessage struct { } // RevokeCertMessage a certificate revocation message -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.6 +// - https://tools.ietf.org/html/rfc8555#section-7.6 // - https://tools.ietf.org/html/rfc5280#section-5.3.1 type RevokeCertMessage struct { // certificate (required, string): diff --git a/vendor/github.com/go-acme/lego/acme/errors.go b/vendor/github.com/go-acme/lego/v3/acme/errors.go similarity index 91% rename from vendor/github.com/go-acme/lego/acme/errors.go rename to vendor/github.com/go-acme/lego/v3/acme/errors.go index 1658fe8d..02a7d92c 100644 --- a/vendor/github.com/go-acme/lego/acme/errors.go +++ b/vendor/github.com/go-acme/lego/v3/acme/errors.go @@ -12,7 +12,7 @@ const ( // 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 +// - https://tools.ietf.org/html/rfc8555#section-7.3.3 type ProblemDetails struct { Type string `json:"type,omitempty"` Detail string `json:"detail,omitempty"` @@ -26,7 +26,7 @@ type ProblemDetails struct { } // SubProblem a "subproblems" -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.7.1 +// - https://tools.ietf.org/html/rfc8555#section-6.7.1 type SubProblem struct { Type string `json:"type,omitempty"` Detail string `json:"detail,omitempty"` diff --git a/vendor/github.com/go-acme/lego/certcrypto/crypto.go b/vendor/github.com/go-acme/lego/v3/certcrypto/crypto.go similarity index 80% rename from vendor/github.com/go-acme/lego/certcrypto/crypto.go rename to vendor/github.com/go-acme/lego/v3/certcrypto/crypto.go index c9d0c109..5274d957 100644 --- a/vendor/github.com/go-acme/lego/certcrypto/crypto.go +++ b/vendor/github.com/go-acme/lego/v3/certcrypto/crypto.go @@ -3,6 +3,7 @@ package certcrypto import ( "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" @@ -13,6 +14,7 @@ import ( "errors" "fmt" "math/big" + "strings" "time" "golang.org/x/crypto/ocsp" @@ -77,17 +79,35 @@ func ParsePEMBundle(bundle []byte) ([]*x509.Certificate, error) { return certificates, nil } +// ParsePEMPrivateKey parses a private key from key, which is a PEM block. +// Borrowed from Go standard library, to handle various private key and PEM block types. +// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308 +// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238) func ParsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) { - keyBlock, _ := pem.Decode(key) + keyBlockDER, _ := 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") + if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") { + return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type) } + + if key, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil { + return key, nil + } + + if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: + return key, nil + default: + return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key) + } + } + + if key, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil { + return key, nil + } + + return nil, errors.New("failed to parse private key") } func GeneratePrivateKey(keyType KeyType) (crypto.PrivateKey, error) { @@ -147,7 +167,7 @@ func PEMBlock(data interface{}) *pem.Block { 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 nil, errors.New("PEM decode did not yield a valid block. Is the certificate in the right format?") } return pemBlock, nil @@ -160,7 +180,7 @@ func PemDecodeTox509CSR(pem []byte) (*x509.CertificateRequest, error) { } if pemBlock.Type != "CERTIFICATE REQUEST" { - return nil, fmt.Errorf("PEM block is not a certificate request") + return nil, errors.New("PEM block is not a certificate request") } return x509.ParseCertificateRequest(pemBlock.Bytes) @@ -179,7 +199,10 @@ func ParsePEMCertificate(cert []byte) (*x509.Certificate, error) { } func ExtractDomains(cert *x509.Certificate) []string { - domains := []string{cert.Subject.CommonName} + var domains []string + if cert.Subject.CommonName != "" { + domains = append(domains, cert.Subject.CommonName) + } // Check for SAN certificate for _, sanDomain := range cert.DNSNames { @@ -193,7 +216,10 @@ func ExtractDomains(cert *x509.Certificate) []string { } func ExtractDomainsCSR(csr *x509.CertificateRequest) []string { - domains := []string{csr.Subject.CommonName} + var domains []string + if csr.Subject.CommonName != "" { + domains = append(domains, csr.Subject.CommonName) + } // loop over the SubjectAltName DNS names for _, sanName := range csr.DNSNames { diff --git a/vendor/github.com/go-acme/lego/certificate/authorization.go b/vendor/github.com/go-acme/lego/v3/certificate/authorization.go similarity index 72% rename from vendor/github.com/go-acme/lego/certificate/authorization.go rename to vendor/github.com/go-acme/lego/v3/certificate/authorization.go index 00062504..f93413f0 100644 --- a/vendor/github.com/go-acme/lego/certificate/authorization.go +++ b/vendor/github.com/go-acme/lego/v3/certificate/authorization.go @@ -3,8 +3,8 @@ package certificate import ( "time" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/log" ) const ( @@ -61,9 +61,21 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz } 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) + for _, authzURL := range order.Authorizations { + auth, err := c.core.Authorizations.Get(authzURL) + if err != nil { + log.Infof("Unable to get the authorization for: %s", authzURL) + continue + } + + if auth.Status == acme.StatusValid { + log.Infof("Skipping deactivating of valid auth: %s", authzURL) + continue + } + + log.Infof("Deactivating auth: %s", authzURL) + if c.core.Authorizations.Deactivate(authzURL) != nil { + log.Infof("Unable to deactivate the authorization: %s", authzURL) } } } diff --git a/vendor/github.com/go-acme/lego/certificate/certificates.go b/vendor/github.com/go-acme/lego/v3/certificate/certificates.go similarity index 96% rename from vendor/github.com/go-acme/lego/certificate/certificates.go rename to vendor/github.com/go-acme/lego/v3/certificate/certificates.go index 79765e71..43145bfe 100644 --- a/vendor/github.com/go-acme/lego/certificate/certificates.go +++ b/vendor/github.com/go-acme/lego/v3/certificate/certificates.go @@ -12,12 +12,12 @@ import ( "strings" "time" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" - "github.com/go-acme/lego/platform/wait" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/acme/api" + "github.com/go-acme/lego/v3/certcrypto" + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/log" + "github.com/go-acme/lego/v3/platform/wait" "golang.org/x/crypto/ocsp" "golang.org/x/net/idna" ) @@ -210,8 +210,8 @@ func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bund // 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 + // RFC8555 Section 7.4 "Applying for Certificate Issuance" + // https://tools.ietf.org/html/rfc8555#section-7.4 // says: // Clients SHOULD NOT make any assumptions about the sort order of // "identifiers" or "authorizations" elements in the returned order @@ -317,7 +317,7 @@ func (c *Certifier) Revoke(cert []byte) error { x509Cert := certificates[0] if x509Cert.IsCA { - return fmt.Errorf("certificate bundle starts with a CA certificate") + return errors.New("certificate bundle starts with a CA certificate") } revokeMsg := acme.RevokeCertMessage{ @@ -502,7 +502,7 @@ func checkOrderStatus(order acme.Order) (bool, error) { } } -// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.4 +// https://tools.ietf.org/html/rfc8555#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]. diff --git a/vendor/github.com/go-acme/lego/certificate/errors.go b/vendor/github.com/go-acme/lego/v3/certificate/errors.go similarity index 85% rename from vendor/github.com/go-acme/lego/certificate/errors.go rename to vendor/github.com/go-acme/lego/v3/certificate/errors.go index 0fec7c16..3adbc783 100644 --- a/vendor/github.com/go-acme/lego/certificate/errors.go +++ b/vendor/github.com/go-acme/lego/v3/certificate/errors.go @@ -10,7 +10,7 @@ import ( type obtainError map[string]error func (e obtainError) Error() string { - buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n") + buffer := bytes.NewBufferString("error: one or more domains had a problem:\n") var domains []string for domain := range e { diff --git a/vendor/github.com/go-acme/lego/challenge/challenges.go b/vendor/github.com/go-acme/lego/v3/challenge/challenges.go similarity index 87% rename from vendor/github.com/go-acme/lego/challenge/challenges.go rename to vendor/github.com/go-acme/lego/v3/challenge/challenges.go index b3281402..f1cac006 100644 --- a/vendor/github.com/go-acme/lego/challenge/challenges.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/challenges.go @@ -3,22 +3,22 @@ package challenge import ( "fmt" - "github.com/go-acme/lego/acme" + "github.com/go-acme/lego/v3/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 + // HTTP01 is the "http-01" ACME challenge https://tools.ietf.org/html/rfc8555#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 + // DNS01 is the "dns-01" ACME challenge https://tools.ietf.org/html/rfc8555#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 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07 TLSALPN01 = Type("tls-alpn-01") ) diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/cname.go b/vendor/github.com/go-acme/lego/v3/challenge/dns01/cname.go similarity index 100% rename from vendor/github.com/go-acme/lego/challenge/dns01/cname.go rename to vendor/github.com/go-acme/lego/v3/challenge/dns01/cname.go diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge.go b/vendor/github.com/go-acme/lego/v3/challenge/dns01/dns_challenge.go similarity index 94% rename from vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge.go rename to vendor/github.com/go-acme/lego/v3/challenge/dns01/dns_challenge.go index 9500305f..0c12a553 100644 --- a/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/dns01/dns_challenge.go @@ -8,11 +8,11 @@ import ( "strconv" "time" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" - "github.com/go-acme/lego/platform/wait" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/acme/api" + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/log" + "github.com/go-acme/lego/v3/platform/wait" "github.com/miekg/dns" ) @@ -93,7 +93,7 @@ func (c *Challenge) PreSolve(authz acme.Authorization) error { 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 fmt.Errorf("[%s] acme: error presenting token: %w", domain, err) } return nil diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge_manual.go b/vendor/github.com/go-acme/lego/v3/challenge/dns01/dns_challenge_manual.go similarity index 100% rename from vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge_manual.go rename to vendor/github.com/go-acme/lego/v3/challenge/dns01/dns_challenge_manual.go diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/fqdn.go b/vendor/github.com/go-acme/lego/v3/challenge/dns01/fqdn.go similarity index 100% rename from vendor/github.com/go-acme/lego/challenge/dns01/fqdn.go rename to vendor/github.com/go-acme/lego/v3/challenge/dns01/fqdn.go diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/nameserver.go b/vendor/github.com/go-acme/lego/v3/challenge/dns01/nameserver.go similarity index 66% rename from vendor/github.com/go-acme/lego/challenge/dns01/nameserver.go rename to vendor/github.com/go-acme/lego/v3/challenge/dns01/nameserver.go index 03f1a8d1..e70db174 100644 --- a/vendor/github.com/go-acme/lego/challenge/dns01/nameserver.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/dns01/nameserver.go @@ -1,6 +1,7 @@ package dns01 import ( + "errors" "fmt" "net" "strings" @@ -16,8 +17,8 @@ const defaultResolvConf = "/etc/resolv.conf" var dnsTimeout = 10 * time.Second var ( - fqdnToZone = map[string]string{} - muFqdnToZone sync.Mutex + fqdnSoaCache = map[string]*soaCacheEntry{} + muFqdnSoaCache sync.Mutex ) var defaultNameservers = []string{ @@ -28,11 +29,31 @@ var defaultNameservers = []string{ // recursiveNameservers are used to pre-check DNS propagation var recursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers) +// soaCacheEntry holds a cached SOA record (only selected fields) +type soaCacheEntry struct { + zone string // zone apex (a domain name) + primaryNs string // primary nameserver for the zone apex + expires time.Time // time when this cache entry should be evicted +} + +func newSoaCacheEntry(soa *dns.SOA) *soaCacheEntry { + return &soaCacheEntry{ + zone: soa.Hdr.Name, + primaryNs: soa.Ns, + expires: time.Now().Add(time.Duration(soa.Refresh) * time.Second), + } +} + +// isExpired checks whether a cache entry should be considered expired. +func (cache *soaCacheEntry) isExpired() bool { + return time.Now().After(cache.expires) +} + // ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. func ClearFqdnCache() { - muFqdnToZone.Lock() - fqdnToZone = map[string]string{} - muFqdnToZone.Unlock() + muFqdnSoaCache.Lock() + fqdnSoaCache = map[string]*soaCacheEntry{} + muFqdnSoaCache.Unlock() } func AddDNSTimeout(timeout time.Duration) ChallengeOption { @@ -78,7 +99,7 @@ func lookupNameservers(fqdn string) ([]string, error) { zone, err := FindZoneByFqdn(fqdn) if err != nil { - return nil, fmt.Errorf("could not determine the zone: %v", err) + return nil, fmt.Errorf("could not determine the zone: %w", err) } r, err := dnsQuery(zone, dns.TypeNS, recursiveNameservers, true) @@ -95,7 +116,23 @@ func lookupNameservers(fqdn string) ([]string, error) { if len(authoritativeNss) > 0 { return authoritativeNss, nil } - return nil, fmt.Errorf("could not determine authoritative nameservers") + return nil, errors.New("could not determine authoritative nameservers") +} + +// FindPrimaryNsByFqdn determines the primary nameserver of 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 FindPrimaryNsByFqdn(fqdn string) (string, error) { + return FindPrimaryNsByFqdnCustom(fqdn, recursiveNameservers) +} + +// FindPrimaryNsByFqdnCustom determines the primary nameserver of 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 FindPrimaryNsByFqdnCustom(fqdn string, nameservers []string) (string, error) { + soa, err := lookupSoaByFqdn(fqdn, nameservers) + if err != nil { + return "", err + } + return soa.primaryNs, nil } // FindZoneByFqdn determines the zone apex for the given fqdn @@ -107,14 +144,32 @@ func FindZoneByFqdn(fqdn string) (string, error) { // 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() + soa, err := lookupSoaByFqdn(fqdn, nameservers) + if err != nil { + return "", err + } + return soa.zone, nil +} - // Do we have it cached? - if zone, ok := fqdnToZone[fqdn]; ok { - return zone, nil +func lookupSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) { + muFqdnSoaCache.Lock() + defer muFqdnSoaCache.Unlock() + + // Do we have it cached and is it still fresh? + if ent := fqdnSoaCache[fqdn]; ent != nil && !ent.isExpired() { + return ent, nil } + ent, err := fetchSoaByFqdn(fqdn, nameservers) + if err != nil { + return nil, err + } + + fqdnSoaCache[fqdn] = ent + return ent, nil +} + +func fetchSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) { var err error var in *dns.Msg @@ -134,7 +189,6 @@ func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) { switch in.Rcode { case dns.RcodeSuccess: // Check if we got a SOA RR in the answer section - if len(in.Answer) == 0 { continue } @@ -147,20 +201,18 @@ func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) { for _, ans := range in.Answer { if soa, ok := ans.(*dns.SOA); ok { - zone := soa.Hdr.Name - fqdnToZone[fqdn] = zone - return zone, nil + return newSoaCacheEntry(soa), 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 nil, 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)) + return nil, fmt.Errorf("could not find the start of authority for %s%s", fqdn, formatDNSError(in, err)) } // dnsMsgContainsCNAME checks for a CNAME answer in msg diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/precheck.go b/vendor/github.com/go-acme/lego/v3/challenge/dns01/precheck.go similarity index 100% rename from vendor/github.com/go-acme/lego/challenge/dns01/precheck.go rename to vendor/github.com/go-acme/lego/v3/challenge/dns01/precheck.go diff --git a/vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go b/vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go new file mode 100644 index 00000000..9d3fc900 --- /dev/null +++ b/vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go @@ -0,0 +1,184 @@ +package http01 + +import ( + "fmt" + "net/http" + "strings" +) + +// A domainMatcher tries to match a domain (the one we're requesting a certificate for) +// in the HTTP request coming from the ACME validation servers. +// This step is part of DNS rebind attack prevention, +// where the webserver matches incoming requests to a list of domain the server acts authoritative for. +// +// The most simple check involves finding the domain in the HTTP Host header; +// this is what hostMatcher does. +// Use it, when the http01.ProviderServer is directly reachable from the internet, +// or when it operates behind a transparent proxy. +// +// In many (reverse) proxy setups, Apache and NGINX traditionally move the Host header to a new header named X-Forwarded-Host. +// Use arbitraryMatcher("X-Forwarded-Host") in this case, +// or the appropriate header name for other proxy servers. +// +// RFC7239 has standardized the different forwarding headers into a single header named Forwarded. +// The header value has a different format, so you should use forwardedMatcher +// when the http01.ProviderServer operates behind a RFC7239 compatible proxy. +// https://tools.ietf.org/html/rfc7239 +// +// Note: RFC7239 also reminds us, "that an HTTP list [...] may be split over multiple header fields" (section 7.1), +// meaning that +// X-Header: a +// X-Header: b +// is equal to +// X-Header: a, b +// +// All matcher implementations (explicitly not excluding arbitraryMatcher!) +// have in common that they only match against the first value in such lists. +type domainMatcher interface { + // matches checks whether the request is valid for the given domain. + matches(request *http.Request, domain string) bool + + // name returns the header name used in the check. + // This is primarily used to create meaningful error messages. + name() string +} + +// hostMatcher checks whether (*net/http).Request.Host starts with a domain name. +type hostMatcher struct{} + +func (m *hostMatcher) name() string { + return "Host" +} + +func (m *hostMatcher) matches(r *http.Request, domain string) bool { + return strings.HasPrefix(r.Host, domain) +} + +// hostMatcher checks whether the specified (*net/http.Request).Header value starts with a domain name. +type arbitraryMatcher string + +func (m arbitraryMatcher) name() string { + return string(m) +} + +func (m arbitraryMatcher) matches(r *http.Request, domain string) bool { + return strings.HasPrefix(r.Header.Get(m.name()), domain) +} + +// forwardedMatcher checks whether the Forwarded header contains a "host" element starting with a domain name. +// See https://tools.ietf.org/html/rfc7239 for details. +type forwardedMatcher struct{} + +func (m *forwardedMatcher) name() string { + return "Forwarded" +} + +func (m *forwardedMatcher) matches(r *http.Request, domain string) bool { + fwds, err := parseForwardedHeader(r.Header.Get(m.name())) + if err != nil { + return false + } + + if len(fwds) == 0 { + return false + } + + host := fwds[0]["host"] + return strings.HasPrefix(host, domain) +} + +// parsing requires some form of state machine +func parseForwardedHeader(s string) (elements []map[string]string, err error) { + cur := make(map[string]string) + key := "" + val := "" + inquote := false + + pos := 0 + l := len(s) + for i := 0; i < l; i++ { + r := rune(s[i]) + + if inquote { + if r == '"' { + cur[key] = s[pos:i] + key = "" + pos = i + inquote = false + } + continue + } + + switch { + case r == '"': // start of quoted-string + if key == "" { + return nil, fmt.Errorf("unexpected quoted string as pos %d", i) + } + inquote = true + pos = i + 1 + + case r == ';': // end of forwarded-pair + cur[key] = s[pos:i] + key = "" + i = skipWS(s, i) + pos = i + 1 + + case r == '=': // end of token + key = strings.ToLower(strings.TrimFunc(s[pos:i], isWS)) + i = skipWS(s, i) + pos = i + 1 + + case r == ',': // end of forwarded-element + if key != "" { + if val == "" { + val = s[pos:i] + } + cur[key] = val + } + elements = append(elements, cur) + cur = make(map[string]string) + key = "" + val = "" + + i = skipWS(s, i) + pos = i + 1 + case tchar(r) || isWS(r): // valid token character or whitespace + continue + default: + return nil, fmt.Errorf("invalid token character at pos %d: %c", i, r) + } + } + + if inquote { + return nil, fmt.Errorf("unterminated quoted-string at pos %d", len(s)) + } + + if key != "" { + if pos < len(s) { + val = s[pos:] + } + cur[key] = val + } + if len(cur) > 0 { + elements = append(elements, cur) + } + return elements, nil +} + +func tchar(r rune) bool { + return strings.ContainsRune("!#$%&'*+-.^_`|~", r) || + '0' <= r && r <= '9' || + 'a' <= r && r <= 'z' || + 'A' <= r && r <= 'Z' +} + +func skipWS(s string, i int) int { + for isWS(rune(s[i+1])) { + i++ + } + return i +} + +func isWS(r rune) bool { + return strings.ContainsRune(" \t\v\r\n", r) +} diff --git a/vendor/github.com/go-acme/lego/challenge/http01/http_challenge.go b/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge.go similarity index 83% rename from vendor/github.com/go-acme/lego/challenge/http01/http_challenge.go rename to vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge.go index c1cf3d45..e6cc0d1b 100644 --- a/vendor/github.com/go-acme/lego/challenge/http01/http_challenge.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge.go @@ -3,10 +3,10 @@ package http01 import ( "fmt" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/acme/api" + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/log" ) type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error @@ -51,12 +51,12 @@ func (c *Challenge) Solve(authz acme.Authorization) error { err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth) if err != nil { - return fmt.Errorf("[%s] acme: error presenting token: %v", domain, err) + return fmt.Errorf("[%s] acme: error presenting token: %w", 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) + log.Warnf("[%s] acme: cleaning up failed: %v", domain, err) } }() diff --git a/vendor/github.com/go-acme/lego/challenge/http01/http_challenge_server.go b/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge_server.go similarity index 57% rename from vendor/github.com/go-acme/lego/challenge/http01/http_challenge_server.go rename to vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge_server.go index 9f442911..af5ab524 100644 --- a/vendor/github.com/go-acme/lego/challenge/http01/http_challenge_server.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge_server.go @@ -4,9 +4,10 @@ import ( "fmt" "net" "net/http" + "net/textproto" "strings" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/log" ) // ProviderServer implements ChallengeProvider for `http-01` challenge @@ -15,6 +16,7 @@ import ( type ProviderServer struct { iface string port string + matcher domainMatcher done chan bool listener net.Listener } @@ -23,19 +25,19 @@ type ProviderServer struct { // Setting iface and / or port to an empty string will make the server fall back to // the "any" interface and port 80 respectively. func NewProviderServer(iface, port string) *ProviderServer { - return &ProviderServer{iface: iface, port: port} + if port == "" { + port = "80" + } + + return &ProviderServer{iface: iface, port: port, matcher: &hostMatcher{}} } // 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", s.GetAddress()) if err != nil { - return fmt.Errorf("could not start HTTP server for challenge -> %v", err) + return fmt.Errorf("could not start HTTP server for challenge: %w", err) } s.done = make(chan bool) @@ -57,14 +59,38 @@ func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { return nil } +// SetProxyHeader changes the validation of incoming requests. +// By default, s matches the "Host" header value to the domain name. +// +// When the server runs behind a proxy server, this is not the correct place to look at; +// Apache and NGINX have traditionally moved the original Host header into a new header named "X-Forwarded-Host". +// Other webservers might use different names; +// and RFC7239 has standadized a new header named "Forwarded" (with slightly different semantics). +// +// The exact behavior depends on the value of headerName: +// - "" (the empty string) and "Host" will restore the default and only check the Host header +// - "Forwarded" will look for a Forwarded header, and inspect it according to https://tools.ietf.org/html/rfc7239 +// - any other value will check the header value with the same name +func (s *ProviderServer) SetProxyHeader(headerName string) { + switch h := textproto.CanonicalMIMEHeaderKey(headerName); h { + case "", "Host": + s.matcher = &hostMatcher{} + case "Forwarded": + s.matcher = &forwardedMatcher{} + default: + s.matcher = arbitraryMatcher(h) + } +} + 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 + // The incoming request must will be validated to prevent DNS rebind attacks. + // We only respond with the keyAuth, when we're receiving a GET requests with + // the "Host" header matching the domain (the latter is configurable though SetProxyHeader). mux := http.NewServeMux() mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.Host, domain) && r.Method == http.MethodGet { + if r.Method == http.MethodGet && s.matcher.matches(r, domain) { w.Header().Add("Content-Type", "text/plain") _, err := w.Write([]byte(keyAuth)) if err != nil { @@ -73,7 +99,7 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) { } log.Infof("[%s] Served key authentication", domain) } else { - log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method) + log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the %s header properly.", r.Host, r.Method, s.matcher.name()) _, err := w.Write([]byte("TEST")) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/vendor/github.com/go-acme/lego/challenge/provider.go b/vendor/github.com/go-acme/lego/v3/challenge/provider.go similarity index 100% rename from vendor/github.com/go-acme/lego/challenge/provider.go rename to vendor/github.com/go-acme/lego/v3/challenge/provider.go diff --git a/vendor/github.com/go-acme/lego/challenge/resolver/errors.go b/vendor/github.com/go-acme/lego/v3/challenge/resolver/errors.go similarity index 83% rename from vendor/github.com/go-acme/lego/challenge/resolver/errors.go rename to vendor/github.com/go-acme/lego/v3/challenge/resolver/errors.go index 9d609143..e9da1ebf 100644 --- a/vendor/github.com/go-acme/lego/challenge/resolver/errors.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/resolver/errors.go @@ -10,7 +10,7 @@ import ( type obtainError map[string]error func (e obtainError) Error() string { - buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n") + buffer := bytes.NewBufferString("error: one or more domains had a problem:\n") var domains []string for domain := range e { diff --git a/vendor/github.com/go-acme/lego/challenge/resolver/prober.go b/vendor/github.com/go-acme/lego/v3/challenge/resolver/prober.go similarity index 96% rename from vendor/github.com/go-acme/lego/challenge/resolver/prober.go rename to vendor/github.com/go-acme/lego/v3/challenge/resolver/prober.go index b787caf1..d3c71e8d 100644 --- a/vendor/github.com/go-acme/lego/challenge/resolver/prober.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/resolver/prober.go @@ -4,9 +4,9 @@ import ( "fmt" "time" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/log" ) // Interface for all challenge solvers to implement. @@ -167,7 +167,7 @@ func cleanUp(solvr solver, authz acme.Authorization) { domain := challenge.GetTargetedDomain(authz) err := solvr.CleanUp(authz) if err != nil { - log.Warnf("[%s] acme: error cleaning up: %v ", domain, err) + log.Warnf("[%s] acme: cleaning up failed: %v ", domain, err) } } } diff --git a/vendor/github.com/go-acme/lego/challenge/resolver/solver_manager.go b/vendor/github.com/go-acme/lego/v3/challenge/resolver/solver_manager.go similarity index 92% rename from vendor/github.com/go-acme/lego/challenge/resolver/solver_manager.go rename to vendor/github.com/go-acme/lego/v3/challenge/resolver/solver_manager.go index de6f02db..bd79a4f8 100644 --- a/vendor/github.com/go-acme/lego/challenge/resolver/solver_manager.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/resolver/solver_manager.go @@ -8,14 +8,14 @@ import ( "strconv" "time" - "github.com/cenkalti/backoff" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/challenge/dns01" - "github.com/go-acme/lego/challenge/http01" - "github.com/go-acme/lego/challenge/tlsalpn01" - "github.com/go-acme/lego/log" + "github.com/cenkalti/backoff/v4" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/acme/api" + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/challenge/dns01" + "github.com/go-acme/lego/v3/challenge/http01" + "github.com/go-acme/lego/v3/challenge/tlsalpn01" + "github.com/go-acme/lego/v3/log" ) type byType []acme.Challenge @@ -79,7 +79,7 @@ func (c *SolverManager) chooseSolver(authz acme.Authorization) solver { 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) + return fmt.Errorf("failed to initiate challenge: %w", err) } valid, err := checkChallengeStatus(chlng) diff --git a/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge.go b/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge.go similarity index 90% rename from vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge.go rename to vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge.go index a3fa7ef4..348e5e92 100644 --- a/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge.go @@ -8,15 +8,15 @@ import ( "encoding/asn1" "fmt" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/acme/api" + "github.com/go-acme/lego/v3/certcrypto" + "github.com/go-acme/lego/v3/challenge" + "github.com/go-acme/lego/v3/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 +// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.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 @@ -57,12 +57,12 @@ func (c *Challenge) Solve(authz acme.Authorization) error { err = c.provider.Present(domain, chlng.Token, keyAuth) if err != nil { - return fmt.Errorf("[%s] acme: error presenting token: %v", challenge.GetTargetedDomain(authz), err) + return fmt.Errorf("[%s] acme: error presenting token: %w", 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) + log.Warnf("[%s] acme: cleaning up failed: %v", challenge.GetTargetedDomain(authz), err) } }() @@ -83,7 +83,7 @@ func ChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) { // 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 + // Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-3 extensions := []pkix.Extension{ { Id: idPeAcmeIdentifierV1, diff --git a/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go b/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge_server.go similarity index 93% rename from vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go rename to vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge_server.go index 61e353be..8d09585c 100644 --- a/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go +++ b/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge_server.go @@ -7,7 +7,7 @@ import ( "net/http" "strings" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/log" ) const ( @@ -60,13 +60,13 @@ func (s *ProviderServer) Present(domain, token, keyAuth string) error { // We must set that the `acme-tls/1` application level protocol is supported // so that the protocol negotiation can succeed. Reference: - // https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-5.2 + // https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.2 tlsConf.NextProtos = []string{ACMETLS1Protocol} // Create the listener with the created tls.Config. s.listener, err = tls.Listen("tcp", s.GetAddress(), tlsConf) if err != nil { - return fmt.Errorf("could not start HTTPS server for challenge -> %v", err) + return fmt.Errorf("could not start HTTPS server for challenge: %w", err) } // Shut the server down when we're finished. diff --git a/vendor/github.com/go-acme/lego/lego/client.go b/vendor/github.com/go-acme/lego/v3/lego/client.go similarity index 91% rename from vendor/github.com/go-acme/lego/lego/client.go rename to vendor/github.com/go-acme/lego/v3/lego/client.go index c55dd25d..751c5cd1 100644 --- a/vendor/github.com/go-acme/lego/lego/client.go +++ b/vendor/github.com/go-acme/lego/v3/lego/client.go @@ -4,10 +4,10 @@ import ( "errors" "net/url" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/certificate" - "github.com/go-acme/lego/challenge/resolver" - "github.com/go-acme/lego/registration" + "github.com/go-acme/lego/v3/acme/api" + "github.com/go-acme/lego/v3/certificate" + "github.com/go-acme/lego/v3/challenge/resolver" + "github.com/go-acme/lego/v3/registration" ) // Client is the user-friendly way to ACME diff --git a/vendor/github.com/go-acme/lego/lego/client_config.go b/vendor/github.com/go-acme/lego/v3/lego/client_config.go similarity index 97% rename from vendor/github.com/go-acme/lego/lego/client_config.go rename to vendor/github.com/go-acme/lego/v3/lego/client_config.go index 2421d7fa..984ed249 100644 --- a/vendor/github.com/go-acme/lego/lego/client_config.go +++ b/vendor/github.com/go-acme/lego/v3/lego/client_config.go @@ -10,8 +10,8 @@ import ( "os" "time" - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/registration" + "github.com/go-acme/lego/v3/certcrypto" + "github.com/go-acme/lego/v3/registration" ) const ( diff --git a/vendor/github.com/go-acme/lego/log/logger.go b/vendor/github.com/go-acme/lego/v3/log/logger.go similarity index 100% rename from vendor/github.com/go-acme/lego/log/logger.go rename to vendor/github.com/go-acme/lego/v3/log/logger.go diff --git a/vendor/github.com/go-acme/lego/platform/wait/wait.go b/vendor/github.com/go-acme/lego/v3/platform/wait/wait.go similarity index 77% rename from vendor/github.com/go-acme/lego/platform/wait/wait.go rename to vendor/github.com/go-acme/lego/v3/platform/wait/wait.go index 97af5dce..163c6b35 100644 --- a/vendor/github.com/go-acme/lego/platform/wait/wait.go +++ b/vendor/github.com/go-acme/lego/v3/platform/wait/wait.go @@ -4,19 +4,19 @@ import ( "fmt" "time" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/log" ) // 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 + var lastErr error timeUp := time.After(timeout) for { select { case <-timeUp: - return fmt.Errorf("time limit exceeded: last error: %s", lastErr) + return fmt.Errorf("time limit exceeded: last error: %w", lastErr) default: } @@ -25,7 +25,7 @@ func For(msg string, timeout, interval time.Duration, f func() (bool, error)) er return nil } if err != nil { - lastErr = err.Error() + lastErr = err } time.Sleep(interval) diff --git a/vendor/github.com/go-acme/lego/registration/registar.go b/vendor/github.com/go-acme/lego/v3/registration/registar.go similarity index 80% rename from vendor/github.com/go-acme/lego/registration/registar.go rename to vendor/github.com/go-acme/lego/v3/registration/registar.go index 09e86657..77c390ee 100644 --- a/vendor/github.com/go-acme/lego/registration/registar.go +++ b/vendor/github.com/go-acme/lego/v3/registration/registar.go @@ -4,14 +4,14 @@ import ( "errors" "net/http" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/log" + "github.com/go-acme/lego/v3/acme" + "github.com/go-acme/lego/v3/acme/api" + "github.com/go-acme/lego/v3/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). +// WARNING: will be remove in the future (acme.ExtendedAccount), https://github.com/go-acme/lego/issues/855. type Resource struct { Body acme.Account `json:"body,omitempty"` URI string `json:"uri,omitempty"` @@ -115,6 +115,30 @@ func (r *Registrar) QueryRegistration() (*Resource, error) { }, nil } +// UpdateRegistration update the user registration on the ACME server. +func (r *Registrar) UpdateRegistration(options RegisterOptions) (*Resource, error) { + if r == nil || r.user == nil { + return nil, errors.New("acme: cannot update 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.Update(r.user.GetRegistration().URI, accMsg) + if err != nil { + return nil, err + } + + return &Resource{URI: account.Location, Body: account.Account}, nil +} + // DeleteRegistration deletes the client's user registration from the ACME server. func (r *Registrar) DeleteRegistration() error { if r == nil || r.user == nil { diff --git a/vendor/github.com/go-acme/lego/registration/user.go b/vendor/github.com/go-acme/lego/v3/registration/user.go similarity index 100% rename from vendor/github.com/go-acme/lego/registration/user.go rename to vendor/github.com/go-acme/lego/v3/registration/user.go diff --git a/vendor/github.com/gorilla/websocket/.travis.yml b/vendor/github.com/gorilla/websocket/.travis.yml deleted file mode 100644 index a49db51c..00000000 --- a/vendor/github.com/gorilla/websocket/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: go -sudo: false - -matrix: - include: - - go: 1.7.x - - go: 1.8.x - - go: 1.9.x - - go: 1.10.x - - go: 1.11.x - - go: tip - allow_failures: - - go: tip - -script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d .) - - go vet $(go list ./... | grep -v /vendor/) - - go test -v -race ./... diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md index 20e391f8..19aa2e75 100644 --- a/vendor/github.com/gorilla/websocket/README.md +++ b/vendor/github.com/gorilla/websocket/README.md @@ -1,14 +1,14 @@ # Gorilla WebSocket +[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) +[![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket) + Gorilla WebSocket is a [Go](http://golang.org/) implementation of the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. -[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) -[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) - ### Documentation -* [API Reference](http://godoc.org/github.com/gorilla/websocket) +* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) @@ -27,7 +27,7 @@ package API is stable. ### Protocol Compliance The Gorilla WebSocket package passes the server tests in the [Autobahn Test -Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn +Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). ### Gorilla WebSocket compared with other packages @@ -40,7 +40,7 @@ subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn RFC 6455 Features -Passes Autobahn Test SuiteYesNo +Passes Autobahn Test SuiteYesNo Receive fragmented messageYesNo, see note 1 Send close messageYesNo Send pings and receive pongsYesNo diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go index 2e32fd50..962c06a3 100644 --- a/vendor/github.com/gorilla/websocket/client.go +++ b/vendor/github.com/gorilla/websocket/client.go @@ -70,7 +70,7 @@ type Dialer struct { // HandshakeTimeout specifies the duration for the handshake to complete. HandshakeTimeout time.Duration - // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer // size is zero, then a useful default size is used. The I/O buffer sizes // do not limit the size of the messages that can be sent or received. ReadBufferSize, WriteBufferSize int @@ -140,7 +140,7 @@ var nilDialer = *DefaultDialer // Use the response.Header to get the selected subprotocol // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). // -// The context will be used in the request and in the Dialer +// The context will be used in the request and in the Dialer. // // If the WebSocket handshake fails, ErrBadHandshake is returned along with a // non-nil *http.Response so that callers can handle redirects, authentication, diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go index d2a21c14..ca46d2f7 100644 --- a/vendor/github.com/gorilla/websocket/conn.go +++ b/vendor/github.com/gorilla/websocket/conn.go @@ -244,8 +244,8 @@ type Conn struct { subprotocol string // Write fields - mu chan bool // used as mutex to protect write to conn - writeBuf []byte // frame is constructed in this buffer. + mu chan struct{} // used as mutex to protect write to conn + writeBuf []byte // frame is constructed in this buffer. writePool BufferPool writeBufSize int writeDeadline time.Time @@ -260,10 +260,12 @@ type Conn struct { newCompressionWriter func(io.WriteCloser, int) io.WriteCloser // Read fields - reader io.ReadCloser // the current reader returned to the application - readErr error - br *bufio.Reader - readRemaining int64 // bytes remaining in current frame. + reader io.ReadCloser // the current reader returned to the application + readErr error + br *bufio.Reader + // bytes remaining in current frame. + // set setReadRemaining to safely update this value and prevent overflow + readRemaining int64 readFinal bool // true the current message has more frames. readLength int64 // Message size. readLimit int64 // Maximum message size. @@ -300,8 +302,8 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBuf = make([]byte, writeBufferSize) } - mu := make(chan bool, 1) - mu <- true + mu := make(chan struct{}, 1) + mu <- struct{}{} c := &Conn{ isServer: isServer, br: br, @@ -320,6 +322,17 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, return c } +// setReadRemaining tracks the number of bytes remaining on the connection. If n +// overflows, an ErrReadLimit is returned. +func (c *Conn) setReadRemaining(n int64) error { + if n < 0 { + return ErrReadLimit + } + + c.readRemaining = n + return nil +} + // Subprotocol returns the negotiated protocol for the connection. func (c *Conn) Subprotocol() string { return c.subprotocol @@ -364,7 +377,7 @@ func (c *Conn) read(n int) ([]byte, error) { func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { <-c.mu - defer func() { c.mu <- true }() + defer func() { c.mu <- struct{}{} }() c.writeErrMu.Lock() err := c.writeErr @@ -416,7 +429,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er maskBytes(key, 0, buf[6:]) } - d := time.Hour * 1000 + d := 1000 * time.Hour if !deadline.IsZero() { d = deadline.Sub(time.Now()) if d < 0 { @@ -431,7 +444,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er case <-timer.C: return errWriteTimeout } - defer func() { c.mu <- true }() + defer func() { c.mu <- struct{}{} }() c.writeErrMu.Lock() err := c.writeErr @@ -451,7 +464,8 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er return err } -func (c *Conn) prepWrite(messageType int) error { +// beginMessage prepares a connection and message writer for a new message. +func (c *Conn) beginMessage(mw *messageWriter, messageType int) error { // Close previous writer if not already closed by the application. It's // probably better to return an error in this situation, but we cannot // change this without breaking existing applications. @@ -471,6 +485,10 @@ func (c *Conn) prepWrite(messageType int) error { return err } + mw.c = c + mw.frameType = messageType + mw.pos = maxFrameHeaderSize + if c.writeBuf == nil { wpd, ok := c.writePool.Get().(writePoolData) if ok { @@ -491,16 +509,11 @@ func (c *Conn) prepWrite(messageType int) error { // All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and // PongMessage) are supported. func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { - if err := c.prepWrite(messageType); err != nil { + var mw messageWriter + if err := c.beginMessage(&mw, messageType); err != nil { return nil, err } - - mw := &messageWriter{ - c: c, - frameType: messageType, - pos: maxFrameHeaderSize, - } - c.writer = mw + c.writer = &mw if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { w := c.newCompressionWriter(c.writer, c.compressionLevel) mw.compress = true @@ -517,10 +530,16 @@ type messageWriter struct { err error } -func (w *messageWriter) fatal(err error) error { +func (w *messageWriter) endMessage(err error) error { if w.err != nil { - w.err = err - w.c.writer = nil + return err + } + c := w.c + w.err = err + c.writer = nil + if c.writePool != nil { + c.writePool.Put(writePoolData{buf: c.writeBuf}) + c.writeBuf = nil } return err } @@ -534,7 +553,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error { // Check for invalid control frames. if isControl(w.frameType) && (!final || length > maxControlFramePayloadSize) { - return w.fatal(errInvalidControlFrame) + return w.endMessage(errInvalidControlFrame) } b0 := byte(w.frameType) @@ -579,7 +598,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error { copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) if len(extra) > 0 { - return c.writeFatal(errors.New("websocket: internal error, extra used in client mode")) + return w.endMessage(c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))) } } @@ -600,15 +619,11 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error { c.isWriting = false if err != nil { - return w.fatal(err) + return w.endMessage(err) } if final { - c.writer = nil - if c.writePool != nil { - c.writePool.Put(writePoolData{buf: c.writeBuf}) - c.writeBuf = nil - } + w.endMessage(errWriteClosed) return nil } @@ -706,11 +721,7 @@ func (w *messageWriter) Close() error { if w.err != nil { return w.err } - if err := w.flushFrame(true, nil); err != nil { - return err - } - w.err = errWriteClosed - return nil + return w.flushFrame(true, nil) } // WritePreparedMessage writes prepared message into connection. @@ -742,10 +753,10 @@ func (c *Conn) WriteMessage(messageType int, data []byte) error { if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { // Fast path with no allocations and single frame. - if err := c.prepWrite(messageType); err != nil { + var mw messageWriter + if err := c.beginMessage(&mw, messageType); err != nil { return err } - mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} n := copy(c.writeBuf[mw.pos:], data) mw.pos += n data = data[n:] @@ -792,7 +803,7 @@ func (c *Conn) advanceFrame() (int, error) { final := p[0]&finalBit != 0 frameType := int(p[0] & 0xf) mask := p[1]&maskBit != 0 - c.readRemaining = int64(p[1] & 0x7f) + c.setReadRemaining(int64(p[1] & 0x7f)) c.readDecompress = false if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { @@ -826,7 +837,17 @@ func (c *Conn) advanceFrame() (int, error) { return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) } - // 3. Read and parse frame length. + // 3. Read and parse frame length as per + // https://tools.ietf.org/html/rfc6455#section-5.2 + // + // The length of the "Payload data", in bytes: if 0-125, that is the payload + // length. + // - If 126, the following 2 bytes interpreted as a 16-bit unsigned + // integer are the payload length. + // - If 127, the following 8 bytes interpreted as + // a 64-bit unsigned integer (the most significant bit MUST be 0) are the + // payload length. Multibyte length quantities are expressed in network byte + // order. switch c.readRemaining { case 126: @@ -834,13 +855,19 @@ func (c *Conn) advanceFrame() (int, error) { if err != nil { return noFrame, err } - c.readRemaining = int64(binary.BigEndian.Uint16(p)) + + if err := c.setReadRemaining(int64(binary.BigEndian.Uint16(p))); err != nil { + return noFrame, err + } case 127: p, err := c.read(8) if err != nil { return noFrame, err } - c.readRemaining = int64(binary.BigEndian.Uint64(p)) + + if err := c.setReadRemaining(int64(binary.BigEndian.Uint64(p))); err != nil { + return noFrame, err + } } // 4. Handle frame masking. @@ -863,6 +890,12 @@ func (c *Conn) advanceFrame() (int, error) { if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { c.readLength += c.readRemaining + // Don't allow readLength to overflow in the presence of a large readRemaining + // counter. + if c.readLength < 0 { + return noFrame, ErrReadLimit + } + if c.readLimit > 0 && c.readLength > c.readLimit { c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) return noFrame, ErrReadLimit @@ -876,7 +909,7 @@ func (c *Conn) advanceFrame() (int, error) { var payload []byte if c.readRemaining > 0 { payload, err = c.read(int(c.readRemaining)) - c.readRemaining = 0 + c.setReadRemaining(0) if err != nil { return noFrame, err } @@ -949,6 +982,7 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { c.readErr = hideTempErr(err) break } + if frameType == TextMessage || frameType == BinaryMessage { c.messageReader = &messageReader{c} c.reader = c.messageReader @@ -989,7 +1023,9 @@ func (r *messageReader) Read(b []byte) (int, error) { if c.isServer { c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) } - c.readRemaining -= int64(n) + rem := c.readRemaining + rem -= int64(n) + c.setReadRemaining(rem) if c.readRemaining > 0 && c.readErr == io.EOF { c.readErr = errUnexpectedEOF } @@ -1041,7 +1077,7 @@ func (c *Conn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) } -// SetReadLimit sets the maximum size for a message read from the peer. If a +// SetReadLimit sets the maximum size in bytes for a message read from the peer. If a // message exceeds the limit, the connection sends a close message to the peer // and returns ErrReadLimit to the application. func (c *Conn) SetReadLimit(limit int64) { diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go index dcce1a63..8db0cef9 100644 --- a/vendor/github.com/gorilla/websocket/doc.go +++ b/vendor/github.com/gorilla/websocket/doc.go @@ -151,6 +151,53 @@ // checking. The application is responsible for checking the Origin header // before calling the Upgrade function. // +// Buffers +// +// Connections buffer network input and output to reduce the number +// of system calls when reading or writing messages. +// +// Write buffers are also used for constructing WebSocket frames. See RFC 6455, +// Section 5 for a discussion of message framing. A WebSocket frame header is +// written to the network each time a write buffer is flushed to the network. +// Decreasing the size of the write buffer can increase the amount of framing +// overhead on the connection. +// +// The buffer sizes in bytes are specified by the ReadBufferSize and +// WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default +// size of 4096 when a buffer size field is set to zero. The Upgrader reuses +// buffers created by the HTTP server when a buffer size field is set to zero. +// The HTTP server buffers have a size of 4096 at the time of this writing. +// +// The buffer sizes do not limit the size of a message that can be read or +// written by a connection. +// +// Buffers are held for the lifetime of the connection by default. If the +// Dialer or Upgrader WriteBufferPool field is set, then a connection holds the +// write buffer only when writing a message. +// +// Applications should tune the buffer sizes to balance memory use and +// performance. Increasing the buffer size uses more memory, but can reduce the +// number of system calls to read or write the network. In the case of writing, +// increasing the buffer size can reduce the number of frame headers written to +// the network. +// +// Some guidelines for setting buffer parameters are: +// +// Limit the buffer sizes to the maximum expected message size. Buffers larger +// than the largest message do not provide any benefit. +// +// Depending on the distribution of message sizes, setting the buffer size to +// a value less than the maximum expected message size can greatly reduce memory +// use with a small impact on performance. Here's an example: If 99% of the +// messages are smaller than 256 bytes and the maximum message size is 512 +// bytes, then a buffer size of 256 bytes will result in 1.01 more system calls +// than a buffer size of 512 bytes. The memory savings is 50%. +// +// A write buffer pool is useful when the application has a modest number +// writes over a large number of connections. when buffers are pooled, a larger +// buffer size has a reduced impact on total memory use and has the benefit of +// reducing system calls and frame overhead. +// // Compression EXPERIMENTAL // // Per message compression extensions (RFC 7692) are experimentally supported diff --git a/vendor/github.com/gorilla/websocket/go.mod b/vendor/github.com/gorilla/websocket/go.mod new file mode 100644 index 00000000..1a7afd50 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/go.mod @@ -0,0 +1,3 @@ +module github.com/gorilla/websocket + +go 1.12 diff --git a/vendor/github.com/gorilla/websocket/go.sum b/vendor/github.com/gorilla/websocket/go.sum new file mode 100644 index 00000000..e69de29b diff --git a/vendor/github.com/gorilla/websocket/join.go b/vendor/github.com/gorilla/websocket/join.go new file mode 100644 index 00000000..c64f8c82 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/join.go @@ -0,0 +1,42 @@ +// Copyright 2019 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "io" + "strings" +) + +// JoinMessages concatenates received messages to create a single io.Reader. +// The string term is appended to each message. The returned reader does not +// support concurrent calls to the Read method. +func JoinMessages(c *Conn, term string) io.Reader { + return &joinReader{c: c, term: term} +} + +type joinReader struct { + c *Conn + term string + r io.Reader +} + +func (r *joinReader) Read(p []byte) (int, error) { + if r.r == nil { + var err error + _, r.r, err = r.c.NextReader() + if err != nil { + return 0, err + } + if r.term != "" { + r.r = io.MultiReader(r.r, strings.NewReader(r.term)) + } + } + n, err := r.r.Read(p) + if err == io.EOF { + err = nil + r.r = nil + } + return n, err +} diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go index 74ec565d..c854225e 100644 --- a/vendor/github.com/gorilla/websocket/prepared.go +++ b/vendor/github.com/gorilla/websocket/prepared.go @@ -73,8 +73,8 @@ func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { // Prepare a frame using a 'fake' connection. // TODO: Refactor code in conn.go to allow more direct construction of // the frame. - mu := make(chan bool, 1) - mu <- true + mu := make(chan struct{}, 1) + mu <- struct{}{} var nc prepareConn c := &Conn{ conn: &nc, diff --git a/vendor/github.com/gorilla/websocket/proxy.go b/vendor/github.com/gorilla/websocket/proxy.go index bf2478e4..e87a8c9f 100644 --- a/vendor/github.com/gorilla/websocket/proxy.go +++ b/vendor/github.com/gorilla/websocket/proxy.go @@ -22,18 +22,18 @@ func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { func init() { proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { - return &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil + return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil }) } type httpProxyDialer struct { - proxyURL *url.URL - fowardDial func(network, addr string) (net.Conn, error) + proxyURL *url.URL + forwardDial func(network, addr string) (net.Conn, error) } func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { hostPort, _ := hostPortNoPort(hpd.proxyURL) - conn, err := hpd.fowardDial(network, hostPort) + conn, err := hpd.forwardDial(network, hostPort) if err != nil { return nil, err } diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go index a761824b..887d5589 100644 --- a/vendor/github.com/gorilla/websocket/server.go +++ b/vendor/github.com/gorilla/websocket/server.go @@ -27,7 +27,7 @@ type Upgrader struct { // HandshakeTimeout specifies the duration for the handshake to complete. HandshakeTimeout time.Duration - // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer // size is zero, then buffers allocated by the HTTP server are used. The // I/O buffer sizes do not limit the size of the messages that can be sent // or received. @@ -153,7 +153,7 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade challengeKey := r.Header.Get("Sec-Websocket-Key") if challengeKey == "" { - return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-WebSocket-Key' header is missing or blank") + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank") } subprotocol := u.selectSubprotocol(r, responseHeader) diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go index 354001e1..7bf2f66c 100644 --- a/vendor/github.com/gorilla/websocket/util.go +++ b/vendor/github.com/gorilla/websocket/util.go @@ -31,68 +31,113 @@ func generateChallengeKey() (string, error) { return base64.StdEncoding.EncodeToString(p), nil } -// Octet types from RFC 2616. -var octetTypes [256]byte - -const ( - isTokenOctet = 1 << iota - isSpaceOctet -) - -func init() { - // From RFC 2616 - // - // OCTET = - // CHAR = - // CTL = - // CR = - // LF = - // SP = - // HT = - // <"> = - // CRLF = CR LF - // LWS = [CRLF] 1*( SP | HT ) - // TEXT = - // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> - // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT - // token = 1* - // qdtext = > - - for c := 0; c < 256; c++ { - var t byte - isCtl := c <= 31 || c == 127 - isChar := 0 <= c && c <= 127 - isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 - if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { - t |= isSpaceOctet - } - if isChar && !isCtl && !isSeparator { - t |= isTokenOctet - } - octetTypes[c] = t - } +// Token octets per RFC 2616. +var isTokenOctet = [256]bool{ + '!': true, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '*': true, + '+': true, + '-': true, + '.': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'W': true, + 'V': true, + 'X': true, + 'Y': true, + 'Z': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '|': true, + '~': true, } +// skipSpace returns a slice of the string s with all leading RFC 2616 linear +// whitespace removed. func skipSpace(s string) (rest string) { i := 0 for ; i < len(s); i++ { - if octetTypes[s[i]]&isSpaceOctet == 0 { + if b := s[i]; b != ' ' && b != '\t' { break } } return s[i:] } +// nextToken returns the leading RFC 2616 token of s and the string following +// the token. func nextToken(s string) (token, rest string) { i := 0 for ; i < len(s); i++ { - if octetTypes[s[i]]&isTokenOctet == 0 { + if !isTokenOctet[s[i]] { break } } return s[:i], s[i:] } +// nextTokenOrQuoted returns the leading token or quoted string per RFC 2616 +// and the string following the token or quoted string. func nextTokenOrQuoted(s string) (value string, rest string) { if !strings.HasPrefix(s, "\"") { return nextToken(s) @@ -128,7 +173,8 @@ func nextTokenOrQuoted(s string) (value string, rest string) { return "", "" } -// equalASCIIFold returns true if s is equal to t with ASCII case folding. +// equalASCIIFold returns true if s is equal to t with ASCII case folding as +// defined in RFC 4790. func equalASCIIFold(s, t string) bool { for s != "" && t != "" { sr, size := utf8.DecodeRuneInString(s) diff --git a/vendor/github.com/jpillora/backoff/backoff.go b/vendor/github.com/jpillora/backoff/backoff.go index b4941b6e..d113e689 100644 --- a/vendor/github.com/jpillora/backoff/backoff.go +++ b/vendor/github.com/jpillora/backoff/backoff.go @@ -4,6 +4,7 @@ package backoff import ( "math" "math/rand" + "sync/atomic" "time" ) @@ -14,19 +15,19 @@ import ( // Backoff is not generally concurrent-safe, but the ForAttempt method can // be used concurrently. type Backoff struct { - //Factor is the multiplying factor for each increment step - attempt, Factor float64 - //Jitter eases contention by randomizing backoff steps + attempt uint64 + // Factor is the multiplying factor for each increment step + Factor float64 + // Jitter eases contention by randomizing backoff steps Jitter bool - //Min and Max are the minimum and maximum values of the counter + // Min and Max are the minimum and maximum values of the counter Min, Max time.Duration } // Duration returns the duration for the current attempt before incrementing // the attempt counter. See ForAttempt. func (b *Backoff) Duration() time.Duration { - d := b.ForAttempt(b.attempt) - b.attempt++ + d := b.ForAttempt(float64(atomic.AddUint64(&b.attempt, 1) - 1)) return d } @@ -80,12 +81,12 @@ func (b *Backoff) ForAttempt(attempt float64) time.Duration { // Reset restarts the current attempt counter at zero. func (b *Backoff) Reset() { - b.attempt = 0 + atomic.StoreUint64(&b.attempt, 0) } // Attempt returns the current attempt counter value. func (b *Backoff) Attempt() float64 { - return b.attempt + return float64(atomic.LoadUint64(&b.attempt)) } // Copy returns a backoff with equals constraints as the original diff --git a/vendor/github.com/jpillora/backoff/go.mod b/vendor/github.com/jpillora/backoff/go.mod new file mode 100644 index 00000000..7c41bc6f --- /dev/null +++ b/vendor/github.com/jpillora/backoff/go.mod @@ -0,0 +1,3 @@ +module github.com/jpillora/backoff + +go 1.13 diff --git a/vendor/github.com/klauspost/cpuid/.travis.yml b/vendor/github.com/klauspost/cpuid/.travis.yml index 630192d5..4c759adb 100644 --- a/vendor/github.com/klauspost/cpuid/.travis.yml +++ b/vendor/github.com/klauspost/cpuid/.travis.yml @@ -6,9 +6,9 @@ os: - linux - osx go: - - 1.8.x - - 1.9.x - - 1.10.x + - 1.11.x + - 1.12.x + - 1.13.x - master script: diff --git a/vendor/github.com/klauspost/cpuid/README.md b/vendor/github.com/klauspost/cpuid/README.md index a7fb41fb..58c00f78 100644 --- a/vendor/github.com/klauspost/cpuid/README.md +++ b/vendor/github.com/klauspost/cpuid/README.md @@ -59,11 +59,21 @@ Package home: https://github.com/klauspost/cpuid * **AVX512BW** (AVX-512 Byte and Word Instructions) * **AVX512VL** (AVX-512 Vector Length Extensions) * **AVX512VBMI** (AVX-512 Vector Bit Manipulation Instructions) +* **AVX512VBMI2** (AVX-512 Vector Bit Manipulation Instructions, Version 2) +* **AVX512VNNI** (AVX-512 Vector Neural Network Instructions) +* **AVX512VPOPCNTDQ** (AVX-512 Vector Population Count Doubleword and Quadword) +* **GFNI** (Galois Field New Instructions) +* **VAES** (Vector AES) +* **AVX512BITALG** (AVX-512 Bit Algorithms) +* **VPCLMULQDQ** (Carry-Less Multiplication Quadword) +* **AVX512BF16** (AVX-512 BFLOAT16 Instructions) +* **AVX512VP2INTERSECT** (AVX-512 Intersect for D/Q) * **MPX** (Intel MPX (Memory Protection Extensions)) * **ERMS** (Enhanced REP MOVSB/STOSB) * **RDTSCP** (RDTSCP Instruction) * **CX16** (CMPXCHG16B Instruction) * **SGX** (Software Guard Extensions, with activation details) +* **VMX** (Virtual Machine Extensions) ## Performance * **RDTSCP()** Returns current cycle count. Can be used for benchmarking. diff --git a/vendor/github.com/klauspost/cpuid/cpuid.go b/vendor/github.com/klauspost/cpuid/cpuid.go index db959132..53cb48cb 100644 --- a/vendor/github.com/klauspost/cpuid/cpuid.go +++ b/vendor/github.com/klauspost/cpuid/cpuid.go @@ -31,55 +31,66 @@ const ( ) const ( - CMOV = 1 << iota // i686 CMOV - NX // NX (No-Execute) bit - AMD3DNOW // AMD 3DNOW - AMD3DNOWEXT // AMD 3DNowExt - MMX // standard MMX - MMXEXT // SSE integer functions or AMD MMX ext - SSE // SSE functions - SSE2 // P4 SSE functions - SSE3 // Prescott SSE3 functions - SSSE3 // Conroe SSSE3 functions - SSE4 // Penryn SSE4.1 functions - SSE4A // AMD Barcelona microarchitecture SSE4a instructions - SSE42 // Nehalem SSE4.2 functions - AVX // AVX functions - AVX2 // AVX2 functions - FMA3 // Intel FMA 3 - FMA4 // Bulldozer FMA4 functions - XOP // Bulldozer XOP functions - F16C // Half-precision floating-point conversion - BMI1 // Bit Manipulation Instruction Set 1 - BMI2 // Bit Manipulation Instruction Set 2 - TBM // AMD Trailing Bit Manipulation - LZCNT // LZCNT instruction - POPCNT // POPCNT instruction - AESNI // Advanced Encryption Standard New Instructions - CLMUL // Carry-less Multiplication - HTT // Hyperthreading (enabled) - HLE // Hardware Lock Elision - RTM // Restricted Transactional Memory - RDRAND // RDRAND instruction is available - RDSEED // RDSEED instruction is available - ADX // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) - SHA // Intel SHA Extensions - AVX512F // AVX-512 Foundation - AVX512DQ // AVX-512 Doubleword and Quadword Instructions - AVX512IFMA // AVX-512 Integer Fused Multiply-Add Instructions - AVX512PF // AVX-512 Prefetch Instructions - AVX512ER // AVX-512 Exponential and Reciprocal Instructions - AVX512CD // AVX-512 Conflict Detection Instructions - AVX512BW // AVX-512 Byte and Word Instructions - AVX512VL // AVX-512 Vector Length Extensions - AVX512VBMI // AVX-512 Vector Bit Manipulation Instructions - MPX // Intel MPX (Memory Protection Extensions) - ERMS // Enhanced REP MOVSB/STOSB - RDTSCP // RDTSCP Instruction - CX16 // CMPXCHG16B Instruction - SGX // Software Guard Extensions - IBPB // Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB) - STIBP // Single Thread Indirect Branch Predictors + CMOV = 1 << iota // i686 CMOV + NX // NX (No-Execute) bit + AMD3DNOW // AMD 3DNOW + AMD3DNOWEXT // AMD 3DNowExt + MMX // standard MMX + MMXEXT // SSE integer functions or AMD MMX ext + SSE // SSE functions + SSE2 // P4 SSE functions + SSE3 // Prescott SSE3 functions + SSSE3 // Conroe SSSE3 functions + SSE4 // Penryn SSE4.1 functions + SSE4A // AMD Barcelona microarchitecture SSE4a instructions + SSE42 // Nehalem SSE4.2 functions + AVX // AVX functions + AVX2 // AVX2 functions + FMA3 // Intel FMA 3 + FMA4 // Bulldozer FMA4 functions + XOP // Bulldozer XOP functions + F16C // Half-precision floating-point conversion + BMI1 // Bit Manipulation Instruction Set 1 + BMI2 // Bit Manipulation Instruction Set 2 + TBM // AMD Trailing Bit Manipulation + LZCNT // LZCNT instruction + POPCNT // POPCNT instruction + AESNI // Advanced Encryption Standard New Instructions + CLMUL // Carry-less Multiplication + HTT // Hyperthreading (enabled) + HLE // Hardware Lock Elision + RTM // Restricted Transactional Memory + RDRAND // RDRAND instruction is available + RDSEED // RDSEED instruction is available + ADX // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + SHA // Intel SHA Extensions + AVX512F // AVX-512 Foundation + AVX512DQ // AVX-512 Doubleword and Quadword Instructions + AVX512IFMA // AVX-512 Integer Fused Multiply-Add Instructions + AVX512PF // AVX-512 Prefetch Instructions + AVX512ER // AVX-512 Exponential and Reciprocal Instructions + AVX512CD // AVX-512 Conflict Detection Instructions + AVX512BW // AVX-512 Byte and Word Instructions + AVX512VL // AVX-512 Vector Length Extensions + AVX512VBMI // AVX-512 Vector Bit Manipulation Instructions + AVX512VBMI2 // AVX-512 Vector Bit Manipulation Instructions, Version 2 + AVX512VNNI // AVX-512 Vector Neural Network Instructions + AVX512VPOPCNTDQ // AVX-512 Vector Population Count Doubleword and Quadword + GFNI // Galois Field New Instructions + VAES // Vector AES + AVX512BITALG // AVX-512 Bit Algorithms + VPCLMULQDQ // Carry-Less Multiplication Quadword + AVX512BF16 // AVX-512 BFLOAT16 Instructions + AVX512VP2INTERSECT // AVX-512 Intersect for D/Q + MPX // Intel MPX (Memory Protection Extensions) + ERMS // Enhanced REP MOVSB/STOSB + RDTSCP // RDTSCP Instruction + CX16 // CMPXCHG16B Instruction + SGX // Software Guard Extensions + SGXLC // Software Guard Extensions Launch Control + IBPB // Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB) + STIBP // Single Thread Indirect Branch Predictors + VMX // Virtual Machine Extensions // Performance indicators SSE2SLOW // SSE2 is supported, but usually not faster @@ -88,55 +99,66 @@ const ( ) var flagNames = map[Flags]string{ - CMOV: "CMOV", // i686 CMOV - NX: "NX", // NX (No-Execute) bit - AMD3DNOW: "AMD3DNOW", // AMD 3DNOW - AMD3DNOWEXT: "AMD3DNOWEXT", // AMD 3DNowExt - MMX: "MMX", // Standard MMX - MMXEXT: "MMXEXT", // SSE integer functions or AMD MMX ext - SSE: "SSE", // SSE functions - SSE2: "SSE2", // P4 SSE2 functions - SSE3: "SSE3", // Prescott SSE3 functions - SSSE3: "SSSE3", // Conroe SSSE3 functions - SSE4: "SSE4.1", // Penryn SSE4.1 functions - SSE4A: "SSE4A", // AMD Barcelona microarchitecture SSE4a instructions - SSE42: "SSE4.2", // Nehalem SSE4.2 functions - AVX: "AVX", // AVX functions - AVX2: "AVX2", // AVX functions - FMA3: "FMA3", // Intel FMA 3 - FMA4: "FMA4", // Bulldozer FMA4 functions - XOP: "XOP", // Bulldozer XOP functions - F16C: "F16C", // Half-precision floating-point conversion - BMI1: "BMI1", // Bit Manipulation Instruction Set 1 - BMI2: "BMI2", // Bit Manipulation Instruction Set 2 - TBM: "TBM", // AMD Trailing Bit Manipulation - LZCNT: "LZCNT", // LZCNT instruction - POPCNT: "POPCNT", // POPCNT instruction - AESNI: "AESNI", // Advanced Encryption Standard New Instructions - CLMUL: "CLMUL", // Carry-less Multiplication - HTT: "HTT", // Hyperthreading (enabled) - HLE: "HLE", // Hardware Lock Elision - RTM: "RTM", // Restricted Transactional Memory - RDRAND: "RDRAND", // RDRAND instruction is available - RDSEED: "RDSEED", // RDSEED instruction is available - ADX: "ADX", // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) - SHA: "SHA", // Intel SHA Extensions - AVX512F: "AVX512F", // AVX-512 Foundation - AVX512DQ: "AVX512DQ", // AVX-512 Doubleword and Quadword Instructions - AVX512IFMA: "AVX512IFMA", // AVX-512 Integer Fused Multiply-Add Instructions - AVX512PF: "AVX512PF", // AVX-512 Prefetch Instructions - AVX512ER: "AVX512ER", // AVX-512 Exponential and Reciprocal Instructions - AVX512CD: "AVX512CD", // AVX-512 Conflict Detection Instructions - AVX512BW: "AVX512BW", // AVX-512 Byte and Word Instructions - AVX512VL: "AVX512VL", // AVX-512 Vector Length Extensions - AVX512VBMI: "AVX512VBMI", // AVX-512 Vector Bit Manipulation Instructions - MPX: "MPX", // Intel MPX (Memory Protection Extensions) - ERMS: "ERMS", // Enhanced REP MOVSB/STOSB - RDTSCP: "RDTSCP", // RDTSCP Instruction - CX16: "CX16", // CMPXCHG16B Instruction - SGX: "SGX", // Software Guard Extensions - IBPB: "IBPB", // Indirect Branch Restricted Speculation and Indirect Branch Predictor Barrier - STIBP: "STIBP", // Single Thread Indirect Branch Predictors + CMOV: "CMOV", // i686 CMOV + NX: "NX", // NX (No-Execute) bit + AMD3DNOW: "AMD3DNOW", // AMD 3DNOW + AMD3DNOWEXT: "AMD3DNOWEXT", // AMD 3DNowExt + MMX: "MMX", // Standard MMX + MMXEXT: "MMXEXT", // SSE integer functions or AMD MMX ext + SSE: "SSE", // SSE functions + SSE2: "SSE2", // P4 SSE2 functions + SSE3: "SSE3", // Prescott SSE3 functions + SSSE3: "SSSE3", // Conroe SSSE3 functions + SSE4: "SSE4.1", // Penryn SSE4.1 functions + SSE4A: "SSE4A", // AMD Barcelona microarchitecture SSE4a instructions + SSE42: "SSE4.2", // Nehalem SSE4.2 functions + AVX: "AVX", // AVX functions + AVX2: "AVX2", // AVX functions + FMA3: "FMA3", // Intel FMA 3 + FMA4: "FMA4", // Bulldozer FMA4 functions + XOP: "XOP", // Bulldozer XOP functions + F16C: "F16C", // Half-precision floating-point conversion + BMI1: "BMI1", // Bit Manipulation Instruction Set 1 + BMI2: "BMI2", // Bit Manipulation Instruction Set 2 + TBM: "TBM", // AMD Trailing Bit Manipulation + LZCNT: "LZCNT", // LZCNT instruction + POPCNT: "POPCNT", // POPCNT instruction + AESNI: "AESNI", // Advanced Encryption Standard New Instructions + CLMUL: "CLMUL", // Carry-less Multiplication + HTT: "HTT", // Hyperthreading (enabled) + HLE: "HLE", // Hardware Lock Elision + RTM: "RTM", // Restricted Transactional Memory + RDRAND: "RDRAND", // RDRAND instruction is available + RDSEED: "RDSEED", // RDSEED instruction is available + ADX: "ADX", // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + SHA: "SHA", // Intel SHA Extensions + AVX512F: "AVX512F", // AVX-512 Foundation + AVX512DQ: "AVX512DQ", // AVX-512 Doubleword and Quadword Instructions + AVX512IFMA: "AVX512IFMA", // AVX-512 Integer Fused Multiply-Add Instructions + AVX512PF: "AVX512PF", // AVX-512 Prefetch Instructions + AVX512ER: "AVX512ER", // AVX-512 Exponential and Reciprocal Instructions + AVX512CD: "AVX512CD", // AVX-512 Conflict Detection Instructions + AVX512BW: "AVX512BW", // AVX-512 Byte and Word Instructions + AVX512VL: "AVX512VL", // AVX-512 Vector Length Extensions + AVX512VBMI: "AVX512VBMI", // AVX-512 Vector Bit Manipulation Instructions + AVX512VBMI2: "AVX512VBMI2", // AVX-512 Vector Bit Manipulation Instructions, Version 2 + AVX512VNNI: "AVX512VNNI", // AVX-512 Vector Neural Network Instructions + AVX512VPOPCNTDQ: "AVX512VPOPCNTDQ", // AVX-512 Vector Population Count Doubleword and Quadword + GFNI: "GFNI", // Galois Field New Instructions + VAES: "VAES", // Vector AES + AVX512BITALG: "AVX512BITALG", // AVX-512 Bit Algorithms + VPCLMULQDQ: "VPCLMULQDQ", // Carry-Less Multiplication Quadword + AVX512BF16: "AVX512BF16", // AVX-512 BFLOAT16 Instruction + AVX512VP2INTERSECT: "AVX512VP2INTERSECT", // AVX-512 Intersect for D/Q + MPX: "MPX", // Intel MPX (Memory Protection Extensions) + ERMS: "ERMS", // Enhanced REP MOVSB/STOSB + RDTSCP: "RDTSCP", // RDTSCP Instruction + CX16: "CX16", // CMPXCHG16B Instruction + SGX: "SGX", // Software Guard Extensions + SGXLC: "SGXLC", // Software Guard Extensions Launch Control + IBPB: "IBPB", // Indirect Branch Restricted Speculation and Indirect Branch Predictor Barrier + STIBP: "STIBP", // Single Thread Indirect Branch Predictors + VMX: "VMX", // Virtual Machine Extensions // Performance indicators SSE2SLOW: "SSE2SLOW", // SSE2 supported, but usually not faster @@ -198,7 +220,7 @@ func Detect() { CPU.CacheLine = cacheLine() CPU.Family, CPU.Model = familyModel() CPU.Features = support() - CPU.SGX = hasSGX(CPU.Features&SGX != 0) + CPU.SGX = hasSGX(CPU.Features&SGX != 0, CPU.Features&SGXLC != 0) CPU.ThreadsPerCore = threadsPerCore() CPU.LogicalCores = logicalCores() CPU.PhysicalCores = physicalCores() @@ -223,6 +245,11 @@ func (c CPUInfo) Amd3dnowExt() bool { return c.Features&AMD3DNOWEXT != 0 } +// VMX indicates support of VMX +func (c CPUInfo) VMX() bool { + return c.Features&VMX != 0 +} + // MMX indicates support of MMX instructions func (c CPUInfo) MMX() bool { return c.Features&MMX != 0 @@ -433,6 +460,51 @@ func (c CPUInfo) AVX512VBMI() bool { return c.Features&AVX512VBMI != 0 } +// AVX512VBMI2 indicates support of AVX-512 Vector Bit Manipulation Instructions, Version 2 +func (c CPUInfo) AVX512VBMI2() bool { + return c.Features&AVX512VBMI2 != 0 +} + +// AVX512VNNI indicates support of AVX-512 Vector Neural Network Instructions +func (c CPUInfo) AVX512VNNI() bool { + return c.Features&AVX512VNNI != 0 +} + +// AVX512VPOPCNTDQ indicates support of AVX-512 Vector Population Count Doubleword and Quadword +func (c CPUInfo) AVX512VPOPCNTDQ() bool { + return c.Features&AVX512VPOPCNTDQ != 0 +} + +// GFNI indicates support of Galois Field New Instructions +func (c CPUInfo) GFNI() bool { + return c.Features&GFNI != 0 +} + +// VAES indicates support of Vector AES +func (c CPUInfo) VAES() bool { + return c.Features&VAES != 0 +} + +// AVX512BITALG indicates support of AVX-512 Bit Algorithms +func (c CPUInfo) AVX512BITALG() bool { + return c.Features&AVX512BITALG != 0 +} + +// VPCLMULQDQ indicates support of Carry-Less Multiplication Quadword +func (c CPUInfo) VPCLMULQDQ() bool { + return c.Features&VPCLMULQDQ != 0 +} + +// AVX512BF16 indicates support of +func (c CPUInfo) AVX512BF16() bool { + return c.Features&AVX512BF16 != 0 +} + +// AVX512VP2INTERSECT indicates support of +func (c CPUInfo) AVX512VP2INTERSECT() bool { + return c.Features&AVX512VP2INTERSECT != 0 +} + // MPX indicates support of Intel MPX (Memory Protection Extensions) func (c CPUInfo) MPX() bool { return c.Features&MPX != 0 @@ -770,26 +842,53 @@ func (c *CPUInfo) cacheSize() { return } +type SGXEPCSection struct { + BaseAddress uint64 + EPCSize uint64 +} + type SGXSupport struct { Available bool + LaunchControl bool SGX1Supported bool SGX2Supported bool MaxEnclaveSizeNot64 int64 MaxEnclaveSize64 int64 + EPCSections []SGXEPCSection } -func hasSGX(available bool) (rval SGXSupport) { +func hasSGX(available, lc bool) (rval SGXSupport) { rval.Available = available if !available { return } + rval.LaunchControl = lc + a, _, _, d := cpuidex(0x12, 0) rval.SGX1Supported = a&0x01 != 0 rval.SGX2Supported = a&0x02 != 0 rval.MaxEnclaveSizeNot64 = 1 << (d & 0xFF) // pow 2 rval.MaxEnclaveSize64 = 1 << ((d >> 8) & 0xFF) // pow 2 + rval.EPCSections = make([]SGXEPCSection, 0) + + for subleaf := uint32(2); subleaf < 2+8; subleaf++ { + eax, ebx, ecx, edx := cpuidex(0x12, subleaf) + leafType := eax & 0xf + + if leafType == 0 { + // Invalid subleaf, stop iterating + break + } else if leafType == 1 { + // EPC Section subleaf + baseAddress := uint64(eax&0xfffff000) + (uint64(ebx&0x000fffff) << 32) + size := uint64(ecx&0xfffff000) + (uint64(edx&0x000fffff) << 32) + + section := SGXEPCSection{BaseAddress: baseAddress, EPCSize: size} + rval.EPCSections = append(rval.EPCSections, section) + } + } return } @@ -820,6 +919,9 @@ func support() Flags { if (c & 1) != 0 { rval |= SSE3 } + if (c & (1 << 5)) != 0 { + rval |= VMX + } if (c & 0x00000200) != 0 { rval |= SSSE3 } @@ -868,6 +970,7 @@ func support() Flags { // Check AVX2, AVX2 requires OS support, but BMI1/2 don't. if mfi >= 7 { _, ebx, ecx, edx := cpuidex(7, 0) + eax1, _, _, _ := cpuidex(7, 1) if (rval&AVX) != 0 && (ebx&0x00000020) != 0 { rval |= AVX2 } @@ -904,6 +1007,9 @@ func support() Flags { if edx&(1<<26) != 0 { rval |= IBPB } + if ecx&(1<<30) != 0 { + rval |= SGXLC + } if edx&(1<<27) != 0 { rval |= STIBP } @@ -945,6 +1051,35 @@ func support() Flags { if ecx&(1<<1) != 0 { rval |= AVX512VBMI } + if ecx&(1<<6) != 0 { + rval |= AVX512VBMI2 + } + if ecx&(1<<8) != 0 { + rval |= GFNI + } + if ecx&(1<<9) != 0 { + rval |= VAES + } + if ecx&(1<<10) != 0 { + rval |= VPCLMULQDQ + } + if ecx&(1<<11) != 0 { + rval |= AVX512VNNI + } + if ecx&(1<<12) != 0 { + rval |= AVX512BITALG + } + if ecx&(1<<14) != 0 { + rval |= AVX512VPOPCNTDQ + } + // edx + if edx&(1<<8) != 0 { + rval |= AVX512VP2INTERSECT + } + // cpuid eax 07h,ecx=1 + if eax1&(1<<5) != 0 { + rval |= AVX512BF16 + } } } } diff --git a/vendor/github.com/mailru/easyjson/.gitignore b/vendor/github.com/mailru/easyjson/.gitignore index 26156fb4..fbfaf7a3 100644 --- a/vendor/github.com/mailru/easyjson/.gitignore +++ b/vendor/github.com/mailru/easyjson/.gitignore @@ -3,3 +3,4 @@ *.iml .idea *.swp +bin/* diff --git a/vendor/github.com/mailru/easyjson/.travis.yml b/vendor/github.com/mailru/easyjson/.travis.yml index 77891411..0ececa07 100644 --- a/vendor/github.com/mailru/easyjson/.travis.yml +++ b/vendor/github.com/mailru/easyjson/.travis.yml @@ -2,8 +2,11 @@ language: go go: - tip + - stable + +matrix: + allow_failures: + - go: tip + install: - - go get github.com/ugorji/go/codec - - go get github.com/pquerna/ffjson/fflib/v1 - - go get github.com/json-iterator/go - go get golang.org/x/lint/golint diff --git a/vendor/github.com/mailru/easyjson/Makefile b/vendor/github.com/mailru/easyjson/Makefile index 1604dfa9..80449f0d 100644 --- a/vendor/github.com/mailru/easyjson/Makefile +++ b/vendor/github.com/mailru/easyjson/Makefile @@ -1,64 +1,56 @@ -PKG=github.com/mailru/easyjson -GOPATH:=$(PWD)/.root:$(GOPATH) -export GOPATH - all: test -.root/src/$(PKG): - mkdir -p $@ - for i in $$PWD/* ; do ln -s $$i $@/`basename $$i` ; done - -root: .root/src/$(PKG) - clean: - rm -rf .root + rm -rf bin rm -rf tests/*_easyjson.go + rm -rf benchmark/*_easyjson.go build: - go build -i -o .root/bin/easyjson $(PKG)/easyjson + go build -i -o ./bin/easyjson ./easyjson -generate: root build - .root/bin/easyjson -stubs \ - .root/src/$(PKG)/tests/snake.go \ - .root/src/$(PKG)/tests/data.go \ - .root/src/$(PKG)/tests/omitempty.go \ - .root/src/$(PKG)/tests/nothing.go \ - .root/src/$(PKG)/tests/named_type.go \ - .root/src/$(PKG)/tests/custom_map_key_type.go \ - .root/src/$(PKG)/tests/embedded_type.go \ - .root/src/$(PKG)/tests/reference_to_pointer.go \ +generate: build + bin/easyjson -stubs \ + ./tests/snake.go \ + ./tests/data.go \ + ./tests/omitempty.go \ + ./tests/nothing.go \ + ./tests/named_type.go \ + ./tests/custom_map_key_type.go \ + ./tests/embedded_type.go \ + ./tests/reference_to_pointer.go \ + ./tests/html.go \ + ./tests/unknown_fields.go \ - .root/bin/easyjson -all .root/src/$(PKG)/tests/data.go - .root/bin/easyjson -all .root/src/$(PKG)/tests/nothing.go - .root/bin/easyjson -all .root/src/$(PKG)/tests/errors.go - .root/bin/easyjson -snake_case .root/src/$(PKG)/tests/snake.go - .root/bin/easyjson -omit_empty .root/src/$(PKG)/tests/omitempty.go - .root/bin/easyjson -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go - .root/bin/easyjson .root/src/$(PKG)/tests/nested_easy.go - .root/bin/easyjson .root/src/$(PKG)/tests/named_type.go - .root/bin/easyjson .root/src/$(PKG)/tests/custom_map_key_type.go - .root/bin/easyjson .root/src/$(PKG)/tests/embedded_type.go - .root/bin/easyjson .root/src/$(PKG)/tests/reference_to_pointer.go - .root/bin/easyjson .root/src/$(PKG)/tests/key_marshaler_map.go - .root/bin/easyjson -disallow_unknown_fields .root/src/$(PKG)/tests/disallow_unknown.go + bin/easyjson -all ./tests/data.go + bin/easyjson -all ./tests/nothing.go + bin/easyjson -all ./tests/errors.go + bin/easyjson -all ./tests/html.go + bin/easyjson -snake_case ./tests/snake.go + bin/easyjson -omit_empty ./tests/omitempty.go + bin/easyjson -build_tags=use_easyjson ./benchmark/data.go + bin/easyjson ./tests/nested_easy.go + bin/easyjson ./tests/named_type.go + bin/easyjson ./tests/custom_map_key_type.go + bin/easyjson ./tests/embedded_type.go + bin/easyjson ./tests/reference_to_pointer.go + bin/easyjson ./tests/key_marshaler_map.go + bin/easyjson -disallow_unknown_fields ./tests/disallow_unknown.go + bin/easyjson ./tests/unknown_fields.go -test: generate root +test: generate go test \ - $(PKG)/tests \ - $(PKG)/jlexer \ - $(PKG)/gen \ - $(PKG)/buffer - go test -benchmem -tags use_easyjson -bench . $(PKG)/benchmark - golint -set_exit_status .root/src/$(PKG)/tests/*_easyjson.go + ./tests \ + ./jlexer \ + ./gen \ + ./buffer + cd benchmark && go test -benchmem -tags use_easyjson -bench . + golint -set_exit_status ./tests/*_easyjson.go -bench-other: generate root - @go test -benchmem -bench . $(PKG)/benchmark - @go test -benchmem -tags use_ffjson -bench . $(PKG)/benchmark - @go test -benchmem -tags use_jsoniter -bench . $(PKG)/benchmark - @go test -benchmem -tags use_codec -bench . $(PKG)/benchmark +bench-other: generate + cd benchmark && make bench-python: benchmark/ujson.sh -.PHONY: root clean generate test build +.PHONY: clean generate test build diff --git a/vendor/github.com/mailru/easyjson/README.md b/vendor/github.com/mailru/easyjson/README.md index 7fd76865..3bdcf2d0 100644 --- a/vendor/github.com/mailru/easyjson/README.md +++ b/vendor/github.com/mailru/easyjson/README.md @@ -173,6 +173,9 @@ for more information. typically for many uses/protocols the final, marshaled length of the JSON needs to be known prior to sending the data. Currently this is not possible with easyjson's architecture. + +* easyjson parser and codegen based on reflection, so it wont works on `package main` + files, because they cant be imported by parser. ## Benchmarks diff --git a/vendor/github.com/mailru/easyjson/go.mod b/vendor/github.com/mailru/easyjson/go.mod new file mode 100644 index 00000000..7bc4a658 --- /dev/null +++ b/vendor/github.com/mailru/easyjson/go.mod @@ -0,0 +1,3 @@ +module github.com/mailru/easyjson + +go 1.12 diff --git a/vendor/github.com/mailru/easyjson/helpers.go b/vendor/github.com/mailru/easyjson/helpers.go index b86b87d2..04ac6356 100644 --- a/vendor/github.com/mailru/easyjson/helpers.go +++ b/vendor/github.com/mailru/easyjson/helpers.go @@ -26,6 +26,16 @@ type Optional interface { IsDefined() bool } +// UnknownsUnmarshaler provides a method to unmarshal unknown struct fileds and save them as you want +type UnknownsUnmarshaler interface { + UnmarshalUnknown(in *jlexer.Lexer, key string) +} + +// UnknownsMarshaler provides a method to write additional struct fields +type UnknownsMarshaler interface { + MarshalUnknowns(w *jwriter.Writer, first bool) +} + // Marshal returns data as a single byte slice. Method is suboptimal as the data is likely to be copied // from a chain of smaller chunks. func Marshal(v Marshaler) ([]byte, error) { diff --git a/vendor/github.com/mailru/easyjson/jlexer/lexer.go b/vendor/github.com/mailru/easyjson/jlexer/lexer.go index ef640751..ddd376b8 100644 --- a/vendor/github.com/mailru/easyjson/jlexer/lexer.go +++ b/vendor/github.com/mailru/easyjson/jlexer/lexer.go @@ -1152,7 +1152,7 @@ func (r *Lexer) Interface() interface{} { } else if r.token.delimValue == '[' { r.consume() - var ret []interface{} + ret := []interface{}{} for !r.IsDelim(']') { ret = append(ret, r.Interface()) r.WantComma() diff --git a/vendor/github.com/mailru/easyjson/jwriter/writer.go b/vendor/github.com/mailru/easyjson/jwriter/writer.go index b9ed7cca..eb8547cc 100644 --- a/vendor/github.com/mailru/easyjson/jwriter/writer.go +++ b/vendor/github.com/mailru/easyjson/jwriter/writer.go @@ -270,16 +270,25 @@ func (w *Writer) Bool(v bool) { const chars = "0123456789abcdef" -func isNotEscapedSingleChar(c byte, escapeHTML bool) bool { - // Note: might make sense to use a table if there are more chars to escape. With 4 chars - // it benchmarks the same. - if escapeHTML { - return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf - } else { - return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf +func getTable(falseValues ...int) [128]bool { + table := [128]bool{} + + for i := 0; i < 128; i++ { + table[i] = true } + + for _, v := range falseValues { + table[v] = false + } + + return table } +var ( + htmlEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '&', '<', '>', '\\') + htmlNoEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '\\') +) + func (w *Writer) String(s string) { w.Buffer.AppendByte('"') @@ -288,15 +297,23 @@ func (w *Writer) String(s string) { p := 0 // last non-escape symbol + var escapeTable [128]bool + if w.NoEscapeHTML { + escapeTable = htmlNoEscapeTable + } else { + escapeTable = htmlEscapeTable + } + for i := 0; i < len(s); { c := s[i] - if isNotEscapedSingleChar(c, !w.NoEscapeHTML) { - // single-width character, no escaping is required - i++ - continue - } else if c < utf8.RuneSelf { - // single-with character, need to escape + if c < utf8.RuneSelf { + if escapeTable[c] { + // single-width character, no escaping is required + i++ + continue + } + w.Buffer.AppendString(s[p:i]) switch c { case '\t': diff --git a/vendor/github.com/mailru/easyjson/unknown_fields.go b/vendor/github.com/mailru/easyjson/unknown_fields.go new file mode 100644 index 00000000..6cfdf830 --- /dev/null +++ b/vendor/github.com/mailru/easyjson/unknown_fields.go @@ -0,0 +1,34 @@ +package easyjson + +import ( + json "encoding/json" + + jlexer "github.com/mailru/easyjson/jlexer" + "github.com/mailru/easyjson/jwriter" +) + +// UnknownFieldsProxy implemets UnknownsUnmarshaler and UnknownsMarshaler +// use it as embedded field in your structure to parse and then serialize unknown struct fields +type UnknownFieldsProxy struct { + unknownFields map[string]interface{} +} + +func (s *UnknownFieldsProxy) UnmarshalUnknown(in *jlexer.Lexer, key string) { + if s.unknownFields == nil { + s.unknownFields = make(map[string]interface{}, 1) + } + s.unknownFields[key] = in.Interface() +} + +func (s UnknownFieldsProxy) MarshalUnknowns(out *jwriter.Writer, first bool) { + for key, val := range s.unknownFields { + if first { + first = false + } else { + out.RawByte(',') + } + out.String(string(key)) + out.RawByte(':') + out.Raw(json.Marshal(val)) + } +} diff --git a/vendor/github.com/mholt/certmagic/.travis.yml b/vendor/github.com/mholt/certmagic/.travis.yml deleted file mode 100644 index 9dfaec42..00000000 --- a/vendor/github.com/mholt/certmagic/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: go - -go: - - 1.x - - tip - -matrix: - allow_failures: - - go: tip - fast_finish: true - -install: - - go get -t ./... - - go get golang.org/x/lint/golint - - go get github.com/alecthomas/gometalinter - -script: - - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.15.0 - - golangci-lint run --disable-all -E vet -E gofmt -E misspell -E ineffassign -E goimports -E deadcode --tests - - go test -race ./... - -after_script: - - golint ./... diff --git a/vendor/github.com/mholt/certmagic/appveyor.yml b/vendor/github.com/mholt/certmagic/appveyor.yml deleted file mode 100644 index 2ab77bec..00000000 --- a/vendor/github.com/mholt/certmagic/appveyor.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: "{build}" - -clone_folder: c:\gopath\src\github.com\mholt\certmagic - -environment: - GOPATH: c:\gopath - -stack: go 1.11 - -install: - - set PATH=%GOPATH%\bin;%PATH% - - set PATH=C:\msys64\mingw64\bin;%PATH% - - go get -t ./... - - go get golang.org/x/lint/golint - - go get github.com/alecthomas/gometalinter - -build: off - -before_test: - - go version - - go env - -test_script: - - go test -race ./... - -after_test: - - golint ./... - -deploy: off diff --git a/vendor/github.com/mholt/certmagic/client.go b/vendor/github.com/mholt/certmagic/client.go deleted file mode 100644 index b1e417a3..00000000 --- a/vendor/github.com/mholt/certmagic/client.go +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package certmagic - -import ( - "fmt" - "log" - "net" - "net/url" - "strings" - "sync" - "time" - - "github.com/go-acme/lego/certificate" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/challenge/http01" - "github.com/go-acme/lego/challenge/tlsalpn01" - "github.com/go-acme/lego/lego" - "github.com/go-acme/lego/registration" -) - -// acmeMu ensures that only one ACME challenge occurs at a time. -var acmeMu sync.Mutex - -// acmeClient is a wrapper over acme.Client with -// some custom state attached. It is used to obtain, -// renew, and revoke certificates with ACME. -type acmeClient struct { - config *Config - acmeClient *lego.Client -} - -// listenerAddressInUse returns true if a TCP connection -// can be made to addr within a short time interval. -func listenerAddressInUse(addr string) bool { - conn, err := net.DialTimeout("tcp", addr, 250*time.Millisecond) - if err == nil { - conn.Close() - } - return err == nil -} - -func (cfg *Config) newManager(interactive bool) (Manager, error) { - if cfg.NewManager != nil { - return cfg.NewManager(interactive) - } - return cfg.newACMEClient(interactive) -} - -func (cfg *Config) newACMEClient(interactive bool) (*acmeClient, error) { - // look up or create the user account - leUser, err := cfg.getUser(cfg.Email) - if err != nil { - return nil, err - } - - // ensure key type and timeout are set - keyType := cfg.KeyType - if keyType == "" { - keyType = Default.KeyType - } - certObtainTimeout := cfg.CertObtainTimeout - if certObtainTimeout == 0 { - certObtainTimeout = Default.CertObtainTimeout - } - - // ensure CA URL (directory endpoint) is set - caURL := Default.CA - if cfg.CA != "" { - caURL = cfg.CA - } - - // ensure endpoint is secure (assume HTTPS if scheme is missing) - if !strings.Contains(caURL, "://") { - caURL = "https://" + caURL - } - u, err := url.Parse(caURL) - if err != nil { - return nil, err - } - - if u.Scheme != "https" && !isLoopback(u.Host) && !isInternal(u.Host) { - return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) - } - - clientKey := caURL + leUser.Email + string(keyType) - - // if an underlying client with this configuration already exists, reuse it - // TODO: Could this be a global cache instead, perhaps? - cfg.acmeClientsMu.Lock() - client, ok := cfg.acmeClients[clientKey] - if !ok { - // the client facilitates our communication with the CA server - legoCfg := lego.NewConfig(&leUser) - legoCfg.CADirURL = caURL - legoCfg.UserAgent = buildUAString() - legoCfg.HTTPClient.Timeout = HTTPTimeout - legoCfg.Certificate = lego.CertificateConfig{ - KeyType: keyType, - Timeout: certObtainTimeout, - } - client, err = lego.NewClient(legoCfg) - if err != nil { - cfg.acmeClientsMu.Unlock() - return nil, err - } - cfg.acmeClients[clientKey] = client - } - cfg.acmeClientsMu.Unlock() - - // if not registered, the user must register an account - // with the CA and agree to terms - if leUser.Registration == nil { - if interactive { // can't prompt a user who isn't there - termsURL := client.GetToSURL() - if !cfg.Agreed && termsURL != "" { - cfg.Agreed = cfg.askUserAgreement(client.GetToSURL()) - } - if !cfg.Agreed && termsURL != "" { - return nil, fmt.Errorf("user must agree to CA terms") - } - } - - reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: cfg.Agreed}) - if err != nil { - return nil, fmt.Errorf("registration error: %v", err) - } - leUser.Registration = reg - - // persist the user to storage - err = cfg.saveUser(leUser) - if err != nil { - return nil, fmt.Errorf("could not save user: %v", err) - } - } - - c := &acmeClient{ - config: cfg, - acmeClient: client, - } - - if cfg.DNSProvider == nil { - // Use HTTP and TLS-ALPN challenges by default - - // figure out which ports we'll be serving the challenges on - useHTTPPort := HTTPChallengePort - useTLSALPNPort := TLSALPNChallengePort - if HTTPPort > 0 && HTTPPort != HTTPChallengePort { - useHTTPPort = HTTPPort - } - if HTTPSPort > 0 && HTTPSPort != TLSALPNChallengePort { - useTLSALPNPort = HTTPSPort - } - if cfg.AltHTTPPort > 0 { - useHTTPPort = cfg.AltHTTPPort - } - if cfg.AltTLSALPNPort > 0 { - useTLSALPNPort = cfg.AltTLSALPNPort - } - - // If this machine is already listening on the HTTP or TLS-ALPN port - // designated for the challenges, then we need to handle the challenges - // a little differently: for HTTP, we will answer the challenge request - // using our own HTTP handler (the HandleHTTPChallenge function - this - // works only because challenge info is written to storage associated - // with cfg when the challenge is initiated); for TLS-ALPN, we will add - // the challenge cert to our cert cache and serve it up during the - // handshake. As for the default solvers... we are careful to honor the - // listener bind preferences by using cfg.ListenHost. - var httpSolver, alpnSolver challenge.Provider - httpSolver = http01.NewProviderServer(cfg.ListenHost, fmt.Sprintf("%d", useHTTPPort)) - alpnSolver = tlsalpn01.NewProviderServer(cfg.ListenHost, fmt.Sprintf("%d", useTLSALPNPort)) - if listenerAddressInUse(net.JoinHostPort(cfg.ListenHost, fmt.Sprintf("%d", useHTTPPort))) { - httpSolver = nil - } - if listenerAddressInUse(net.JoinHostPort(cfg.ListenHost, fmt.Sprintf("%d", useTLSALPNPort))) { - alpnSolver = tlsALPNSolver{certCache: cfg.certCache} - } - - // because of our nifty Storage interface, we can distribute the HTTP and - // TLS-ALPN challenges across all instances that share the same storage - - // in fact, this is required now for successful solving of the HTTP challenge - // if the port is already in use, since we must write the challenge info - // to storage for the HTTPChallengeHandler to solve it successfully - c.acmeClient.Challenge.SetHTTP01Provider(distributedSolver{ - config: cfg, - providerServer: httpSolver, - }) - c.acmeClient.Challenge.SetTLSALPN01Provider(distributedSolver{ - config: cfg, - providerServer: alpnSolver, - }) - - // disable any challenges that should not be used - if cfg.DisableHTTPChallenge { - c.acmeClient.Challenge.Remove(challenge.HTTP01) - } - if cfg.DisableTLSALPNChallenge { - c.acmeClient.Challenge.Remove(challenge.TLSALPN01) - } - } else { - // Otherwise, use DNS challenge exclusively - c.acmeClient.Challenge.Remove(challenge.HTTP01) - c.acmeClient.Challenge.Remove(challenge.TLSALPN01) - c.acmeClient.Challenge.SetDNS01Provider(cfg.DNSProvider) - } - - return c, nil -} - -// lockKey returns a key for a lock that is specific to the operation -// named op being performed related to domainName and this config's CA. -func (cfg *Config) lockKey(op, domainName string) string { - return fmt.Sprintf("%s_%s_%s", op, domainName, cfg.CA) -} - -// Obtain obtains a single certificate for name. It stores the certificate -// on the disk if successful. This function is safe for concurrent use. -// -// Our storage mechanism only supports one name per certificate, so this -// function (along with Renew and Revoke) only accepts one domain as input. -// It could be easily modified to support SAN certificates if our storage -// mechanism is upgraded later, but that will increase logical complexity -// in other areas. -// -// Callers who have access to a Config value should use the ObtainCert -// method on that instead of this lower-level method. -func (c *acmeClient) Obtain(name string) error { - // ensure idempotency of the obtain operation for this name - lockKey := c.config.lockKey("cert_acme", name) - err := c.config.Storage.Lock(lockKey) - if err != nil { - return err - } - defer func() { - if err := c.config.Storage.Unlock(lockKey); err != nil { - log.Printf("[ERROR][%s] Obtain: Unable to unlock '%s': %v", name, lockKey, err) - } - }() - - // check if obtain is still needed -- might have - // been obtained during lock - if c.config.storageHasCertResources(name) { - log.Printf("[INFO][%s] Obtain: Certificate already exists in storage", name) - return nil - } - - for attempts := 0; attempts < 2; attempts++ { - request := certificate.ObtainRequest{ - Domains: []string{name}, - Bundle: true, - MustStaple: c.config.MustStaple, - } - acmeMu.Lock() - certificate, err := c.acmeClient.Certificate.Obtain(request) - acmeMu.Unlock() - if err != nil { - return fmt.Errorf("[%s] failed to obtain certificate: %s", name, err) - } - - // double-check that we actually got a certificate, in case there's a bug upstream (see issue mholt/caddy#2121) - if certificate.Domain == "" || certificate.Certificate == nil { - return fmt.Errorf("returned certificate was empty; probably an unchecked error obtaining it") - } - - // Success - immediately save the certificate resource - err = c.config.saveCertResource(certificate) - if err != nil { - return fmt.Errorf("error saving assets for %v: %v", name, err) - } - - break - } - - if c.config.OnEvent != nil { - c.config.OnEvent("acme_cert_obtained", name) - } - - return nil -} - -// Renew renews the managed certificate for name. It puts the renewed -// certificate into storage (not the cache). This function is safe for -// concurrent use. -// -// Callers who have access to a Config value should use the RenewCert -// method on that instead of this lower-level method. -func (c *acmeClient) Renew(name string) error { - // ensure idempotency of the renew operation for this name - lockKey := c.config.lockKey("cert_acme", name) - err := c.config.Storage.Lock(lockKey) - if err != nil { - return err - } - defer func() { - if err := c.config.Storage.Unlock(lockKey); err != nil { - log.Printf("[ERROR][%s] Renew: Unable to unlock '%s': %v", name, lockKey, err) - } - }() - - // Prepare for renewal (load PEM cert, key, and meta) - certRes, err := c.config.loadCertResource(name) - if err != nil { - return err - } - - // Check if renew is still needed - might have been renewed while waiting for lock - if !c.config.managedCertNeedsRenewal(certRes) { - log.Printf("[INFO][%s] Renew: Certificate appears to have been renewed already", name) - return nil - } - - // Perform renewal and retry if necessary, but not too many times. - var newCertMeta *certificate.Resource - var success bool - for attempts := 0; attempts < 2; attempts++ { - acmeMu.Lock() - newCertMeta, err = c.acmeClient.Certificate.Renew(certRes, true, c.config.MustStaple) - acmeMu.Unlock() - if err == nil { - // double-check that we actually got a certificate; check a couple fields, just in case - if newCertMeta == nil || newCertMeta.Domain == "" || newCertMeta.Certificate == nil { - err = fmt.Errorf("returned certificate was empty; probably an unchecked error renewing it") - } else { - success = true - break - } - } - - // wait a little bit and try again - wait := 10 * time.Second - log.Printf("[ERROR] Renewing [%v]: %v; trying again in %s", name, err, wait) - time.Sleep(wait) - } - - if !success { - return fmt.Errorf("too many renewal attempts; last error: %v", err) - } - - if c.config.OnEvent != nil { - c.config.OnEvent("acme_cert_renewed", name) - } - - return c.config.saveCertResource(newCertMeta) -} - -// Revoke revokes the certificate for name and deletes -// it from storage. -func (c *acmeClient) Revoke(name string) error { - if !c.config.Storage.Exists(StorageKeys.SitePrivateKey(c.config.CA, name)) { - return fmt.Errorf("private key not found for %s", name) - } - - certRes, err := c.config.loadCertResource(name) - if err != nil { - return err - } - - err = c.acmeClient.Certificate.Revoke(certRes.Certificate) - if err != nil { - return err - } - - if c.config.OnEvent != nil { - c.config.OnEvent("acme_cert_revoked", name) - } - - err = c.config.Storage.Delete(StorageKeys.SiteCert(c.config.CA, name)) - if err != nil { - return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err) - } - err = c.config.Storage.Delete(StorageKeys.SitePrivateKey(c.config.CA, name)) - if err != nil { - return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err) - } - err = c.config.Storage.Delete(StorageKeys.SiteMeta(c.config.CA, name)) - if err != nil { - return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err) - } - - return nil -} - -func buildUAString() string { - ua := "CertMagic" - if UserAgent != "" { - ua += " " + UserAgent - } - return ua -} - -// Some default values passed down to the underlying lego client. -var ( - UserAgent string - HTTPTimeout = 30 * time.Second -) - -// Interface guard -var _ Manager = (*acmeClient)(nil) diff --git a/vendor/github.com/mholt/certmagic/config.go b/vendor/github.com/mholt/certmagic/config.go deleted file mode 100644 index 12ff698d..00000000 --- a/vendor/github.com/mholt/certmagic/config.go +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package certmagic - -import ( - "crypto/tls" - "fmt" - "sync" - "time" - - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/certificate" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/challenge/tlsalpn01" - "github.com/go-acme/lego/lego" -) - -// Config configures a certificate manager instance. -// An empty Config is not valid: use New() to obtain -// a valid Config. -type Config struct { - // The endpoint of the directory for the ACME - // CA we are to use - CA string - - // The email address to use when creating or - // selecting an existing ACME server account - Email string - - // Set to true if agreed to the CA's - // subscriber agreement - Agreed bool - - // Disable all HTTP challenges - DisableHTTPChallenge bool - - // Disable all TLS-ALPN challenges - DisableTLSALPNChallenge bool - - // How long before expiration to renew certificates - RenewDurationBefore time.Duration - - // How long before expiration to require a renewed - // certificate when in interactive mode, like when - // the program is first starting up (see - // mholt/caddy#1680). A wider window between - // RenewDurationBefore and this value will suppress - // errors under duress (bad) but hopefully this duration - // will give it enough time for the blockage to be - // relieved. - RenewDurationBeforeAtStartup time.Duration - - // An optional event callback clients can set - // to subscribe to certain things happening - // internally by this config; invocations are - // synchronous, so make them return quickly! - OnEvent func(event string, data interface{}) - - // The host (ONLY the host, not port) to listen - // on if necessary to start a listener to solve - // an ACME challenge - ListenHost string - - // The alternate port to use for the ACME HTTP - // challenge; if non-empty, this port will be - // used instead of HTTPChallengePort to spin up - // a listener for the HTTP challenge - AltHTTPPort int - - // The alternate port to use for the ACME - // TLS-ALPN challenge; the system must forward - // TLSALPNChallengePort to this port for - // challenge to succeed - AltTLSALPNPort int - - // The DNS provider to use when solving the - // ACME DNS challenge - DNSProvider challenge.Provider - - // The type of key to use when generating - // certificates - KeyType certcrypto.KeyType - - // The maximum amount of time to allow for - // obtaining a certificate. If empty, the - // default from the underlying lego lib is - // used. If set, it must not be too low so - // as to cancel orders too early, running - // the risk of rate limiting. - CertObtainTimeout time.Duration - - // DefaultServerName specifies a server name - // to use when choosing a certificate if the - // ClientHello's ServerName field is empty - DefaultServerName string - - // The state needed to operate on-demand TLS - OnDemand *OnDemandConfig - - // Add the must staple TLS extension to the - // CSR generated by lego/acme - MustStaple bool - - // The storage to access when storing or - // loading TLS assets - Storage Storage - - // NewManager returns a new Manager. If nil, - // an ACME client will be created and used. - NewManager func(interactive bool) (Manager, error) - - // Pointer to the in-memory certificate cache - certCache *Cache - - // Map of client config key to ACME clients - // so they can be reused - // TODO: It might be better if these were globally cached, rather than per-config, which are ephemeral... but maybe evict them after a certain time, like 1 day or something - acmeClients map[string]*lego.Client - acmeClientsMu *sync.Mutex -} - -// NewDefault makes a valid config based on the package -// Default config. Most users will call this function -// instead of New() since most use cases require only a -// single config for any and all certificates. -// -// If your requirements are more advanced (for example, -// multiple configs depending on the certificate), then use -// New() instead. (You will need to make your own Cache -// first.) If you only need a single Config to manage your -// certs (even if that config changes, as long as it is the -// only one), customize the Default package variable before -// calling NewDefault(). -// -// All calls to NewDefault() will return configs that use the -// same, default certificate cache. All configs returned -// by NewDefault() are based on the values of the fields of -// Default at the time it is called. -func NewDefault() *Config { - defaultCacheMu.Lock() - if defaultCache == nil { - defaultCache = NewCache(CacheOptions{ - // the cache will likely need to renew certificates, - // so it will need to know how to do that, which - // depends on the certificate being managed and which - // can change during the lifetime of the cache; this - // callback makes it possible to get the latest and - // correct config with which to manage the cert, - // but if the user does not provide one, we can only - // assume that we are to use the default config - GetConfigForCert: func(Certificate) (Config, error) { - return Default, nil - }, - }) - } - certCache := defaultCache - defaultCacheMu.Unlock() - - return newWithCache(certCache, Default) -} - -// New makes a new, valid config based on cfg and -// uses the provided certificate cache. certCache -// MUST NOT be nil or this function will panic. -// -// Use this method when you have an advanced use case -// that requires a custom certificate cache and config -// that may differ from the Default. For example, if -// not all certificates are managed/renewed the same -// way, you need to make your own Cache value with a -// GetConfigForCert callback that returns the correct -// configuration for each certificate. However, for -// the vast majority of cases, there will be only a -// single Config, thus the default cache (which always -// uses the default Config) and default config will -// suffice, and you should use New() instead. -func New(certCache *Cache, cfg Config) *Config { - if certCache == nil { - panic("a certificate cache is required") - } - if certCache.options.GetConfigForCert == nil { - panic("cache must have GetConfigForCert set in its options") - } - return newWithCache(certCache, cfg) -} - -// newWithCache ensures that cfg is a valid config by populating -// zero-value fields from the Default Config. If certCache is -// nil, this function panics. -func newWithCache(certCache *Cache, cfg Config) *Config { - if certCache == nil { - panic("cannot make a valid config without a pointer to a certificate cache") - } - - // fill in default values - if cfg.CA == "" { - cfg.CA = Default.CA - } - if cfg.Email == "" { - cfg.Email = Default.Email - } - if cfg.OnDemand == nil { - cfg.OnDemand = Default.OnDemand - } - if !cfg.Agreed { - cfg.Agreed = Default.Agreed - } - if !cfg.DisableHTTPChallenge { - cfg.DisableHTTPChallenge = Default.DisableHTTPChallenge - } - if !cfg.DisableTLSALPNChallenge { - cfg.DisableTLSALPNChallenge = Default.DisableTLSALPNChallenge - } - if cfg.RenewDurationBefore == 0 { - cfg.RenewDurationBefore = Default.RenewDurationBefore - } - if cfg.RenewDurationBeforeAtStartup == 0 { - cfg.RenewDurationBeforeAtStartup = Default.RenewDurationBeforeAtStartup - } - if cfg.OnEvent == nil { - cfg.OnEvent = Default.OnEvent - } - if cfg.ListenHost == "" { - cfg.ListenHost = Default.ListenHost - } - if cfg.AltHTTPPort == 0 { - cfg.AltHTTPPort = Default.AltHTTPPort - } - if cfg.AltTLSALPNPort == 0 { - cfg.AltTLSALPNPort = Default.AltTLSALPNPort - } - if cfg.DNSProvider == nil { - cfg.DNSProvider = Default.DNSProvider - } - if cfg.KeyType == "" { - cfg.KeyType = Default.KeyType - } - if cfg.CertObtainTimeout == 0 { - cfg.CertObtainTimeout = Default.CertObtainTimeout - } - if cfg.DefaultServerName == "" { - cfg.DefaultServerName = Default.DefaultServerName - } - if cfg.OnDemand == nil { - cfg.OnDemand = Default.OnDemand - } - if !cfg.MustStaple { - cfg.MustStaple = Default.MustStaple - } - if cfg.Storage == nil { - cfg.Storage = Default.Storage - } - if cfg.NewManager == nil { - cfg.NewManager = Default.NewManager - } - - // absolutely don't allow a nil storage, - // because that would make almost anything - // a config can do pointless - if cfg.Storage == nil { - cfg.Storage = defaultFileStorage - } - - // ensure the unexported fields are valid - cfg.certCache = certCache - cfg.acmeClients = make(map[string]*lego.Client) - cfg.acmeClientsMu = new(sync.Mutex) - - return &cfg -} - -// Manage causes the certificates for domainNames to be managed -// according to cfg. If cfg is enabled for OnDemand, then this -// simply whitelists the domain names. Otherwise, the certificate(s) -// for each name are loaded from storage or obtained from the CA; -// and if loaded from storage, renewed if they are expiring or -// expired. It then caches the certificate in memory and is -// prepared to serve them up during TLS handshakes. -func (cfg *Config) Manage(domainNames []string) error { - for _, domainName := range domainNames { - if !HostQualifies(domainName) { - return fmt.Errorf("name does not qualify for automatic certificate management: %s", domainName) - } - - // if on-demand is configured, simply whitelist this name - if cfg.OnDemand != nil { - if !cfg.OnDemand.whitelistContains(domainName) { - cfg.OnDemand.HostWhitelist = append(cfg.OnDemand.HostWhitelist, domainName) - } - continue - } - - // try loading an existing certificate; if it doesn't - // exist yet, obtain one and try loading it again - cert, err := cfg.CacheManagedCertificate(domainName) - if err != nil { - if _, ok := err.(ErrNotExist); ok { - // if it doesn't exist, get it, then try loading it again - err := cfg.ObtainCert(domainName, false) - if err != nil { - return fmt.Errorf("%s: obtaining certificate: %v", domainName, err) - } - cert, err = cfg.CacheManagedCertificate(domainName) - if err != nil { - return fmt.Errorf("%s: caching certificate after obtaining it: %v", domainName, err) - } - continue - } - return fmt.Errorf("%s: caching certificate: %v", domainName, err) - } - - // for existing certificates, make sure it is renewed - if cert.NeedsRenewal(cfg) { - err := cfg.RenewCert(domainName, false) - if err != nil { - return fmt.Errorf("%s: renewing certificate: %v", domainName, err) - } - } - } - - return nil -} - -// ObtainCert obtains a certificate for name using cfg, as long -// as a certificate does not already exist in storage for that -// name. The name must qualify and cfg must be flagged as Managed. -// This function is a no-op if storage already has a certificate -// for name. -// -// It only obtains and stores certificates (and their keys), -// it does not load them into memory. If interactive is true, -// the user may be shown a prompt. -func (cfg *Config) ObtainCert(name string, interactive bool) error { - if cfg.storageHasCertResources(name) { - return nil - } - skip, err := cfg.preObtainOrRenewChecks(name, interactive) - if err != nil { - return err - } - if skip { - return nil - } - manager, err := cfg.newManager(interactive) - if err != nil { - return err - } - return manager.Obtain(name) -} - -// RenewCert renews the certificate for name using cfg. It stows the -// renewed certificate and its assets in storage if successful. -func (cfg *Config) RenewCert(name string, interactive bool) error { - skip, err := cfg.preObtainOrRenewChecks(name, interactive) - if err != nil { - return err - } - if skip { - return nil - } - manager, err := cfg.newManager(interactive) - if err != nil { - return err - } - return manager.Renew(name) -} - -// RevokeCert revokes the certificate for domain via ACME protocol. -func (cfg *Config) RevokeCert(domain string, interactive bool) error { - manager, err := cfg.newManager(interactive) - if err != nil { - return err - } - return manager.Revoke(domain) -} - -// TLSConfig is an opinionated method that returns a -// recommended, modern TLS configuration that can be -// used to configure TLS listeners, which also supports -// the TLS-ALPN challenge and serves up certificates -// managed by cfg. -// -// Unlike the package TLS() function, this method does -// not, by itself, enable certificate management for -// any domain names. -// -// Feel free to further customize the returned tls.Config, -// but do not mess with the GetCertificate or NextProtos -// fields unless you know what you're doing, as they're -// necessary to solve the TLS-ALPN challenge. -func (cfg *Config) TLSConfig() *tls.Config { - return &tls.Config{ - // these two fields necessary for TLS-ALPN challenge - GetCertificate: cfg.GetCertificate, - NextProtos: []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol}, - - // the rest recommended for modern TLS servers - MinVersion: tls.VersionTLS12, - CurvePreferences: []tls.CurveID{ - tls.X25519, - tls.CurveP256, - }, - CipherSuites: preferredDefaultCipherSuites(), - PreferServerCipherSuites: true, - } -} - -// preObtainOrRenewChecks perform a few simple checks before -// obtaining or renewing a certificate with ACME, and returns -// whether this name should be skipped (like if it's not -// managed TLS) as well as any error. It ensures that the -// config is Managed, that the name qualifies for a certificate, -// and that an email address is available. -func (cfg *Config) preObtainOrRenewChecks(name string, allowPrompts bool) (bool, error) { - if !HostQualifies(name) { - return true, nil - } - - err := cfg.getEmail(allowPrompts) - if err != nil { - return false, err - } - - return false, nil -} - -// storageHasCertResources returns true if the storage -// associated with cfg's certificate cache has all the -// resources related to the certificate for domain: the -// certificate, the private key, and the metadata. -func (cfg *Config) storageHasCertResources(domain string) bool { - certKey := StorageKeys.SiteCert(cfg.CA, domain) - keyKey := StorageKeys.SitePrivateKey(cfg.CA, domain) - metaKey := StorageKeys.SiteMeta(cfg.CA, domain) - return cfg.Storage.Exists(certKey) && - cfg.Storage.Exists(keyKey) && - cfg.Storage.Exists(metaKey) -} - -// managedCertNeedsRenewal returns true if certRes is -// expiring soon or already expired, or if the process -// of checking the expiration returned an error. -func (cfg *Config) managedCertNeedsRenewal(certRes certificate.Resource) bool { - cert, err := makeCertificate(certRes.Certificate, certRes.PrivateKey) - if err != nil { - return true - } - return cert.NeedsRenewal(cfg) -} - -// Manager is a type that can manage a certificate. -// They are usually very short-lived. -type Manager interface { - Obtain(name string) error - Renew(name string) error - Revoke(name string) error -} diff --git a/vendor/github.com/mholt/certmagic/go.mod b/vendor/github.com/mholt/certmagic/go.mod deleted file mode 100644 index 75559b41..00000000 --- a/vendor/github.com/mholt/certmagic/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -module github.com/mholt/certmagic - -require ( - github.com/cenkalti/backoff v2.1.1+incompatible // indirect - github.com/go-acme/lego v2.5.0+incompatible - github.com/klauspost/cpuid v1.2.0 - github.com/miekg/dns v1.1.3 // indirect - github.com/stretchr/testify v1.3.0 // indirect - golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b - golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect - golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect - golang.org/x/sys v0.0.0-20190124100055-b90733256f2e // indirect - golang.org/x/text v0.3.0 // indirect - gopkg.in/square/go-jose.v2 v2.2.2 // indirect -) diff --git a/vendor/github.com/mholt/certmagic/go.sum b/vendor/github.com/mholt/certmagic/go.sum deleted file mode 100644 index 7093ad5b..00000000 --- a/vendor/github.com/mholt/certmagic/go.sum +++ /dev/null @@ -1,29 +0,0 @@ -github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-acme/lego v2.3.1-0.20190318164254-3684cc738d37+incompatible h1:D8mQOFMowsqoVMibY3U+xeNmd83bdNPEjTScRiPgVoc= -github.com/go-acme/lego v2.3.1-0.20190318164254-3684cc738d37+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/go-acme/lego v2.5.0+incompatible h1:5fNN9yRQfv8ymH3DSsxla+4aYeQt2IgfZqHKVnK8f0s= -github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM= -github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b h1:Elez2XeF2p9uyVj0yEUDqQ56NFcDtcBNkYP7yv8YbUE= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e h1:3GIlrlVLfkoipSReOMNAgApI0ajnalyLa/EZHHca/XI= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/vendor/github.com/mholt/certmagic/solvers.go b/vendor/github.com/mholt/certmagic/solvers.go deleted file mode 100644 index 743edfce..00000000 --- a/vendor/github.com/mholt/certmagic/solvers.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package certmagic - -import ( - "encoding/json" - "fmt" - "log" - "path/filepath" - - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/challenge/tlsalpn01" -) - -// tlsALPNSolver is a type that can solve TLS-ALPN challenges using -// an existing listener and our custom, in-memory certificate cache. -type tlsALPNSolver struct { - certCache *Cache -} - -// Present adds the challenge certificate to the cache. -func (s tlsALPNSolver) Present(domain, token, keyAuth string) error { - cert, err := tlsalpn01.ChallengeCert(domain, keyAuth) - if err != nil { - return err - } - certHash := hashCertificateChain(cert.Certificate) - s.certCache.mu.Lock() - s.certCache.cache[tlsALPNCertKeyName(domain)] = Certificate{ - Certificate: *cert, - Names: []string{domain}, - Hash: certHash, // perhaps not necesssary - } - s.certCache.mu.Unlock() - return nil -} - -// CleanUp removes the challenge certificate from the cache. -func (s tlsALPNSolver) CleanUp(domain, token, keyAuth string) error { - s.certCache.mu.Lock() - delete(s.certCache.cache, tlsALPNCertKeyName(domain)) - s.certCache.mu.Unlock() - return nil -} - -// tlsALPNCertKeyName returns the key to use when caching a cert -// for use with the TLS-ALPN ACME challenge. It is simply to help -// avoid conflicts (although at time of writing, there shouldn't -// be, since the cert cache is keyed by hash of certificate chain). -func tlsALPNCertKeyName(sniName string) string { - return sniName + ":acme-tls-alpn" -} - -// distributedSolver allows the ACME HTTP-01 and TLS-ALPN challenges -// to be solved by an instance other than the one which initiated it. -// This is useful behind load balancers or in other cluster/fleet -// configurations. The only requirement is that the instance which -// initiates the challenge shares the same storage and locker with -// the others in the cluster. The storage backing the certificate -// cache in distributedSolver.config is crucial. -// -// Obviously, the instance which completes the challenge must be -// serving on the HTTPChallengePort for the HTTP-01 challenge or the -// TLSALPNChallengePort for the TLS-ALPN-01 challenge (or have all -// the packets port-forwarded) to receive and handle the request. The -// server which receives the challenge must handle it by checking to -// see if the challenge token exists in storage, and if so, decode it -// and use it to serve up the correct response. HTTPChallengeHandler -// in this package as well as the GetCertificate method implemented -// by a Config support and even require this behavior. -// -// In short: the only two requirements for cluster operation are -// sharing sync and storage, and using the facilities provided by -// this package for solving the challenges. -type distributedSolver struct { - // The config with a certificate cache - // with a reference to the storage to - // use which is shared among all the - // instances in the cluster - REQUIRED. - config *Config - - // Since the distributedSolver is only a - // wrapper over an actual solver, place - // the actual solver here. - providerServer challenge.Provider -} - -// Present invokes the underlying solver's Present method -// and also stores domain, token, and keyAuth to the storage -// backing the certificate cache of dhs.config. -func (dhs distributedSolver) Present(domain, token, keyAuth string) error { - if dhs.providerServer != nil { - err := dhs.providerServer.Present(domain, token, keyAuth) - if err != nil { - return fmt.Errorf("presenting with standard provider server: %v", err) - } - } - - infoBytes, err := json.Marshal(challengeInfo{ - Domain: domain, - Token: token, - KeyAuth: keyAuth, - }) - if err != nil { - return err - } - - return dhs.config.Storage.Store(dhs.challengeTokensKey(domain), infoBytes) -} - -// CleanUp invokes the underlying solver's CleanUp method -// and also cleans up any assets saved to storage. -func (dhs distributedSolver) CleanUp(domain, token, keyAuth string) error { - if dhs.providerServer != nil { - err := dhs.providerServer.CleanUp(domain, token, keyAuth) - if err != nil { - log.Printf("[ERROR] Cleaning up standard provider server: %v", err) - } - } - return dhs.config.Storage.Delete(dhs.challengeTokensKey(domain)) -} - -// challengeTokensPrefix returns the key prefix for challenge info. -func (dhs distributedSolver) challengeTokensPrefix() string { - return filepath.Join(StorageKeys.CAPrefix(dhs.config.CA), "challenge_tokens") -} - -// challengeTokensKey returns the key to use to store and access -// challenge info for domain. -func (dhs distributedSolver) challengeTokensKey(domain string) string { - return filepath.Join(dhs.challengeTokensPrefix(), StorageKeys.Safe(domain)+".json") -} - -type challengeInfo struct { - Domain, Token, KeyAuth string -} diff --git a/vendor/github.com/miekg/dns/.travis.yml b/vendor/github.com/miekg/dns/.travis.yml index 013b5ae4..8eaa0642 100644 --- a/vendor/github.com/miekg/dns/.travis.yml +++ b/vendor/github.com/miekg/dns/.travis.yml @@ -2,17 +2,15 @@ language: go sudo: false go: - - 1.10.x - - 1.11.x - - 1.12.x + - "1.12.x" + - "1.13.x" - tip -before_install: - # don't use the miekg/dns when testing forks - - mkdir -p $GOPATH/src/github.com/miekg - - ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true +env: + - GO111MODULE=on script: + - go generate ./... && test `git ls-files --modified | wc -l` = 0 - go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./... after_success: diff --git a/vendor/github.com/miekg/dns/CODEOWNERS b/vendor/github.com/miekg/dns/CODEOWNERS new file mode 100644 index 00000000..e0917031 --- /dev/null +++ b/vendor/github.com/miekg/dns/CODEOWNERS @@ -0,0 +1 @@ +* @miekg @tmthrgd diff --git a/vendor/github.com/miekg/dns/Gopkg.lock b/vendor/github.com/miekg/dns/Gopkg.lock deleted file mode 100644 index 68663220..00000000 --- a/vendor/github.com/miekg/dns/Gopkg.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:6914c49eed986dfb8dffb33516fa129c49929d4d873f41e073c83c11c372b870" - name = "golang.org/x/crypto" - packages = [ - "ed25519", - "ed25519/internal/edwards25519", - ] - pruneopts = "" - revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900" - -[[projects]] - branch = "master" - digest = "1:08e41d63f8dac84d83797368b56cf0b339e42d0224e5e56668963c28aec95685" - name = "golang.org/x/net" - packages = [ - "bpf", - "context", - "internal/iana", - "internal/socket", - "ipv4", - "ipv6", - ] - pruneopts = "" - revision = "4dfa2610cdf3b287375bbba5b8f2a14d3b01d8de" - -[[projects]] - branch = "master" - digest = "1:b2ea75de0ccb2db2ac79356407f8a4cd8f798fe15d41b381c00abf3ae8e55ed1" - name = "golang.org/x/sync" - packages = ["errgroup"] - pruneopts = "" - revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" - -[[projects]] - branch = "master" - digest = "1:149a432fabebb8221a80f77731b1cd63597197ded4f14af606ebe3a0959004ec" - name = "golang.org/x/sys" - packages = ["unix"] - pruneopts = "" - revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "golang.org/x/crypto/ed25519", - "golang.org/x/net/ipv4", - "golang.org/x/net/ipv6", - "golang.org/x/sync/errgroup", - "golang.org/x/sys/unix", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/miekg/dns/Gopkg.toml b/vendor/github.com/miekg/dns/Gopkg.toml deleted file mode 100644 index 85e6ff31..00000000 --- a/vendor/github.com/miekg/dns/Gopkg.toml +++ /dev/null @@ -1,38 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - branch = "master" - name = "golang.org/x/crypto" - -[[constraint]] - branch = "master" - name = "golang.org/x/net" - -[[constraint]] - branch = "master" - name = "golang.org/x/sys" - -[[constraint]] - branch = "master" - name = "golang.org/x/sync" diff --git a/vendor/github.com/miekg/dns/LICENSE b/vendor/github.com/miekg/dns/LICENSE index 5763fa7f..55f12ab7 100644 --- a/vendor/github.com/miekg/dns/LICENSE +++ b/vendor/github.com/miekg/dns/LICENSE @@ -1,7 +1,3 @@ -Extensions of the original work are copyright (c) 2011 Miek Gieben - -As this is fork of the official Go code the same license applies: - Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -30,3 +26,5 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +As this is fork of the official Go code the same license applies. +Extensions of the original work are copyright (c) 2011 Miek Gieben diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 4af848a6..126fe62c 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -69,6 +69,7 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/semihalev/sdns * https://render.com * https://github.com/peterzen/goresolver +* https://github.com/folbricht/routedns Send pull request if you want to be listed here. @@ -93,8 +94,8 @@ DNS Authors 2012- # Building -Building is done with the `go` tool. If you have setup your GOPATH correctly, the following should -work: +This library uses Go modules and uses semantic versioning. Building is done with the `go` tool, so +the following should work: go get github.com/miekg/dns go build github.com/miekg/dns @@ -126,6 +127,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 2915 - NAPTR record * 2929 - DNS IANA Considerations * 3110 - RSASHA1 DNS keys +* 3123 - APL record * 3225 - DO bit (DNSSEC OK) * 340{1,2,3} - NAPTR record * 3445 - Limiting the scope of (DNS)KEY diff --git a/vendor/github.com/miekg/dns/acceptfunc.go b/vendor/github.com/miekg/dns/acceptfunc.go index 94e7c9d9..825617fe 100644 --- a/vendor/github.com/miekg/dns/acceptfunc.go +++ b/vendor/github.com/miekg/dns/acceptfunc.go @@ -6,22 +6,30 @@ type MsgAcceptFunc func(dh Header) MsgAcceptAction // DefaultMsgAcceptFunc checks the request and will reject if: // -// * isn't a request (don't respond in that case). +// * 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 1 RR 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. + MsgAccept MsgAcceptAction = iota // Accept the message + MsgReject // Reject the message with a RcodeFormatError + MsgIgnore // Ignore the error and send nothing back. + MsgRejectNotImplemented // Reject the message with a RcodeNotImplemented ) func defaultMsgAcceptFunc(dh Header) MsgAcceptAction { @@ -32,7 +40,7 @@ func defaultMsgAcceptFunc(dh Header) MsgAcceptAction { // 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 + return MsgRejectNotImplemented } if dh.Qdcount != 1 { diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index b059f6fc..d874e300 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -105,7 +105,7 @@ func (dns *Msg) SetAxfr(z string) *Msg { // SetTsig appends a TSIG RR to the message. // This is only a skeleton TSIG RR that is added as the last RR in the -// additional section. The Tsig is calculated when the message is being send. +// additional section. The TSIG is calculated when the message is being send. func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg { t := new(TSIG) t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0} @@ -317,6 +317,12 @@ func Fqdn(s string) string { return s + "." } +// CanonicalName returns the domain name in canonical form. A name in canonical +// form is lowercase and fully qualified. See Section 6.2 in RFC 4034. +func CanonicalName(s string) string { + return strings.ToLower(Fqdn(s)) +} + // Copied from the official Go code. // ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP @@ -364,7 +370,7 @@ func (t Type) String() string { // String returns the string representation for the class c. func (c Class) String() string { if s, ok := ClassToString[uint16(c)]; ok { - // Only emit mnemonics when they are unambiguous, specically ANY is in both. + // Only emit mnemonics when they are unambiguous, specially ANY is in both. if _, ok := StringToType[s]; !ok { return s } diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go index f57337b8..ad83a27e 100644 --- a/vendor/github.com/miekg/dns/dns.go +++ b/vendor/github.com/miekg/dns/dns.go @@ -54,7 +54,7 @@ type RR interface { // parse parses an RR from zone file format. // // This will only be called on a new and empty RR type with only the header populated. - parse(c *zlexer, origin, file string) *ParseError + parse(c *zlexer, origin string) *ParseError // isDuplicate returns whether the two RRs are duplicates. isDuplicate(r2 RR) bool @@ -105,7 +105,7 @@ func (h *RR_Header) unpack(msg []byte, off int) (int, error) { panic("dns: internal error: unpack should never be called on RR_Header") } -func (h *RR_Header) parse(c *zlexer, origin, file string) *ParseError { +func (h *RR_Header) parse(c *zlexer, origin string) *ParseError { panic("dns: internal error: parse should never be called on RR_Header") } diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go index 12a693f9..68c0bd74 100644 --- a/vendor/github.com/miekg/dns/dnssec.go +++ b/vendor/github.com/miekg/dns/dnssec.go @@ -200,7 +200,7 @@ func (k *DNSKEY) ToDS(h uint8) *DS { wire = wire[:n] owner := make([]byte, 255) - off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false) + off, err1 := PackDomainName(CanonicalName(k.Hdr.Name), owner, 0, nil, false) if err1 != nil { return nil } @@ -285,7 +285,7 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error { sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag // For signing, lowercase this name - sigwire.SignerName = strings.ToLower(rr.SignerName) + sigwire.SignerName = CanonicalName(rr.SignerName) // Create the desired binary blob signdata := make([]byte, DefaultMsgSize) @@ -423,7 +423,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { sigwire.Expiration = rr.Expiration sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag - sigwire.SignerName = strings.ToLower(rr.SignerName) + sigwire.SignerName = CanonicalName(rr.SignerName) // Create the desired binary blob signeddata := make([]byte, DefaultMsgSize) n, err := packSigWire(sigwire, signeddata) @@ -659,7 +659,7 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "." } // RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase - h.Name = strings.ToLower(h.Name) + h.Name = CanonicalName(h.Name) // 6.2. Canonical RR Form. (3) - domain rdata to lowercase. // NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR, // HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, @@ -672,49 +672,49 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { // conversion. switch x := r1.(type) { case *NS: - x.Ns = strings.ToLower(x.Ns) + x.Ns = CanonicalName(x.Ns) case *MD: - x.Md = strings.ToLower(x.Md) + x.Md = CanonicalName(x.Md) case *MF: - x.Mf = strings.ToLower(x.Mf) + x.Mf = CanonicalName(x.Mf) case *CNAME: - x.Target = strings.ToLower(x.Target) + x.Target = CanonicalName(x.Target) case *SOA: - x.Ns = strings.ToLower(x.Ns) - x.Mbox = strings.ToLower(x.Mbox) + x.Ns = CanonicalName(x.Ns) + x.Mbox = CanonicalName(x.Mbox) case *MB: - x.Mb = strings.ToLower(x.Mb) + x.Mb = CanonicalName(x.Mb) case *MG: - x.Mg = strings.ToLower(x.Mg) + x.Mg = CanonicalName(x.Mg) case *MR: - x.Mr = strings.ToLower(x.Mr) + x.Mr = CanonicalName(x.Mr) case *PTR: - x.Ptr = strings.ToLower(x.Ptr) + x.Ptr = CanonicalName(x.Ptr) case *MINFO: - x.Rmail = strings.ToLower(x.Rmail) - x.Email = strings.ToLower(x.Email) + x.Rmail = CanonicalName(x.Rmail) + x.Email = CanonicalName(x.Email) case *MX: - x.Mx = strings.ToLower(x.Mx) + x.Mx = CanonicalName(x.Mx) case *RP: - x.Mbox = strings.ToLower(x.Mbox) - x.Txt = strings.ToLower(x.Txt) + x.Mbox = CanonicalName(x.Mbox) + x.Txt = CanonicalName(x.Txt) case *AFSDB: - x.Hostname = strings.ToLower(x.Hostname) + x.Hostname = CanonicalName(x.Hostname) case *RT: - x.Host = strings.ToLower(x.Host) + x.Host = CanonicalName(x.Host) case *SIG: - x.SignerName = strings.ToLower(x.SignerName) + x.SignerName = CanonicalName(x.SignerName) case *PX: - x.Map822 = strings.ToLower(x.Map822) - x.Mapx400 = strings.ToLower(x.Mapx400) + x.Map822 = CanonicalName(x.Map822) + x.Mapx400 = CanonicalName(x.Mapx400) case *NAPTR: - x.Replacement = strings.ToLower(x.Replacement) + x.Replacement = CanonicalName(x.Replacement) case *KX: - x.Exchanger = strings.ToLower(x.Exchanger) + x.Exchanger = CanonicalName(x.Exchanger) case *SRV: - x.Target = strings.ToLower(x.Target) + x.Target = CanonicalName(x.Target) case *DNAME: - x.Target = strings.ToLower(x.Target) + x.Target = CanonicalName(x.Target) } // 6.2. Canonical RR Form. (5) - origTTL wire := make([]byte, Len(r1)+1) // +1 to be safe(r) diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index d3d7cec9..92421681 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -83,7 +83,7 @@ with: in, err := dns.Exchange(m1, "127.0.0.1:53") -When this functions returns you will get dns message. A dns message consists +When this functions returns you will get DNS message. A DNS message consists out of four sections. The question section: in.Question, the answer section: in.Answer, the authority section: in.Ns and the additional section: in.Extra. @@ -209,7 +209,7 @@ Basic use pattern validating and replying to a message that has TSIG set. // *Msg r has an TSIG record and it was validated m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) } else { - // *Msg r has an TSIG records and it was not valided + // *Msg r has an TSIG records and it was not validated } } w.WriteMsg(m) @@ -221,7 +221,7 @@ 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 https://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 @@ -238,9 +238,8 @@ Basic use pattern for creating an (empty) OPT RR: 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: +EDNS0_SUBNET (RFC 7871). 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 { diff --git a/vendor/github.com/miekg/dns/duplicate.go b/vendor/github.com/miekg/dns/duplicate.go index 00cda0aa..d21ae1ca 100644 --- a/vendor/github.com/miekg/dns/duplicate.go +++ b/vendor/github.com/miekg/dns/duplicate.go @@ -3,9 +3,8 @@ 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. +// So this means the header data is equal *and* the RDATA is the same. Returns true +// if so, otherwise false. It's a protocol violation to have identical RRs in a message. func IsDuplicate(r1, r2 RR) bool { // Check whether the record header is identical. if !r1.Header().isDuplicate(r2.Header()) { diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index ca8873e1..04808d57 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -88,7 +88,7 @@ func (rr *OPT) len(off int, compression map[string]struct{}) int { return l } -func (rr *OPT) parse(c *zlexer, origin, file string) *ParseError { +func (rr *OPT) parse(c *zlexer, origin string) *ParseError { panic("dns: internal error: parse should never be called on OPT") } @@ -360,7 +360,7 @@ func (e *EDNS0_COOKIE) copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.C // The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set // an expiration on an update RR. This is helpful for clients that cannot clean // up after themselves. This is a draft RFC and more information can be found at -// http://files.dns-sd.org/draft-sekar-dns-ul.txt +// https://tools.ietf.org/html/draft-sekar-dns-ul-02 // // o := new(dns.OPT) // o.Hdr.Name = "." @@ -370,24 +370,36 @@ func (e *EDNS0_COOKIE) copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.C // e.Lease = 120 // in seconds // o.Option = append(o.Option, e) type EDNS0_UL struct { - Code uint16 // Always EDNS0UL - Lease uint32 + Code uint16 // Always EDNS0UL + Lease uint32 + KeyLease uint32 } // Option implements the EDNS0 interface. func (e *EDNS0_UL) Option() uint16 { return EDNS0UL } -func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) } -func (e *EDNS0_UL) copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease} } +func (e *EDNS0_UL) String() string { return fmt.Sprintf("%d %d", e.Lease, e.KeyLease) } +func (e *EDNS0_UL) copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease, e.KeyLease} } // Copied: http://golang.org/src/pkg/net/dnsmsg.go func (e *EDNS0_UL) pack() ([]byte, error) { - b := make([]byte, 4) + var b []byte + if e.KeyLease == 0 { + b = make([]byte, 4) + } else { + b = make([]byte, 8) + binary.BigEndian.PutUint32(b[4:], e.KeyLease) + } binary.BigEndian.PutUint32(b, e.Lease) return b, nil } func (e *EDNS0_UL) unpack(b []byte) error { - if len(b) < 4 { + switch len(b) { + case 4: + e.KeyLease = 0 + case 8: + e.KeyLease = binary.BigEndian.Uint32(b[4:]) + default: return ErrBuf } e.Lease = binary.BigEndian.Uint32(b) @@ -531,6 +543,10 @@ func (e *EDNS0_EXPIRE) pack() ([]byte, error) { } func (e *EDNS0_EXPIRE) unpack(b []byte) error { + if len(b) == 0 { + // zero-length EXPIRE query, see RFC 7314 Section 2 + return nil + } if len(b) < 4 { return ErrBuf } diff --git a/vendor/github.com/miekg/dns/fuzz.go b/vendor/github.com/miekg/dns/fuzz.go index a8a09184..57410acd 100644 --- a/vendor/github.com/miekg/dns/fuzz.go +++ b/vendor/github.com/miekg/dns/fuzz.go @@ -2,6 +2,8 @@ package dns +import "strings" + func Fuzz(data []byte) int { msg := new(Msg) @@ -16,7 +18,14 @@ func Fuzz(data []byte) int { } func FuzzNewRR(data []byte) int { - if _, err := NewRR(string(data)); err != nil { + str := string(data) + // Do not fuzz lines that include the $INCLUDE keyword and hint the fuzzer + // at avoiding them. + // See GH#1025 for context. + if strings.Contains(strings.ToUpper(str), "$INCLUDE") { + return -1 + } + if _, err := NewRR(str); err != nil { return 0 } return 1 diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go index 97bc39f5..f7e91a23 100644 --- a/vendor/github.com/miekg/dns/generate.go +++ b/vendor/github.com/miekg/dns/generate.go @@ -49,11 +49,15 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) { if err != nil { return zp.setParseError("bad stop in $GENERATE range", l) } - if end < 0 || start < 0 || end < start { + if end < 0 || start < 0 || end < start || (end-start)/step > 65535 { return zp.setParseError("bad range in $GENERATE range", l) } - zp.c.Next() // _BLANK + // _BLANK + l, ok := zp.c.Next() + if !ok || l.value != zBlank { + return zp.setParseError("garbage after $GENERATE range", l) + } // Create a complete new string, which we then parse again. var s string @@ -81,6 +85,7 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) { } zp.sub = NewZoneParser(r, zp.origin, zp.file) zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed + zp.sub.generateDisallowed = true zp.sub.SetDefaultTTL(defaultTtl) return zp.subNext() } diff --git a/vendor/github.com/miekg/dns/go.mod b/vendor/github.com/miekg/dns/go.mod new file mode 100644 index 00000000..6003d057 --- /dev/null +++ b/vendor/github.com/miekg/dns/go.mod @@ -0,0 +1,11 @@ +module github.com/miekg/dns + +go 1.12 + +require ( + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 + golang.org/x/net v0.0.0-20190923162816-aa69164e4478 + golang.org/x/sync v0.0.0-20190423024810-112230192c58 + golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe + golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 // indirect +) diff --git a/vendor/github.com/miekg/dns/go.sum b/vendor/github.com/miekg/dns/go.sum new file mode 100644 index 00000000..96bda3a9 --- /dev/null +++ b/vendor/github.com/miekg/dns/go.sum @@ -0,0 +1,39 @@ +golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc= +golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM= +golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0= +golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0= +golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index e32d2a1d..df1675df 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -83,7 +83,7 @@ func CompareDomainName(s1, s2 string) (n int) { return } -// CountLabel counts the the number of labels in the string s. +// CountLabel counts the number of labels in the string s. // s must be a syntactically valid domain name. func CountLabel(s string) (labels int) { if s == "." { @@ -126,20 +126,23 @@ func Split(s string) []int { // The bool end is true when the end of the string has been reached. // Also see PrevLabel. func NextLabel(s string, offset int) (i int, end bool) { - quote := false + if s == "" { + return 0, true + } for i = offset; i < len(s)-1; i++ { - switch s[i] { - case '\\': - quote = !quote - default: - quote = false - case '.': - if quote { - quote = !quote - continue - } - return i + 1, false + if s[i] != '.' { + continue } + j := i - 1 + for j >= 0 && s[j] == '\\' { + j-- + } + + if (j-i)%2 == 0 { + continue + } + + return i + 1, false } return i + 1, true } @@ -149,17 +152,38 @@ func NextLabel(s string, offset int) (i int, end bool) { // The bool start is true when the start of the string has been overshot. // Also see NextLabel. func PrevLabel(s string, n int) (i int, start bool) { + if s == "" { + return 0, true + } if n == 0 { return len(s), false } - lab := Split(s) - if lab == nil { - return 0, true + + l := len(s) - 1 + if s[l] == '.' { + l-- } - if n > len(lab) { - return 0, true + + for ; l >= 0 && n > 0; l-- { + if s[l] != '.' { + continue + } + j := l - 1 + for j >= 0 && s[j] == '\\' { + j-- + } + + if (j-l)%2 == 0 { + continue + } + + n-- + if n == 0 { + return l + 1, false + } } - return lab[len(lab)-n], false + + return 0, n > 1 } // equal compares a and b while ignoring case. It returns true when equal otherwise false. diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index e04fb5d7..63656873 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -11,14 +11,12 @@ package dns //go:generate go run msg_generate.go import ( - crand "crypto/rand" + "crypto/rand" "encoding/binary" "fmt" "math/big" - "math/rand" "strconv" "strings" - "sync" ) const ( @@ -73,53 +71,23 @@ var ( 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 -// message id. The random provided should be good enough. This being a -// variable the function can be reassigned to a custom function. -// For instance, to make it return a static value: +// Id by default returns a 16-bit random number to be used as a message id. The +// number is drawn from a cryptographically secure random number generator. +// This being a variable the function can be reassigned to a custom function. +// For instance, to make it return a static value for testing: // // dns.Id = func() uint16 { return 3 } var Id = id -var ( - idLock sync.Mutex - idRand *rand.Rand -) - // id returns a 16 bits random number to be used as a // message id. The random provided should be good enough. func id() uint16 { - idLock.Lock() - - if idRand == nil { - // This (partially) works around - // https://github.com/golang/go/issues/11833 by only - // seeding idRand upon the first call to id. - - var seed int64 - var buf [8]byte - - if _, err := crand.Read(buf[:]); err == nil { - seed = int64(binary.LittleEndian.Uint64(buf[:])) - } else { - seed = rand.Int63() - } - - idRand = rand.New(rand.NewSource(seed)) + var output uint16 + err := binary.Read(rand.Reader, binary.BigEndian, &output) + if err != nil { + panic("dns: reading random id failed: " + err.Error()) } - - // The call to idRand.Uint32 must be within the - // mutex lock because *rand.Rand is not safe for - // concurrent use. - // - // There is no added performance overhead to calling - // idRand.Uint32 inside a mutex lock over just - // calling rand.Uint32 as the global math/rand rng - // is internally protected by a sync.Mutex. - id := uint16(idRand.Uint32()) - - idLock.Unlock() - return id + return output } // MsgHdr is a a manually-unpacked version of (id, bits). @@ -693,7 +661,6 @@ func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) } // If offset does not increase anymore, l is a lie if off1 == off { - l = i break } dst = append(dst, r) diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go index cb4ae764..98fadc31 100644 --- a/vendor/github.com/miekg/dns/msg_helpers.go +++ b/vendor/github.com/miekg/dns/msg_helpers.go @@ -265,24 +265,36 @@ func unpackString(msg []byte, off int) (string, int, error) { return "", off, &Error{err: "overflow unpacking txt"} } l := int(msg[off]) - if off+l+1 > len(msg) { + off++ + if off+l > len(msg) { return "", off, &Error{err: "overflow unpacking txt"} } var s strings.Builder - s.Grow(l) - for _, b := range msg[off+1 : off+1+l] { + consumed := 0 + for i, b := range msg[off : off+l] { switch { case b == '"' || b == '\\': + if consumed == 0 { + s.Grow(l * 2) + } + s.Write(msg[off+consumed : off+i]) s.WriteByte('\\') s.WriteByte(b) + consumed = i + 1 case b < ' ' || b > '~': // unprintable + if consumed == 0 { + s.Grow(l * 2) + } + s.Write(msg[off+consumed : off+i]) s.WriteString(escapeByte(b)) - default: - s.WriteByte(b) + consumed = i + 1 } } - off += 1 + l - return s.String(), off, nil + if consumed == 0 { // no escaping needed + return string(msg[off : off+l]), off + l, nil + } + s.Write(msg[off+consumed : off+l]) + return s.String(), off + l, nil } func packString(s string, msg []byte, off int) (int, error) { @@ -433,6 +445,13 @@ Option: } edns = append(edns, e) off += int(optlen) + case EDNS0EXPIRE: + e := new(EDNS0_EXPIRE) + if err := e.unpack(msg[off : off+int(optlen)]); err != nil { + return nil, len(msg), err + } + edns = append(edns, e) + off += int(optlen) case EDNS0UL: e := new(EDNS0_UL) if err := e.unpack(msg[off : off+int(optlen)]); err != nil { @@ -669,3 +688,123 @@ func packDataDomainNames(names []string, msg []byte, off int, compression compre } return off, nil } + +func packDataApl(data []APLPrefix, msg []byte, off int) (int, error) { + var err error + for i := range data { + off, err = packDataAplPrefix(&data[i], msg, off) + if err != nil { + return len(msg), err + } + } + return off, nil +} + +func packDataAplPrefix(p *APLPrefix, msg []byte, off int) (int, error) { + if len(p.Network.IP) != len(p.Network.Mask) { + return len(msg), &Error{err: "address and mask lengths don't match"} + } + + var err error + prefix, _ := p.Network.Mask.Size() + addr := p.Network.IP.Mask(p.Network.Mask)[:(prefix+7)/8] + + switch len(p.Network.IP) { + case net.IPv4len: + off, err = packUint16(1, msg, off) + case net.IPv6len: + off, err = packUint16(2, msg, off) + default: + err = &Error{err: "unrecognized address family"} + } + if err != nil { + return len(msg), err + } + + off, err = packUint8(uint8(prefix), msg, off) + if err != nil { + return len(msg), err + } + + var n uint8 + if p.Negation { + n = 0x80 + } + adflen := uint8(len(addr)) & 0x7f + off, err = packUint8(n|adflen, msg, off) + if err != nil { + return len(msg), err + } + + if off+len(addr) > len(msg) { + return len(msg), &Error{err: "overflow packing APL prefix"} + } + off += copy(msg[off:], addr) + + return off, nil +} + +func unpackDataApl(msg []byte, off int) ([]APLPrefix, int, error) { + var result []APLPrefix + for off < len(msg) { + prefix, end, err := unpackDataAplPrefix(msg, off) + if err != nil { + return nil, len(msg), err + } + off = end + result = append(result, prefix) + } + return result, off, nil +} + +func unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) { + family, off, err := unpackUint16(msg, off) + if err != nil { + return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"} + } + prefix, off, err := unpackUint8(msg, off) + if err != nil { + return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"} + } + nlen, off, err := unpackUint8(msg, off) + if err != nil { + return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"} + } + + var ip []byte + switch family { + case 1: + ip = make([]byte, net.IPv4len) + case 2: + ip = make([]byte, net.IPv6len) + default: + return APLPrefix{}, len(msg), &Error{err: "unrecognized APL address family"} + } + if int(prefix) > 8*len(ip) { + return APLPrefix{}, len(msg), &Error{err: "APL prefix too long"} + } + + afdlen := int(nlen & 0x7f) + if (int(prefix)+7)/8 != afdlen { + return APLPrefix{}, len(msg), &Error{err: "invalid APL address length"} + } + if off+afdlen > len(msg) { + return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL address"} + } + off += copy(ip, msg[off:off+afdlen]) + if prefix%8 > 0 { + last := ip[afdlen-1] + zero := uint8(0xff) >> (prefix % 8) + if last&zero > 0 { + return APLPrefix{}, len(msg), &Error{err: "extra APL address bits"} + } + } + + return APLPrefix{ + Negation: (nlen & 0x80) != 0, + Network: net.IPNet{ + IP: ip, + Mask: net.CIDRMask(int(prefix), 8*len(ip)), + }, + }, off, nil +} diff --git a/vendor/github.com/miekg/dns/msg_truncate.go b/vendor/github.com/miekg/dns/msg_truncate.go index 83780af6..a76150a8 100644 --- a/vendor/github.com/miekg/dns/msg_truncate.go +++ b/vendor/github.com/miekg/dns/msg_truncate.go @@ -8,8 +8,13 @@ package dns // record adding as many records as possible without exceeding the // requested buffer size. // -// The TC bit will be set if any answer records were excluded from the -// message. This indicates to that the client should retry over TCP. +// The TC bit will be set if any records were excluded from the message. +// This indicates to that the client should retry over TCP. +// +// According to RFC 2181, the TC bit should only be set if not all of the +// "required" RRs can be included in the response. Unfortunately, we have +// no way of knowing which RRs are required so we set the TC bit if any RR +// had to be omitted from the response. // // The appropriate buffer size can be retrieved from the requests OPT // record, if present, and is transport specific otherwise. dns.MinMsgSize @@ -68,12 +73,12 @@ func (dns *Msg) Truncate(size int) { var numExtra int if l < size { - l, numExtra = truncateLoop(dns.Extra, size, l, compression) + _, numExtra = truncateLoop(dns.Extra, size, l, compression) } - // According to RFC 2181, the TC bit should only be set if not all - // of the answer RRs can be included in the response. - dns.Truncated = len(dns.Answer) > numAnswer + // See the function documentation for when we set this. + dns.Truncated = len(dns.Answer) > numAnswer || + len(dns.Ns) > numNS || len(dns.Extra) > numExtra dns.Answer = dns.Answer[:numAnswer] dns.Ns = dns.Ns[:numNS] diff --git a/vendor/github.com/miekg/dns/nsecx.go b/vendor/github.com/miekg/dns/nsecx.go index 8f071a47..f8826817 100644 --- a/vendor/github.com/miekg/dns/nsecx.go +++ b/vendor/github.com/miekg/dns/nsecx.go @@ -43,7 +43,7 @@ func HashName(label string, ha uint8, iter uint16, salt string) string { return toBase32(nsec3) } -// Cover returns true if a name is covered by the NSEC3 record +// Cover returns true if a name is covered by the NSEC3 record. func (rr *NSEC3) Cover(name string) bool { nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt) owner := strings.ToUpper(rr.Hdr.Name) diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go index 110639f3..cda6cae3 100644 --- a/vendor/github.com/miekg/dns/privaterr.go +++ b/vendor/github.com/miekg/dns/privaterr.go @@ -13,7 +13,6 @@ type PrivateRdata interface { // Pack is used when packing a private RR into a buffer. Pack([]byte) (int, error) // Unpack is used when unpacking a private RR from a buffer. - // TODO(miek): diff. signature than Pack, see edns0.go for instance. Unpack([]byte) (int, error) // Copy copies the Rdata into the PrivateRdata argument. Copy(PrivateRdata) error @@ -68,7 +67,7 @@ func (r *PrivateRR) unpack(msg []byte, off int) (int, error) { return off, err } -func (r *PrivateRR) parse(c *zlexer, origin, file string) *ParseError { +func (r *PrivateRR) parse(c *zlexer, origin string) *ParseError { var l lex text := make([]string, 0, 2) // could be 0..N elements, median is probably 1 Fetch: @@ -85,7 +84,7 @@ Fetch: err := r.Data.Parse(text) if err != nil { - return &ParseError{file, err.Error(), l} + return &ParseError{"", err.Error(), l} } return nil diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go index a8691bca..e52f43c9 100644 --- a/vendor/github.com/miekg/dns/scan.go +++ b/vendor/github.com/miekg/dns/scan.go @@ -103,15 +103,12 @@ type ttlState struct { isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive } -// NewRR reads the RR contained in the string s. Only the first RR is -// returned. If s contains no records, NewRR will return nil with no -// error. +// NewRR reads the RR contained in the string s. Only the first RR is 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. +// 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"), "") @@ -134,7 +131,7 @@ func ReadRR(r io.Reader, file string) (RR, error) { } // ParseZone reads a RFC 1035 style zonefile from r. It returns -// *Tokens on the returned channel, each consisting of either a +// 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. // @@ -143,7 +140,8 @@ func ReadRR(r io.Reader, file string) (RR, error) { // origin, as if the file would start with an $ORIGIN directive. // // The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all -// supported. +// supported. Note that $GENERATE's range support up to a maximum of +// of 65535 steps. // // Basic usage pattern when reading from a string (z) containing the // zone data: @@ -203,6 +201,7 @@ func parseZone(r io.Reader, origin, file string, t chan *Token) { // // The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all // supported. Although $INCLUDE is disabled by default. +// Note that $GENERATE's range support up to a maximum of 65535 steps. // // Basic usage pattern when reading from a string (z) containing the // zone data: @@ -245,7 +244,8 @@ type ZoneParser struct { includeDepth uint8 - includeAllowed bool + includeAllowed bool + generateDisallowed bool } // NewZoneParser returns an RFC 1035 style zonefile parser that reads @@ -503,9 +503,8 @@ func (zp *ZoneParser) Next() (RR, bool) { return zp.setParseError("expecting $TTL value, not this...", l) } - if e := slurpRemainder(zp.c, zp.file); e != nil { - zp.parseErr = e - return nil, false + if err := slurpRemainder(zp.c); err != nil { + return zp.setParseError(err.err, err.lex) } ttl, ok := stringToTTL(l.token) @@ -527,9 +526,8 @@ func (zp *ZoneParser) Next() (RR, bool) { return zp.setParseError("expecting $ORIGIN value, not this...", l) } - if e := slurpRemainder(zp.c, zp.file); e != nil { - zp.parseErr = e - return nil, false + if err := slurpRemainder(zp.c); err != nil { + return zp.setParseError(err.err, err.lex) } name, ok := toAbsoluteName(l.token, zp.origin) @@ -547,6 +545,9 @@ func (zp *ZoneParser) Next() (RR, bool) { st = zExpectDirGenerate case zExpectDirGenerate: + if zp.generateDisallowed { + return zp.setParseError("nested $GENERATE directive not allowed", l) + } if l.value != zString { return zp.setParseError("expecting $GENERATE value, not this...", l) } @@ -650,19 +651,44 @@ func (zp *ZoneParser) Next() (RR, bool) { st = zExpectRdata case zExpectRdata: - r, e := 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 - } - - zp.parseErr = e - return nil, false + var rr RR + if newFn, ok := TypeToRR[h.Rrtype]; ok && canParseAsRR(h.Rrtype) { + rr = newFn() + *rr.Header() = *h + } else { + rr = &RFC3597{Hdr: *h} } - return r, true + _, isPrivate := rr.(*PrivateRR) + if !isPrivate && zp.c.Peek().token == "" { + // This is a dynamic update rr. + + // TODO(tmthrgd): Previously slurpRemainder was only called + // for certain RR types, which may have been important. + if err := slurpRemainder(zp.c); err != nil { + return zp.setParseError(err.err, err.lex) + } + + return rr, true + } else if l.value == zNewline { + return zp.setParseError("unexpected newline", l) + } + + if err := rr.parse(zp.c, zp.origin); err != nil { + // err is a concrete *ParseError without the file field set. + // The setParseError call below will construct a new + // *ParseError with file set to zp.file. + + // If err.lex is nil than we have encounter an unknown RR type + // in that case we substitute our current lex token. + if err.lex == (lex{}) { + return zp.setParseError(err.err, l) + } + + return zp.setParseError(err.err, err.lex) + } + + return rr, true } } @@ -671,6 +697,18 @@ func (zp *ZoneParser) Next() (RR, bool) { return nil, false } +// canParseAsRR returns true if the record type can be parsed as a +// concrete RR. It blacklists certain record types that must be parsed +// according to RFC 3597 because they lack a presentation format. +func canParseAsRR(rrtype uint16) bool { + switch rrtype { + case TypeANY, TypeNULL, TypeOPT, TypeTSIG: + return false + default: + return true + } +} + type zlexer struct { br io.ByteReader @@ -682,7 +720,8 @@ type zlexer struct { comBuf string comment string - l lex + l lex + cachedL *lex brace int quote bool @@ -748,13 +787,37 @@ func (zl *zlexer) readByte() (byte, bool) { return c, true } +func (zl *zlexer) Peek() lex { + if zl.nextL { + return zl.l + } + + l, ok := zl.Next() + if !ok { + return l + } + + if zl.nextL { + // Cache l. Next returns zl.cachedL then zl.l. + zl.cachedL = &l + } else { + // In this case l == zl.l, so we just tell Next to return zl.l. + zl.nextL = true + } + + return l +} + func (zl *zlexer) Next() (lex, bool) { l := &zl.l - if zl.nextL { + switch { + case zl.cachedL != nil: + l, zl.cachedL = zl.cachedL, nil + return *l, true + case zl.nextL: zl.nextL = false return *l, true - } - if l.err { + case l.err: // Parsing errors should be sticky. return lex{value: zEOF}, false } @@ -908,6 +971,11 @@ func (zl *zlexer) Next() (lex, bool) { // was inside braces and we delayed adding it until now. com[comi] = ' ' // convert newline to space comi++ + if comi >= len(com) { + l.token = "comment length insufficient for parsing" + l.err = true + return *l, true + } } com[comi] = ';' @@ -1302,18 +1370,18 @@ func locCheckEast(token string, longitude uint32) (uint32, bool) { } // "Eat" the rest of the "line" -func slurpRemainder(c *zlexer, f string) *ParseError { +func slurpRemainder(c *zlexer) *ParseError { l, _ := c.Next() switch l.value { case zBlank: l, _ = c.Next() if l.value != zNewline && l.value != zEOF { - return &ParseError{f, "garbage after rdata", l} + return &ParseError{"", "garbage after rdata", l} } case zNewline: case zEOF: default: - return &ParseError{f, "garbage after rdata", l} + return &ParseError{"", "garbage after rdata", l} } return nil } diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go index 6096f9b0..3fe8a82a 100644 --- a/vendor/github.com/miekg/dns/scan_rr.go +++ b/vendor/github.com/miekg/dns/scan_rr.go @@ -7,55 +7,21 @@ import ( "strings" ) -// Parse the rdata of each rrtype. -// All data from the channel c is either zString or zBlank. -// 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 *zlexer, o, f string) (RR, *ParseError) { - var rr RR - if newFn, ok := TypeToRR[h.Rrtype]; ok && canParseAsRR(h.Rrtype) { - rr = newFn() - *rr.Header() = h - } else { - rr = &RFC3597{Hdr: h} - } - - err := rr.parse(c, o, f) - if err != nil { - return nil, err - } - - return rr, nil -} - -// canParseAsRR returns true if the record type can be parsed as a -// concrete RR. It blacklists certain record types that must be parsed -// according to RFC 3597 because they lack a presentation format. -func canParseAsRR(rrtype uint16) bool { - switch rrtype { - case TypeANY, TypeNULL, TypeOPT, TypeTSIG: - return false - default: - return true - } -} - // A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces) // or an error -func endingToString(c *zlexer, errstr, f string) (string, *ParseError) { +func endingToString(c *zlexer, errstr string) (string, *ParseError) { var s string l, _ := c.Next() // zString for l.value != zNewline && l.value != zEOF { if l.err { - return s, &ParseError{f, errstr, l} + return s, &ParseError{"", errstr, l} } switch l.value { case zString: s += l.token case zBlank: // Ok default: - return "", &ParseError{f, errstr, l} + return "", &ParseError{"", errstr, l} } l, _ = c.Next() } @@ -65,11 +31,11 @@ func endingToString(c *zlexer, errstr, f string) (string, *ParseError) { // A remainder of the rdata with embedded spaces, split on unquoted whitespace // and return the parsed string slice or an error -func endingToTxtSlice(c *zlexer, errstr, f string) ([]string, *ParseError) { +func endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) { // Get the remaining data until we see a zNewline l, _ := c.Next() if l.err { - return nil, &ParseError{f, errstr, l} + return nil, &ParseError{"", errstr, l} } // Build the slice @@ -78,7 +44,7 @@ func endingToTxtSlice(c *zlexer, errstr, f string) ([]string, *ParseError) { empty := false for l.value != zNewline && l.value != zEOF { if l.err { - return nil, &ParseError{f, errstr, l} + return nil, &ParseError{"", errstr, l} } switch l.value { case zString: @@ -105,7 +71,7 @@ func endingToTxtSlice(c *zlexer, errstr, f string) ([]string, *ParseError) { case zBlank: if quote { // zBlank can only be seen in between txt parts. - return nil, &ParseError{f, errstr, l} + return nil, &ParseError{"", errstr, l} } case zQuote: if empty && quote { @@ -114,24 +80,20 @@ func endingToTxtSlice(c *zlexer, errstr, f string) ([]string, *ParseError) { quote = !quote empty = true default: - return nil, &ParseError{f, errstr, l} + return nil, &ParseError{"", errstr, l} } l, _ = c.Next() } if quote { - return nil, &ParseError{f, errstr, l} + return nil, &ParseError{"", errstr, l} } return s, nil } -func (rr *A) parse(c *zlexer, o, f string) *ParseError { +func (rr *A) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - rr.A = net.ParseIP(l.token) // IPv4 addresses cannot include ":". // We do this rather than use net.IP's To4() because @@ -139,82 +101,58 @@ func (rr *A) parse(c *zlexer, o, f string) *ParseError { // IPv4. isIPv4 := !strings.Contains(l.token, ":") if rr.A == nil || !isIPv4 || l.err { - return &ParseError{f, "bad A A", l} + return &ParseError{"", "bad A A", l} } - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *AAAA) parse(c *zlexer, o, f string) *ParseError { +func (rr *AAAA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - rr.AAAA = net.ParseIP(l.token) // IPv6 addresses must include ":", and IPv4 // addresses cannot include ":". isIPv6 := strings.Contains(l.token, ":") if rr.AAAA == nil || !isIPv6 || l.err { - return &ParseError{f, "bad AAAA AAAA", l} + return &ParseError{"", "bad AAAA AAAA", l} } - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *NS) parse(c *zlexer, o, f string) *ParseError { +func (rr *NS) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Ns = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad NS Ns", l} + return &ParseError{"", "bad NS Ns", l} } rr.Ns = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *PTR) parse(c *zlexer, o, f string) *ParseError { +func (rr *PTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Ptr = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad PTR Ptr", l} + return &ParseError{"", "bad PTR Ptr", l} } rr.Ptr = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *NSAPPTR) parse(c *zlexer, o, f string) *ParseError { +func (rr *NSAPPTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Ptr = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad NSAP-PTR Ptr", l} + return &ParseError{"", "bad NSAP-PTR Ptr", l} } rr.Ptr = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *RP) parse(c *zlexer, o, f string) *ParseError { +func (rr *RP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Mbox = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - mbox, mboxOk := toAbsoluteName(l.token, o) if l.err || !mboxOk { - return &ParseError{f, "bad RP Mbox", l} + return &ParseError{"", "bad RP Mbox", l} } rr.Mbox = mbox @@ -224,60 +162,45 @@ func (rr *RP) parse(c *zlexer, o, f string) *ParseError { txt, txtOk := toAbsoluteName(l.token, o) if l.err || !txtOk { - return &ParseError{f, "bad RP Txt", l} + return &ParseError{"", "bad RP Txt", l} } rr.Txt = txt - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *MR) parse(c *zlexer, o, f string) *ParseError { +func (rr *MR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Mr = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad MR Mr", l} + return &ParseError{"", "bad MR Mr", l} } rr.Mr = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *MB) parse(c *zlexer, o, f string) *ParseError { +func (rr *MB) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Mb = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad MB Mb", l} + return &ParseError{"", "bad MB Mb", l} } rr.Mb = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *MG) parse(c *zlexer, o, f string) *ParseError { +func (rr *MG) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Mg = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad MG Mg", l} + return &ParseError{"", "bad MG Mg", l} } rr.Mg = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *HINFO) parse(c *zlexer, o, f string) *ParseError { - chunks, e := endingToTxtSlice(c, "bad HINFO Fields", f) +func (rr *HINFO) parse(c *zlexer, o string) *ParseError { + chunks, e := endingToTxtSlice(c, "bad HINFO Fields") if e != nil { return e } @@ -299,16 +222,11 @@ func (rr *HINFO) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *MINFO) parse(c *zlexer, o, f string) *ParseError { +func (rr *MINFO) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Rmail = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - rmail, rmailOk := toAbsoluteName(l.token, o) if l.err || !rmailOk { - return &ParseError{f, "bad MINFO Rmail", l} + return &ParseError{"", "bad MINFO Rmail", l} } rr.Rmail = rmail @@ -318,52 +236,38 @@ func (rr *MINFO) parse(c *zlexer, o, f string) *ParseError { email, emailOk := toAbsoluteName(l.token, o) if l.err || !emailOk { - return &ParseError{f, "bad MINFO Email", l} + return &ParseError{"", "bad MINFO Email", l} } rr.Email = email - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *MF) parse(c *zlexer, o, f string) *ParseError { +func (rr *MF) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Mf = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad MF Mf", l} + return &ParseError{"", "bad MF Mf", l} } rr.Mf = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *MD) parse(c *zlexer, o, f string) *ParseError { +func (rr *MD) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Md = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad MD Md", l} + return &ParseError{"", "bad MD Md", l} } rr.Md = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *MX) parse(c *zlexer, o, f string) *ParseError { +func (rr *MX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad MX Pref", l} + return &ParseError{"", "bad MX Pref", l} } rr.Preference = uint16(i) @@ -373,22 +277,18 @@ func (rr *MX) parse(c *zlexer, o, f string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad MX Mx", l} + return &ParseError{"", "bad MX Mx", l} } rr.Mx = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *RT) parse(c *zlexer, o, f string) *ParseError { +func (rr *RT) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil { - return &ParseError{f, "bad RT Preference", l} + return &ParseError{"", "bad RT Preference", l} } rr.Preference = uint16(i) @@ -398,22 +298,18 @@ func (rr *RT) parse(c *zlexer, o, f string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad RT Host", l} + return &ParseError{"", "bad RT Host", l} } rr.Host = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *AFSDB) parse(c *zlexer, o, f string) *ParseError { +func (rr *AFSDB) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad AFSDB Subtype", l} + return &ParseError{"", "bad AFSDB Subtype", l} } rr.Subtype = uint16(i) @@ -423,34 +319,26 @@ func (rr *AFSDB) parse(c *zlexer, o, f string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad AFSDB Hostname", l} + return &ParseError{"", "bad AFSDB Hostname", l} } rr.Hostname = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *X25) parse(c *zlexer, o, f string) *ParseError { +func (rr *X25) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - if l.err { - return &ParseError{f, "bad X25 PSDNAddress", l} + return &ParseError{"", "bad X25 PSDNAddress", l} } rr.PSDNAddress = l.token - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *KX) parse(c *zlexer, o, f string) *ParseError { +func (rr *KX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad KX Pref", l} + return &ParseError{"", "bad KX Pref", l} } rr.Preference = uint16(i) @@ -460,52 +348,37 @@ func (rr *KX) parse(c *zlexer, o, f string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad KX Exchanger", l} + return &ParseError{"", "bad KX Exchanger", l} } rr.Exchanger = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *CNAME) parse(c *zlexer, o, f string) *ParseError { +func (rr *CNAME) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Target = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad CNAME Target", l} + return &ParseError{"", "bad CNAME Target", l} } rr.Target = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *DNAME) parse(c *zlexer, o, f string) *ParseError { +func (rr *DNAME) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Target = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad DNAME Target", l} + return &ParseError{"", "bad DNAME Target", l} } rr.Target = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *SOA) parse(c *zlexer, o, f string) *ParseError { +func (rr *SOA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.Ns = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - ns, nsOk := toAbsoluteName(l.token, o) if l.err || !nsOk { - return &ParseError{f, "bad SOA Ns", l} + return &ParseError{"", "bad SOA Ns", l} } rr.Ns = ns @@ -515,7 +388,7 @@ func (rr *SOA) parse(c *zlexer, o, f string) *ParseError { mbox, mboxOk := toAbsoluteName(l.token, o) if l.err || !mboxOk { - return &ParseError{f, "bad SOA Mbox", l} + return &ParseError{"", "bad SOA Mbox", l} } rr.Mbox = mbox @@ -528,16 +401,16 @@ func (rr *SOA) parse(c *zlexer, o, f string) *ParseError { for i := 0; i < 5; i++ { l, _ = c.Next() if l.err { - return &ParseError{f, "bad SOA zone parameter", l} + return &ParseError{"", "bad SOA zone parameter", l} } - if j, e := strconv.ParseUint(l.token, 10, 32); e != nil { + if j, err := strconv.ParseUint(l.token, 10, 32); err != nil { if i == 0 { // Serial must be a number - return &ParseError{f, "bad SOA zone parameter", l} + return &ParseError{"", "bad SOA zone parameter", l} } // We allow other fields to be unitful duration strings if v, ok = stringToTTL(l.token); !ok { - return &ParseError{f, "bad SOA zone parameter", l} + return &ParseError{"", "bad SOA zone parameter", l} } } else { @@ -560,34 +433,30 @@ func (rr *SOA) parse(c *zlexer, o, f string) *ParseError { rr.Minttl = v } } - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *SRV) parse(c *zlexer, o, f string) *ParseError { +func (rr *SRV) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad SRV Priority", l} + return &ParseError{"", "bad SRV Priority", l} } rr.Priority = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad SRV Weight", l} + i, e1 := strconv.ParseUint(l.token, 10, 16) + if e1 != nil || l.err { + return &ParseError{"", "bad SRV Weight", l} } rr.Weight = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad SRV Port", l} + i, e2 := strconv.ParseUint(l.token, 10, 16) + if e2 != nil || l.err { + return &ParseError{"", "bad SRV Port", l} } rr.Port = uint16(i) @@ -597,29 +466,25 @@ func (rr *SRV) parse(c *zlexer, o, f string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad SRV Target", l} + return &ParseError{"", "bad SRV Target", l} } rr.Target = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *NAPTR) parse(c *zlexer, o, f string) *ParseError { +func (rr *NAPTR) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad NAPTR Order", l} + return &ParseError{"", "bad NAPTR Order", l} } rr.Order = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad NAPTR Preference", l} + i, e1 := strconv.ParseUint(l.token, 10, 16) + if e1 != nil || l.err { + return &ParseError{"", "bad NAPTR Preference", l} } rr.Preference = uint16(i) @@ -627,57 +492,57 @@ func (rr *NAPTR) parse(c *zlexer, o, f string) *ParseError { c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{f, "bad NAPTR Flags", l} + return &ParseError{"", "bad NAPTR Flags", l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Flags = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{f, "bad NAPTR Flags", l} + return &ParseError{"", "bad NAPTR Flags", l} } } else if l.value == zQuote { rr.Flags = "" } else { - return &ParseError{f, "bad NAPTR Flags", l} + return &ParseError{"", "bad NAPTR Flags", l} } // Service c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{f, "bad NAPTR Service", l} + return &ParseError{"", "bad NAPTR Service", l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Service = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{f, "bad NAPTR Service", l} + return &ParseError{"", "bad NAPTR Service", l} } } else if l.value == zQuote { rr.Service = "" } else { - return &ParseError{f, "bad NAPTR Service", l} + return &ParseError{"", "bad NAPTR Service", l} } // Regexp c.Next() // zBlank l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{f, "bad NAPTR Regexp", l} + return &ParseError{"", "bad NAPTR Regexp", l} } l, _ = c.Next() // Either String or Quote if l.value == zString { rr.Regexp = l.token l, _ = c.Next() // _QUOTE if l.value != zQuote { - return &ParseError{f, "bad NAPTR Regexp", l} + return &ParseError{"", "bad NAPTR Regexp", l} } } else if l.value == zQuote { rr.Regexp = "" } else { - return &ParseError{f, "bad NAPTR Regexp", l} + return &ParseError{"", "bad NAPTR Regexp", l} } // After quote no space?? @@ -687,22 +552,17 @@ func (rr *NAPTR) parse(c *zlexer, o, f string) *ParseError { name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad NAPTR Replacement", l} + return &ParseError{"", "bad NAPTR Replacement", l} } rr.Replacement = name - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *TALINK) parse(c *zlexer, o, f string) *ParseError { +func (rr *TALINK) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.PreviousName = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - previousName, previousNameOk := toAbsoluteName(l.token, o) if l.err || !previousNameOk { - return &ParseError{f, "bad TALINK PreviousName", l} + return &ParseError{"", "bad TALINK PreviousName", l} } rr.PreviousName = previousName @@ -712,28 +572,25 @@ func (rr *TALINK) parse(c *zlexer, o, f string) *ParseError { nextName, nextNameOk := toAbsoluteName(l.token, o) if l.err || !nextNameOk { - return &ParseError{f, "bad TALINK NextName", l} + return &ParseError{"", "bad TALINK NextName", l} } rr.NextName = nextName - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *LOC) parse(c *zlexer, o, f string) *ParseError { +func (rr *LOC) parse(c *zlexer, o string) *ParseError { // Non zero defaults for LOC record, see RFC 1876, Section 3. - rr.HorizPre = 165 // 10000 - rr.VertPre = 162 // 10 - rr.Size = 18 // 1 + rr.Size = 0x12 // 1e2 cm (1m) + rr.HorizPre = 0x16 // 1e6 cm (10000m) + rr.VertPre = 0x13 // 1e3 cm (10m) ok := false // North l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { - return &ParseError{f, "bad LOC Latitude", l} + return &ParseError{"", "bad LOC Latitude", l} } rr.Latitude = 1000 * 60 * 60 * uint32(i) @@ -743,16 +600,16 @@ func (rr *LOC) parse(c *zlexer, o, f string) *ParseError { if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } - i, e = strconv.ParseUint(l.token, 10, 32) - if e != nil || l.err { - return &ParseError{f, "bad LOC Latitude minutes", l} + if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err { + return &ParseError{"", "bad LOC Latitude minutes", l} + } else { + rr.Latitude += 1000 * 60 * uint32(i) } - rr.Latitude += 1000 * 60 * uint32(i) c.Next() // zBlank l, _ = c.Next() - if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { - return &ParseError{f, "bad LOC Latitude seconds", l} + if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err { + return &ParseError{"", "bad LOC Latitude seconds", l} } else { rr.Latitude += uint32(1000 * i) } @@ -763,14 +620,14 @@ func (rr *LOC) parse(c *zlexer, o, f string) *ParseError { goto East } // If still alive, flag an error - return &ParseError{f, "bad LOC Latitude North/South", l} + return &ParseError{"", "bad LOC Latitude North/South", l} East: // East c.Next() // zBlank l, _ = c.Next() - if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err { - return &ParseError{f, "bad LOC Longitude", l} + if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err { + return &ParseError{"", "bad LOC Longitude", l} } else { rr.Longitude = 1000 * 60 * 60 * uint32(i) } @@ -780,15 +637,15 @@ East: if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } - if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err { - return &ParseError{f, "bad LOC Longitude minutes", l} + if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err { + return &ParseError{"", "bad LOC Longitude minutes", l} } else { rr.Longitude += 1000 * 60 * uint32(i) } c.Next() // zBlank l, _ = c.Next() - if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { - return &ParseError{f, "bad LOC Longitude seconds", l} + if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err { + return &ParseError{"", "bad LOC Longitude seconds", l} } else { rr.Longitude += uint32(1000 * i) } @@ -799,19 +656,19 @@ East: goto Altitude } // If still alive, flag an error - return &ParseError{f, "bad LOC Longitude East/West", l} + return &ParseError{"", "bad LOC Longitude East/West", l} Altitude: c.Next() // zBlank l, _ = c.Next() if len(l.token) == 0 || l.err { - return &ParseError{f, "bad LOC Altitude", l} + return &ParseError{"", "bad LOC Altitude", l} } if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { l.token = l.token[0 : len(l.token)-1] } - if i, e := strconv.ParseFloat(l.token, 32); e != nil { - return &ParseError{f, "bad LOC Altitude", l} + if i, err := strconv.ParseFloat(l.token, 32); err != nil { + return &ParseError{"", "bad LOC Altitude", l} } else { rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5) } @@ -824,52 +681,48 @@ Altitude: case zString: switch count { case 0: // Size - e, m, ok := stringToCm(l.token) + exp, m, ok := stringToCm(l.token) if !ok { - return &ParseError{f, "bad LOC Size", l} + return &ParseError{"", "bad LOC Size", l} } - rr.Size = e&0x0f | m<<4&0xf0 + rr.Size = exp&0x0f | m<<4&0xf0 case 1: // HorizPre - e, m, ok := stringToCm(l.token) + exp, m, ok := stringToCm(l.token) if !ok { - return &ParseError{f, "bad LOC HorizPre", l} + return &ParseError{"", "bad LOC HorizPre", l} } - rr.HorizPre = e&0x0f | m<<4&0xf0 + rr.HorizPre = exp&0x0f | m<<4&0xf0 case 2: // VertPre - e, m, ok := stringToCm(l.token) + exp, m, ok := stringToCm(l.token) if !ok { - return &ParseError{f, "bad LOC VertPre", l} + return &ParseError{"", "bad LOC VertPre", l} } - rr.VertPre = e&0x0f | m<<4&0xf0 + rr.VertPre = exp&0x0f | m<<4&0xf0 } count++ case zBlank: // Ok default: - return &ParseError{f, "bad LOC Size, HorizPre or VertPre", l} + return &ParseError{"", "bad LOC Size, HorizPre or VertPre", l} } l, _ = c.Next() } return nil } -func (rr *HIP) parse(c *zlexer, o, f string) *ParseError { +func (rr *HIP) parse(c *zlexer, o string) *ParseError { // HitLength is not represented l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{f, "bad HIP PublicKeyAlgorithm", l} + return &ParseError{"", "bad HIP PublicKeyAlgorithm", l} } rr.PublicKeyAlgorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString if len(l.token) == 0 || l.err { - return &ParseError{f, "bad HIP Hit", l} + return &ParseError{"", "bad HIP Hit", l} } rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. rr.HitLength = uint8(len(rr.Hit)) / 2 @@ -877,7 +730,7 @@ func (rr *HIP) parse(c *zlexer, o, f string) *ParseError { c.Next() // zBlank l, _ = c.Next() // zString if len(l.token) == 0 || l.err { - return &ParseError{f, "bad HIP PublicKey", l} + return &ParseError{"", "bad HIP PublicKey", l} } rr.PublicKey = l.token // This cannot contain spaces rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey))) @@ -890,13 +743,13 @@ func (rr *HIP) parse(c *zlexer, o, f string) *ParseError { case zString: name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad HIP RendezvousServers", l} + return &ParseError{"", "bad HIP RendezvousServers", l} } xs = append(xs, name) case zBlank: // Ok default: - return &ParseError{f, "bad HIP RendezvousServers", l} + return &ParseError{"", "bad HIP RendezvousServers", l} } l, _ = c.Next() } @@ -905,16 +758,12 @@ func (rr *HIP) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *CERT) parse(c *zlexer, o, f string) *ParseError { +func (rr *CERT) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - if v, ok := StringToCertType[l.token]; ok { rr.Type = v - } else if i, e := strconv.ParseUint(l.token, 10, 16); e != nil { - return &ParseError{f, "bad CERT Type", l} + } else if i, err := strconv.ParseUint(l.token, 10, 16); err != nil { + return &ParseError{"", "bad CERT Type", l} } else { rr.Type = uint16(i) } @@ -922,19 +771,19 @@ func (rr *CERT) parse(c *zlexer, o, f string) *ParseError { l, _ = c.Next() // zString i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad CERT KeyTag", l} + return &ParseError{"", "bad CERT KeyTag", l} } rr.KeyTag = uint16(i) 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 { - return &ParseError{f, "bad CERT Algorithm", l} + } else if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { + return &ParseError{"", "bad CERT Algorithm", l} } else { rr.Algorithm = uint8(i) } - s, e1 := endingToString(c, "bad CERT Certificate", f) + s, e1 := endingToString(c, "bad CERT Certificate") if e1 != nil { return e1 } @@ -942,8 +791,8 @@ func (rr *CERT) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *OPENPGPKEY) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToString(c, "bad OPENPGPKEY PublicKey", f) +func (rr *OPENPGPKEY) parse(c *zlexer, o string) *ParseError { + s, e := endingToString(c, "bad OPENPGPKEY PublicKey") if e != nil { return e } @@ -951,25 +800,22 @@ func (rr *OPENPGPKEY) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *CSYNC) parse(c *zlexer, o, f string) *ParseError { +func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } j, e := strconv.ParseUint(l.token, 10, 32) if e != nil { // Serial must be a number - return &ParseError{f, "bad CSYNC serial", l} + return &ParseError{"", "bad CSYNC serial", l} } rr.Serial = uint32(j) c.Next() // zBlank l, _ = c.Next() - j, e = strconv.ParseUint(l.token, 10, 16) - if e != nil { + j, e1 := strconv.ParseUint(l.token, 10, 16) + if e1 != nil { // Serial must be a number - return &ParseError{f, "bad CSYNC flags", l} + return &ParseError{"", "bad CSYNC flags", l} } rr.Flags = uint16(j) @@ -987,38 +833,32 @@ func (rr *CSYNC) parse(c *zlexer, o, f string) *ParseError { tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { - return &ParseError{f, "bad CSYNC TypeBitMap", l} + return &ParseError{"", "bad CSYNC TypeBitMap", l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: - return &ParseError{f, "bad CSYNC TypeBitMap", l} + return &ParseError{"", "bad CSYNC TypeBitMap", l} } l, _ = c.Next() } return nil } -func (rr *SIG) parse(c *zlexer, o, f string) *ParseError { - return rr.RRSIG.parse(c, o, f) -} +func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) } -func (rr *RRSIG) parse(c *zlexer, o, f string) *ParseError { +func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - tokenUpper := strings.ToUpper(l.token) if t, ok := StringToType[tokenUpper]; !ok { if strings.HasPrefix(tokenUpper, "TYPE") { t, ok = typeToInt(l.token) if !ok { - return &ParseError{f, "bad RRSIG Typecovered", l} + return &ParseError{"", "bad RRSIG Typecovered", l} } rr.TypeCovered = t } else { - return &ParseError{f, "bad RRSIG Typecovered", l} + return &ParseError{"", "bad RRSIG Typecovered", l} } } else { rr.TypeCovered = t @@ -1026,25 +866,25 @@ func (rr *RRSIG) parse(c *zlexer, o, f string) *ParseError { c.Next() // zBlank l, _ = c.Next() - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad RRSIG Algorithm", l} + i, e := strconv.ParseUint(l.token, 10, 8) + if e != nil || l.err { + return &ParseError{"", "bad RRSIG Algorithm", l} } rr.Algorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad RRSIG Labels", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad RRSIG Labels", l} } rr.Labels = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 32) - if err != nil || l.err { - return &ParseError{f, "bad RRSIG OrigTtl", l} + i, e2 := strconv.ParseUint(l.token, 10, 32) + if e2 != nil || l.err { + return &ParseError{"", "bad RRSIG OrigTtl", l} } rr.OrigTtl = uint32(i) @@ -1056,7 +896,7 @@ func (rr *RRSIG) parse(c *zlexer, o, f string) *ParseError { // TODO(miek): error out on > MAX_UINT32, same below rr.Expiration = uint32(i) } else { - return &ParseError{f, "bad RRSIG Expiration", l} + return &ParseError{"", "bad RRSIG Expiration", l} } } else { rr.Expiration = i @@ -1068,7 +908,7 @@ func (rr *RRSIG) parse(c *zlexer, o, f string) *ParseError { if i, err := strconv.ParseInt(l.token, 10, 64); err == nil { rr.Inception = uint32(i) } else { - return &ParseError{f, "bad RRSIG Inception", l} + return &ParseError{"", "bad RRSIG Inception", l} } } else { rr.Inception = i @@ -1076,9 +916,9 @@ func (rr *RRSIG) parse(c *zlexer, o, f string) *ParseError { c.Next() // zBlank l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 16) - if err != nil || l.err { - return &ParseError{f, "bad RRSIG KeyTag", l} + i, e3 := strconv.ParseUint(l.token, 10, 16) + if e3 != nil || l.err { + return &ParseError{"", "bad RRSIG KeyTag", l} } rr.KeyTag = uint16(i) @@ -1087,29 +927,24 @@ func (rr *RRSIG) parse(c *zlexer, o, f string) *ParseError { rr.SignerName = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad RRSIG SignerName", l} + return &ParseError{"", "bad RRSIG SignerName", l} } rr.SignerName = name - s, e := endingToString(c, "bad RRSIG Signature", f) - if e != nil { - return e + s, e4 := endingToString(c, "bad RRSIG Signature") + if e4 != nil { + return e4 } rr.Signature = s return nil } -func (rr *NSEC) parse(c *zlexer, o, f string) *ParseError { +func (rr *NSEC) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - rr.NextDomain = l.token - if len(l.token) == 0 { // dynamic update rr. - return nil - } - name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad NSEC NextDomain", l} + return &ParseError{"", "bad NSEC NextDomain", l} } rr.NextDomain = name @@ -1127,47 +962,43 @@ func (rr *NSEC) parse(c *zlexer, o, f string) *ParseError { tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { - return &ParseError{f, "bad NSEC TypeBitMap", l} + return &ParseError{"", "bad NSEC TypeBitMap", l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: - return &ParseError{f, "bad NSEC TypeBitMap", l} + return &ParseError{"", "bad NSEC TypeBitMap", l} } l, _ = c.Next() } return nil } -func (rr *NSEC3) parse(c *zlexer, o, f string) *ParseError { +func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{f, "bad NSEC3 Hash", l} + return &ParseError{"", "bad NSEC3 Hash", l} } rr.Hash = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3 Flags", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad NSEC3 Flags", l} } rr.Flags = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3 Iterations", l} + i, e2 := strconv.ParseUint(l.token, 10, 16) + if e2 != nil || l.err { + return &ParseError{"", "bad NSEC3 Iterations", l} } rr.Iterations = uint16(i) c.Next() l, _ = c.Next() if len(l.token) == 0 || l.err { - return &ParseError{f, "bad NSEC3 Salt", l} + return &ParseError{"", "bad NSEC3 Salt", l} } if l.token != "-" { rr.SaltLength = uint8(len(l.token)) / 2 @@ -1177,7 +1008,7 @@ func (rr *NSEC3) parse(c *zlexer, o, f string) *ParseError { c.Next() l, _ = c.Next() if len(l.token) == 0 || l.err { - return &ParseError{f, "bad NSEC3 NextDomain", l} + return &ParseError{"", "bad NSEC3 NextDomain", l} } rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) rr.NextDomain = l.token @@ -1196,60 +1027,52 @@ func (rr *NSEC3) parse(c *zlexer, o, f string) *ParseError { tokenUpper := strings.ToUpper(l.token) if k, ok = StringToType[tokenUpper]; !ok { if k, ok = typeToInt(l.token); !ok { - return &ParseError{f, "bad NSEC3 TypeBitMap", l} + return &ParseError{"", "bad NSEC3 TypeBitMap", l} } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: - return &ParseError{f, "bad NSEC3 TypeBitMap", l} + return &ParseError{"", "bad NSEC3 TypeBitMap", l} } l, _ = c.Next() } return nil } -func (rr *NSEC3PARAM) parse(c *zlexer, o, f string) *ParseError { +func (rr *NSEC3PARAM) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{f, "bad NSEC3PARAM Hash", l} + return &ParseError{"", "bad NSEC3PARAM Hash", l} } rr.Hash = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3PARAM Flags", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad NSEC3PARAM Flags", l} } rr.Flags = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3PARAM Iterations", l} + i, e2 := strconv.ParseUint(l.token, 10, 16) + if e2 != nil || l.err { + return &ParseError{"", "bad NSEC3PARAM Iterations", l} } rr.Iterations = uint16(i) c.Next() l, _ = c.Next() if l.token != "-" { - rr.SaltLength = uint8(len(l.token)) + rr.SaltLength = uint8(len(l.token) / 2) rr.Salt = l.token } - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *EUI48) parse(c *zlexer, o, f string) *ParseError { +func (rr *EUI48) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - if len(l.token) != 17 || l.err { - return &ParseError{f, "bad EUI48 Address", l} + return &ParseError{"", "bad EUI48 Address", l} } addr := make([]byte, 12) dash := 0 @@ -1258,7 +1081,7 @@ func (rr *EUI48) parse(c *zlexer, o, f string) *ParseError { addr[i+1] = l.token[i+1+dash] dash++ if l.token[i+1+dash] != '-' { - return &ParseError{f, "bad EUI48 Address", l} + return &ParseError{"", "bad EUI48 Address", l} } } addr[10] = l.token[15] @@ -1266,20 +1089,16 @@ func (rr *EUI48) parse(c *zlexer, o, f string) *ParseError { i, e := strconv.ParseUint(string(addr), 16, 48) if e != nil { - return &ParseError{f, "bad EUI48 Address", l} + return &ParseError{"", "bad EUI48 Address", l} } rr.Address = i - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *EUI64) parse(c *zlexer, o, f string) *ParseError { +func (rr *EUI64) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - if len(l.token) != 23 || l.err { - return &ParseError{f, "bad EUI64 Address", l} + return &ParseError{"", "bad EUI64 Address", l} } addr := make([]byte, 16) dash := 0 @@ -1288,7 +1107,7 @@ func (rr *EUI64) parse(c *zlexer, o, f string) *ParseError { addr[i+1] = l.token[i+1+dash] dash++ if l.token[i+1+dash] != '-' { - return &ParseError{f, "bad EUI64 Address", l} + return &ParseError{"", "bad EUI64 Address", l} } } addr[14] = l.token[21] @@ -1296,119 +1115,102 @@ func (rr *EUI64) parse(c *zlexer, o, f string) *ParseError { i, e := strconv.ParseUint(string(addr), 16, 64) if e != nil { - return &ParseError{f, "bad EUI68 Address", l} + return &ParseError{"", "bad EUI68 Address", l} } rr.Address = i - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *SSHFP) parse(c *zlexer, o, f string) *ParseError { +func (rr *SSHFP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{f, "bad SSHFP Algorithm", l} + return &ParseError{"", "bad SSHFP Algorithm", l} } rr.Algorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad SSHFP Type", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad SSHFP Type", l} } rr.Type = uint8(i) c.Next() // zBlank - s, e1 := endingToString(c, "bad SSHFP Fingerprint", f) - if e1 != nil { - return e1 + s, e2 := endingToString(c, "bad SSHFP Fingerprint") + if e2 != nil { + return e2 } rr.FingerPrint = s return nil } -func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, f, typ string) *ParseError { +func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, typ string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad " + typ + " Flags", l} + return &ParseError{"", "bad " + typ + " Flags", l} } rr.Flags = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad " + typ + " Protocol", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad " + typ + " Protocol", l} } rr.Protocol = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad " + typ + " Algorithm", l} + i, e2 := strconv.ParseUint(l.token, 10, 8) + if e2 != nil || l.err { + return &ParseError{"", "bad " + typ + " Algorithm", l} } rr.Algorithm = uint8(i) - s, e1 := endingToString(c, "bad "+typ+" PublicKey", f) - if e1 != nil { - return e1 + s, e3 := endingToString(c, "bad "+typ+" PublicKey") + if e3 != nil { + return e3 } rr.PublicKey = s return nil } -func (rr *DNSKEY) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDNSKEY(c, o, f, "DNSKEY") -} +func (rr *DNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "DNSKEY") } +func (rr *KEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "KEY") } +func (rr *CDNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "CDNSKEY") } +func (rr *DS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DS") } +func (rr *DLV) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DLV") } +func (rr *CDS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "CDS") } -func (rr *KEY) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDNSKEY(c, o, f, "KEY") -} - -func (rr *CDNSKEY) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDNSKEY(c, o, f, "CDNSKEY") -} - -func (rr *RKEY) parse(c *zlexer, o, f string) *ParseError { +func (rr *RKEY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad RKEY Flags", l} + return &ParseError{"", "bad RKEY Flags", l} } rr.Flags = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad RKEY Protocol", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad RKEY Protocol", l} } rr.Protocol = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad RKEY Algorithm", l} + i, e2 := strconv.ParseUint(l.token, 10, 8) + if e2 != nil || l.err { + return &ParseError{"", "bad RKEY Algorithm", l} } rr.Algorithm = uint8(i) - s, e1 := endingToString(c, "bad RKEY PublicKey", f) - if e1 != nil { - return e1 + s, e3 := endingToString(c, "bad RKEY PublicKey") + if e3 != nil { + return e3 } rr.PublicKey = s return nil } -func (rr *EID) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToString(c, "bad EID Endpoint", f) +func (rr *EID) parse(c *zlexer, o string) *ParseError { + s, e := endingToString(c, "bad EID Endpoint") if e != nil { return e } @@ -1416,8 +1218,8 @@ func (rr *EID) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *NIMLOC) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToString(c, "bad NIMLOC Locator", f) +func (rr *NIMLOC) parse(c *zlexer, o string) *ParseError { + s, e := endingToString(c, "bad NIMLOC Locator") if e != nil { return e } @@ -1425,52 +1227,44 @@ func (rr *NIMLOC) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *GPOS) parse(c *zlexer, o, f string) *ParseError { +func (rr *GPOS) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - _, e := strconv.ParseFloat(l.token, 64) if e != nil || l.err { - return &ParseError{f, "bad GPOS Longitude", l} + return &ParseError{"", "bad GPOS Longitude", l} } rr.Longitude = l.token c.Next() // zBlank l, _ = c.Next() - _, e = strconv.ParseFloat(l.token, 64) - if e != nil || l.err { - return &ParseError{f, "bad GPOS Latitude", l} + _, e1 := strconv.ParseFloat(l.token, 64) + if e1 != nil || l.err { + return &ParseError{"", "bad GPOS Latitude", l} } rr.Latitude = l.token c.Next() // zBlank l, _ = c.Next() - _, e = strconv.ParseFloat(l.token, 64) - if e != nil || l.err { - return &ParseError{f, "bad GPOS Altitude", l} + _, e2 := strconv.ParseFloat(l.token, 64) + if e2 != nil || l.err { + return &ParseError{"", "bad GPOS Altitude", l} } rr.Altitude = l.token - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *DS) parseDS(c *zlexer, o, f, typ string) *ParseError { +func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad " + typ + " KeyTag", l} + return &ParseError{"", "bad " + typ + " KeyTag", l} } rr.KeyTag = uint16(i) c.Next() // zBlank l, _ = c.Next() - if i, e = strconv.ParseUint(l.token, 10, 8); e != nil { + if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { tokenUpper := strings.ToUpper(l.token) i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { - return &ParseError{f, "bad " + typ + " Algorithm", l} + return &ParseError{"", "bad " + typ + " Algorithm", l} } rr.Algorithm = i } else { @@ -1478,49 +1272,33 @@ func (rr *DS) parseDS(c *zlexer, o, f, typ string) *ParseError { } c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad " + typ + " DigestType", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad " + typ + " DigestType", l} } rr.DigestType = uint8(i) - s, e1 := endingToString(c, "bad "+typ+" Digest", f) - if e1 != nil { - return e1 + s, e2 := endingToString(c, "bad "+typ+" Digest") + if e2 != nil { + return e2 } rr.Digest = s return nil } -func (rr *DS) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDS(c, o, f, "DS") -} - -func (rr *DLV) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDS(c, o, f, "DLV") -} - -func (rr *CDS) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDS(c, o, f, "CDS") -} - -func (rr *TA) parse(c *zlexer, o, f string) *ParseError { +func (rr *TA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad TA KeyTag", l} + return &ParseError{"", "bad TA KeyTag", l} } rr.KeyTag = uint16(i) c.Next() // zBlank l, _ = c.Next() - if i, e := strconv.ParseUint(l.token, 10, 8); e != nil { + if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { tokenUpper := strings.ToUpper(l.token) i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { - return &ParseError{f, "bad TA Algorithm", l} + return &ParseError{"", "bad TA Algorithm", l} } rr.Algorithm = i } else { @@ -1528,113 +1306,105 @@ func (rr *TA) parse(c *zlexer, o, f string) *ParseError { } c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad TA DigestType", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad TA DigestType", l} } rr.DigestType = uint8(i) - s, err := endingToString(c, "bad TA Digest", f) - if err != nil { - return err + s, e2 := endingToString(c, "bad TA Digest") + if e2 != nil { + return e2 } rr.Digest = s return nil } -func (rr *TLSA) parse(c *zlexer, o, f string) *ParseError { +func (rr *TLSA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{f, "bad TLSA Usage", l} + return &ParseError{"", "bad TLSA Usage", l} } rr.Usage = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad TLSA Selector", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad TLSA Selector", l} } rr.Selector = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad TLSA MatchingType", l} + i, e2 := strconv.ParseUint(l.token, 10, 8) + if e2 != nil || l.err { + return &ParseError{"", "bad TLSA MatchingType", l} } rr.MatchingType = uint8(i) // So this needs be e2 (i.e. different than e), because...??t - s, e2 := endingToString(c, "bad TLSA Certificate", f) - if e2 != nil { - return e2 + s, e3 := endingToString(c, "bad TLSA Certificate") + if e3 != nil { + return e3 } rr.Certificate = s return nil } -func (rr *SMIMEA) parse(c *zlexer, o, f string) *ParseError { +func (rr *SMIMEA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 8) if e != nil || l.err { - return &ParseError{f, "bad SMIMEA Usage", l} + return &ParseError{"", "bad SMIMEA Usage", l} } rr.Usage = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad SMIMEA Selector", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad SMIMEA Selector", l} } rr.Selector = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad SMIMEA MatchingType", l} + i, e2 := strconv.ParseUint(l.token, 10, 8) + if e2 != nil || l.err { + return &ParseError{"", "bad SMIMEA MatchingType", l} } rr.MatchingType = uint8(i) // So this needs be e2 (i.e. different than e), because...??t - s, e2 := endingToString(c, "bad SMIMEA Certificate", f) - if e2 != nil { - return e2 + s, e3 := endingToString(c, "bad SMIMEA Certificate") + if e3 != nil { + return e3 } rr.Certificate = s return nil } -func (rr *RFC3597) parse(c *zlexer, o, f string) *ParseError { +func (rr *RFC3597) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if l.token != "\\#" { - return &ParseError{f, "bad RFC3597 Rdata", l} + return &ParseError{"", "bad RFC3597 Rdata", l} } c.Next() // zBlank l, _ = c.Next() rdlength, e := strconv.Atoi(l.token) if e != nil || l.err { - return &ParseError{f, "bad RFC3597 Rdata ", l} + return &ParseError{"", "bad RFC3597 Rdata ", l} } - s, e1 := endingToString(c, "bad RFC3597 Rdata", f) + s, e1 := endingToString(c, "bad RFC3597 Rdata") if e1 != nil { return e1 } if rdlength*2 != len(s) { - return &ParseError{f, "bad RFC3597 Rdata", l} + return &ParseError{"", "bad RFC3597 Rdata", l} } rr.Rdata = s return nil } -func (rr *SPF) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToTxtSlice(c, "bad SPF Txt", f) +func (rr *SPF) parse(c *zlexer, o string) *ParseError { + s, e := endingToTxtSlice(c, "bad SPF Txt") if e != nil { return e } @@ -1642,8 +1412,8 @@ func (rr *SPF) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *AVC) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToTxtSlice(c, "bad AVC Txt", f) +func (rr *AVC) parse(c *zlexer, o string) *ParseError { + s, e := endingToTxtSlice(c, "bad AVC Txt") if e != nil { return e } @@ -1651,9 +1421,9 @@ func (rr *AVC) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *TXT) parse(c *zlexer, o, f string) *ParseError { +func (rr *TXT) parse(c *zlexer, o string) *ParseError { // no zBlank reading here, because all this rdata is TXT - s, e := endingToTxtSlice(c, "bad TXT Txt", f) + s, e := endingToTxtSlice(c, "bad TXT Txt") if e != nil { return e } @@ -1662,8 +1432,8 @@ func (rr *TXT) parse(c *zlexer, o, f string) *ParseError { } // identical to setTXT -func (rr *NINFO) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToTxtSlice(c, "bad NINFO ZSData", f) +func (rr *NINFO) parse(c *zlexer, o string) *ParseError { + s, e := endingToTxtSlice(c, "bad NINFO ZSData") if e != nil { return e } @@ -1671,40 +1441,36 @@ func (rr *NINFO) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *URI) parse(c *zlexer, o, f string) *ParseError { +func (rr *URI) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad URI Priority", l} + return &ParseError{"", "bad URI Priority", l} } rr.Priority = uint16(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad URI Weight", l} + i, e1 := strconv.ParseUint(l.token, 10, 16) + if e1 != nil || l.err { + return &ParseError{"", "bad URI Weight", l} } rr.Weight = uint16(i) c.Next() // zBlank - s, err := endingToTxtSlice(c, "bad URI Target", f) - if err != nil { - return err + s, e2 := endingToTxtSlice(c, "bad URI Target") + if e2 != nil { + return e2 } if len(s) != 1 { - return &ParseError{f, "bad URI Target", l} + return &ParseError{"", "bad URI Target", l} } rr.Target = s[0] return nil } -func (rr *DHCID) parse(c *zlexer, o, f string) *ParseError { +func (rr *DHCID) parse(c *zlexer, o string) *ParseError { // awesome record to parse! - s, e := endingToString(c, "bad DHCID Digest", f) + s, e := endingToString(c, "bad DHCID Digest") if e != nil { return e } @@ -1712,56 +1478,44 @@ func (rr *DHCID) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *NID) parse(c *zlexer, o, f string) *ParseError { +func (rr *NID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad NID Preference", l} + return &ParseError{"", "bad NID Preference", l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - u, err := stringToNodeID(l) - if err != nil || l.err { - return err + u, e1 := stringToNodeID(l) + if e1 != nil || l.err { + return e1 } rr.NodeID = u - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *L32) parse(c *zlexer, o, f string) *ParseError { +func (rr *L32) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad L32 Preference", l} + return &ParseError{"", "bad L32 Preference", l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString rr.Locator32 = net.ParseIP(l.token) if rr.Locator32 == nil || l.err { - return &ParseError{f, "bad L32 Locator", l} + return &ParseError{"", "bad L32 Locator", l} } - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *LP) parse(c *zlexer, o, f string) *ParseError { +func (rr *LP) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad LP Preference", l} + return &ParseError{"", "bad LP Preference", l} } rr.Preference = uint16(i) @@ -1770,64 +1524,51 @@ func (rr *LP) parse(c *zlexer, o, f string) *ParseError { rr.Fqdn = l.token name, nameOk := toAbsoluteName(l.token, o) if l.err || !nameOk { - return &ParseError{f, "bad LP Fqdn", l} + return &ParseError{"", "bad LP Fqdn", l} } rr.Fqdn = name - - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *L64) parse(c *zlexer, o, f string) *ParseError { +func (rr *L64) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad L64 Preference", l} + return &ParseError{"", "bad L64 Preference", l} } rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - u, err := stringToNodeID(l) - if err != nil || l.err { - return err + u, e1 := stringToNodeID(l) + if e1 != nil || l.err { + return e1 } rr.Locator64 = u - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *UID) parse(c *zlexer, o, f string) *ParseError { +func (rr *UID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { - return &ParseError{f, "bad UID Uid", l} + return &ParseError{"", "bad UID Uid", l} } rr.Uid = uint32(i) - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *GID) parse(c *zlexer, o, f string) *ParseError { +func (rr *GID) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 32) if e != nil || l.err { - return &ParseError{f, "bad GID Gid", l} + return &ParseError{"", "bad GID Gid", l} } rr.Gid = uint32(i) - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *UINFO) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToTxtSlice(c, "bad UINFO Uinfo", f) +func (rr *UINFO) parse(c *zlexer, o string) *ParseError { + s, e := endingToTxtSlice(c, "bad UINFO Uinfo") if e != nil { return e } @@ -1838,15 +1579,11 @@ func (rr *UINFO) parse(c *zlexer, o, f string) *ParseError { return nil } -func (rr *PX) parse(c *zlexer, o, f string) *ParseError { +func (rr *PX) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - i, e := strconv.ParseUint(l.token, 10, 16) if e != nil || l.err { - return &ParseError{f, "bad PX Preference", l} + return &ParseError{"", "bad PX Preference", l} } rr.Preference = uint16(i) @@ -1855,7 +1592,7 @@ func (rr *PX) parse(c *zlexer, o, f string) *ParseError { rr.Map822 = l.token map822, map822Ok := toAbsoluteName(l.token, o) if l.err || !map822Ok { - return &ParseError{f, "bad PX Map822", l} + return &ParseError{"", "bad PX Map822", l} } rr.Map822 = map822 @@ -1864,82 +1601,142 @@ func (rr *PX) parse(c *zlexer, o, f string) *ParseError { rr.Mapx400 = l.token mapx400, mapx400Ok := toAbsoluteName(l.token, o) if l.err || !mapx400Ok { - return &ParseError{f, "bad PX Mapx400", l} + return &ParseError{"", "bad PX Mapx400", l} } rr.Mapx400 = mapx400 - - return slurpRemainder(c, f) + return slurpRemainder(c) } -func (rr *CAA) parse(c *zlexer, o, f string) *ParseError { +func (rr *CAA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad CAA Flag", l} + i, e := strconv.ParseUint(l.token, 10, 8) + if e != nil || l.err { + return &ParseError{"", "bad CAA Flag", l} } rr.Flag = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString if l.value != zString { - return &ParseError{f, "bad CAA Tag", l} + return &ParseError{"", "bad CAA Tag", l} } rr.Tag = l.token c.Next() // zBlank - s, e := endingToTxtSlice(c, "bad CAA Value", f) - if e != nil { - return e + s, e1 := endingToTxtSlice(c, "bad CAA Value") + if e1 != nil { + return e1 } if len(s) != 1 { - return &ParseError{f, "bad CAA Value", l} + return &ParseError{"", "bad CAA Value", l} } rr.Value = s[0] return nil } -func (rr *TKEY) parse(c *zlexer, o, f string) *ParseError { +func (rr *TKEY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() // Algorithm if l.value != zString { - return &ParseError{f, "bad TKEY algorithm", l} + return &ParseError{"", "bad TKEY algorithm", l} } rr.Algorithm = l.token c.Next() // zBlank // Get the key length and key values l, _ = c.Next() - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad TKEY key length", l} + i, e := strconv.ParseUint(l.token, 10, 8) + if e != nil || l.err { + return &ParseError{"", "bad TKEY key length", l} } rr.KeySize = uint16(i) c.Next() // zBlank l, _ = c.Next() if l.value != zString { - return &ParseError{f, "bad TKEY key", l} + return &ParseError{"", "bad TKEY key", l} } rr.Key = l.token c.Next() // zBlank // Get the otherdata length and string data l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad TKEY otherdata length", l} + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { + return &ParseError{"", "bad TKEY otherdata length", l} } rr.OtherLen = uint16(i) c.Next() // zBlank l, _ = c.Next() if l.value != zString { - return &ParseError{f, "bad TKEY otherday", l} + return &ParseError{"", "bad TKEY otherday", l} } rr.OtherData = l.token - + return nil +} + +func (rr *APL) parse(c *zlexer, o string) *ParseError { + var prefixes []APLPrefix + + for { + l, _ := c.Next() + if l.value == zNewline || l.value == zEOF { + break + } + if l.value == zBlank && prefixes != nil { + continue + } + if l.value != zString { + return &ParseError{"", "unexpected APL field", l} + } + + // Expected format: [!]afi:address/prefix + + colon := strings.IndexByte(l.token, ':') + if colon == -1 { + return &ParseError{"", "missing colon in APL field", l} + } + + family, cidr := l.token[:colon], l.token[colon+1:] + + var negation bool + if family != "" && family[0] == '!' { + negation = true + family = family[1:] + } + + afi, e := strconv.ParseUint(family, 10, 16) + if e != nil { + return &ParseError{"", "failed to parse APL family: " + e.Error(), l} + } + var addrLen int + switch afi { + case 1: + addrLen = net.IPv4len + case 2: + addrLen = net.IPv6len + default: + return &ParseError{"", "unrecognized APL family", l} + } + + ip, subnet, e1 := net.ParseCIDR(cidr) + if e1 != nil { + return &ParseError{"", "failed to parse APL address: " + e1.Error(), l} + } + if !ip.Equal(subnet.IP) { + return &ParseError{"", "extra bits in APL address", l} + } + + if len(subnet.IP) != addrLen { + return &ParseError{"", "address mismatch with the APL family", l} + } + + prefixes = append(prefixes, APLPrefix{ + Negation: negation, + Network: *subnet, + }) + } + + rr.Prefixes = prefixes return nil } diff --git a/vendor/github.com/miekg/dns/serve_mux.go b/vendor/github.com/miekg/dns/serve_mux.go index ae304db5..aadb0bf0 100644 --- a/vendor/github.com/miekg/dns/serve_mux.go +++ b/vendor/github.com/miekg/dns/serve_mux.go @@ -1,7 +1,6 @@ package dns import ( - "strings" "sync" ) @@ -36,33 +35,9 @@ func (mux *ServeMux) match(q string, t uint16) Handler { return nil } + q = CanonicalName(q) + 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 { @@ -90,7 +65,7 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) { if mux.z == nil { mux.z = make(map[string]Handler) } - mux.z[Fqdn(pattern)] = handler + mux.z[CanonicalName(pattern)] = handler mux.m.Unlock() } @@ -105,7 +80,7 @@ func (mux *ServeMux) HandleRemove(pattern string) { panic("dns: invalid pattern " + pattern) } mux.m.Lock() - delete(mux.z, Fqdn(pattern)) + delete(mux.z, CanonicalName(pattern)) mux.m.Unlock() } diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index 354f7712..3cf1a024 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -560,18 +560,24 @@ func (srv *Server) serveDNS(m []byte, w *response) { req := new(Msg) req.setHdr(dh) - switch srv.MsgAcceptFunc(dh) { + switch action := srv.MsgAcceptFunc(dh); action { case MsgAccept: if req.unpack(dh, m, off) == nil { break } fallthrough - case MsgReject: + case MsgReject, MsgRejectNotImplemented: + opcode := req.Opcode req.SetRcodeFormatError(req) + req.Zero = false + if action == MsgRejectNotImplemented { + req.Opcode = opcode + req.Rcode = RcodeNotImplemented + } + // Are we allowed to delete any OPT records here? req.Ns, req.Answer, req.Extra = nil, nil, nil - req.Zero = false w.WriteMsg(req) fallthrough diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go index afa462fa..9451f1a8 100644 --- a/vendor/github.com/miekg/dns/tsig.go +++ b/vendor/github.com/miekg/dns/tsig.go @@ -40,7 +40,7 @@ type TSIG struct { // TSIG has no official presentation format, but this will suffice. func (rr *TSIG) String() string { - s := "\n;; TSIG PSEUDOSECTION:\n" + s := "\n;; TSIG PSEUDOSECTION:\n; " // add another semi-colon to signify TSIG does not have a presentation format s += rr.Hdr.String() + " " + rr.Algorithm + " " + tsigTimeToString(rr.TimeSigned) + @@ -54,7 +54,7 @@ func (rr *TSIG) String() string { return s } -func (rr *TSIG) parse(c *zlexer, origin, file string) *ParseError { +func (rr *TSIG) parse(c *zlexer, origin string) *ParseError { panic("dns: internal error: parse should never be called on TSIG") } @@ -115,7 +115,7 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s t := new(TSIG) var h hash.Hash - switch strings.ToLower(rr.Algorithm) { + switch CanonicalName(rr.Algorithm) { case HmacMD5: h = hmac.New(md5.New, rawsecret) case HmacSHA1: @@ -182,7 +182,7 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error { } var h hash.Hash - switch strings.ToLower(tsig.Algorithm) { + switch CanonicalName(tsig.Algorithm) { case HmacMD5: h = hmac.New(md5.New, rawsecret) case HmacSHA1: @@ -232,10 +232,10 @@ func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []b tsigvar = tsigvar[:n] } else { tsig := new(tsigWireFmt) - tsig.Name = strings.ToLower(rr.Hdr.Name) + tsig.Name = CanonicalName(rr.Hdr.Name) tsig.Class = ClassANY tsig.Ttl = rr.Hdr.Ttl - tsig.Algorithm = strings.ToLower(rr.Algorithm) + tsig.Algorithm = CanonicalName(rr.Algorithm) tsig.TimeSigned = rr.TimeSigned tsig.Fudge = rr.Fudge tsig.Error = rr.Error diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index afee0d30..b8c75557 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -1,6 +1,7 @@ package dns import ( + "bytes" "fmt" "net" "strconv" @@ -61,6 +62,7 @@ const ( TypeCERT uint16 = 37 TypeDNAME uint16 = 39 TypeOPT uint16 = 41 // EDNS + TypeAPL uint16 = 42 TypeDS uint16 = 43 TypeSSHFP uint16 = 44 TypeRRSIG uint16 = 46 @@ -163,11 +165,11 @@ const ( _RD = 1 << 8 // recursion desired _RA = 1 << 7 // recursion available _Z = 1 << 6 // Z - _AD = 1 << 5 // authticated data + _AD = 1 << 5 // authenticated data _CD = 1 << 4 // checking disabled ) -// Various constants used in the LOC RR, See RFC 1887. +// Various constants used in the LOC RR. See RFC 1887. const ( LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2. @@ -229,7 +231,7 @@ func (q *Question) String() (s string) { return s } -// ANY is a wildcard record. See RFC 1035, Section 3.2.3. ANY +// ANY is a wild card record. See RFC 1035, Section 3.2.3. ANY // is named "*" there. type ANY struct { Hdr RR_Header @@ -238,7 +240,7 @@ type ANY struct { func (rr *ANY) String() string { return rr.Hdr.String() } -func (rr *ANY) parse(c *zlexer, origin, file string) *ParseError { +func (rr *ANY) parse(c *zlexer, origin string) *ParseError { panic("dns: internal error: parse should never be called on ANY") } @@ -253,7 +255,7 @@ func (rr *NULL) String() string { return ";" + rr.Hdr.String() + rr.Data } -func (rr *NULL) parse(c *zlexer, origin, file string) *ParseError { +func (rr *NULL) parse(c *zlexer, origin string) *ParseError { panic("dns: internal error: parse should never be called on NULL") } @@ -438,25 +440,54 @@ func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } func sprintName(s string) string { 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]) + if dst.Len() != 0 { + dst.WriteString(s[i : i+2]) + } i += 2 continue } b, n := nextByte(s, i) - switch { - case n == 0: - i++ // dangling back slash - case b == '.': - dst.WriteByte('.') + if n == 0 { + i++ + continue + } + if b == '.' { + if dst.Len() != 0 { + dst.WriteByte('.') + } + i += n + continue + } + switch b { + case ' ', '\'', '@', ';', '(', ')', '"', '\\': // additional chars to escape + if dst.Len() == 0 { + dst.Grow(len(s) * 2) + dst.WriteString(s[:i]) + } + dst.WriteByte('\\') + dst.WriteByte(b) default: - writeDomainNameByte(&dst, b) + if ' ' <= b && b <= '~' { + if dst.Len() != 0 { + dst.WriteByte(b) + } + } else { + if dst.Len() == 0 { + dst.Grow(len(s) * 2) + dst.WriteString(s[:i]) + } + dst.WriteString(escapeByte(b)) + } } i += n } + if dst.Len() == 0 { + return s + } return dst.String() } @@ -510,16 +541,6 @@ func sprintTxt(txt []string) string { return out.String() } -func writeDomainNameByte(s *strings.Builder, b byte) { - switch b { - case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape - s.WriteByte('\\') - s.WriteByte(b) - default: - writeTXTStringByte(s, b) - } -} - func writeTXTStringByte(s *strings.Builder, b byte) { switch { case b == '"' || b == '\\': @@ -738,8 +759,8 @@ type LOC struct { Altitude uint32 } -// cmToM takes a cm value expressed in RFC1876 SIZE mantissa/exponent -// format and returns a string in m (two decimals for the cm) +// cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent +// format and returns a string in m (two decimals for the cm). func cmToM(m, e uint8) string { if e < 2 { if e == 1 { @@ -1334,6 +1355,88 @@ func (rr *CSYNC) len(off int, compression map[string]struct{}) int { return l } +// APL RR. See RFC 3123. +type APL struct { + Hdr RR_Header + Prefixes []APLPrefix `dns:"apl"` +} + +// APLPrefix is an address prefix hold by an APL record. +type APLPrefix struct { + Negation bool + Network net.IPNet +} + +// String returns presentation form of the APL record. +func (rr *APL) String() string { + var sb strings.Builder + sb.WriteString(rr.Hdr.String()) + for i, p := range rr.Prefixes { + if i > 0 { + sb.WriteByte(' ') + } + sb.WriteString(p.str()) + } + return sb.String() +} + +// str returns presentation form of the APL prefix. +func (p *APLPrefix) str() string { + var sb strings.Builder + if p.Negation { + sb.WriteByte('!') + } + + switch len(p.Network.IP) { + case net.IPv4len: + sb.WriteByte('1') + case net.IPv6len: + sb.WriteByte('2') + } + + sb.WriteByte(':') + + switch len(p.Network.IP) { + case net.IPv4len: + sb.WriteString(p.Network.IP.String()) + case net.IPv6len: + // add prefix for IPv4-mapped IPv6 + if v4 := p.Network.IP.To4(); v4 != nil { + sb.WriteString("::ffff:") + } + sb.WriteString(p.Network.IP.String()) + } + + sb.WriteByte('/') + + prefix, _ := p.Network.Mask.Size() + sb.WriteString(strconv.Itoa(prefix)) + + return sb.String() +} + +// equals reports whether two APL prefixes are identical. +func (a *APLPrefix) equals(b *APLPrefix) bool { + return a.Negation == b.Negation && + bytes.Equal(a.Network.IP, b.Network.IP) && + bytes.Equal(a.Network.Mask, b.Network.Mask) +} + +// copy returns a copy of the APL prefix. +func (p *APLPrefix) copy() APLPrefix { + return APLPrefix{ + Negation: p.Negation, + Network: copyNet(p.Network), + } +} + +// len returns size of the prefix in wire format. +func (p *APLPrefix) len() int { + // 4-byte header and the network address prefix (see Section 4 of RFC 3123) + prefix, _ := p.Network.Mask.Size() + return 4 + (prefix+7)/8 +} + // TimeToString translates the RRSIG's incep. and expir. times to the // string representation used when printing the record. // It takes serial arithmetic (RFC 1982) into account. @@ -1390,6 +1493,17 @@ func copyIP(ip net.IP) net.IP { return p } +// copyNet returns a copy of a subnet. +func copyNet(n net.IPNet) net.IPNet { + m := make(net.IPMask, len(n.Mask)) + copy(m, n.Mask) + + return net.IPNet{ + IP: copyIP(n.IP), + Mask: m, + } +} + // SplitN splits a string into N sized string chunks. // This might become an exported function once. func splitN(s string, n int) []string { diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index 204776d9..d2c8f05f 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,13 +3,13 @@ package dns import "fmt" // Version is current version of this library. -var Version = V{1, 1, 13} +var Version = v{1, 1, 29} -// V holds the version of this library. -type V struct { +// v holds the version of this library. +type v struct { Major, Minor, Patch int } -func (v V) String() string { +func (v v) String() string { return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) } diff --git a/vendor/github.com/miekg/dns/xfr.go b/vendor/github.com/miekg/dns/xfr.go index bb4ca3d8..43970e64 100644 --- a/vendor/github.com/miekg/dns/xfr.go +++ b/vendor/github.com/miekg/dns/xfr.go @@ -182,14 +182,17 @@ func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) { // // ch := make(chan *dns.Envelope) // tr := new(dns.Transfer) -// go tr.Out(w, r, ch) +// var wg sync.WaitGroup +// go func() { +// tr.Out(w, r, ch) +// wg.Done() +// }() // ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} // close(ch) -// w.Hijack() -// // w.Close() // Client closes connection +// wg.Wait() // wait until everything is written out +// w.Close() // close connection // -// The server is responsible for sending the correct sequence of RRs through the -// channel ch. +// The server is responsible for sending the correct sequence of RRs through the channel ch. func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error { for x := range ch { r := new(Msg) diff --git a/vendor/github.com/miekg/dns/zduplicate.go b/vendor/github.com/miekg/dns/zduplicate.go index 74389162..a58a8c0c 100644 --- a/vendor/github.com/miekg/dns/zduplicate.go +++ b/vendor/github.com/miekg/dns/zduplicate.go @@ -52,6 +52,23 @@ func (r1 *ANY) isDuplicate(_r2 RR) bool { return true } +func (r1 *APL) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*APL) + if !ok { + return false + } + _ = r2 + if len(r1.Prefixes) != len(r2.Prefixes) { + return false + } + for i := 0; i < len(r1.Prefixes); i++ { + if !r1.Prefixes[i].equals(&r2.Prefixes[i]) { + return false + } + } + return true +} + func (r1 *AVC) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*AVC) if !ok { diff --git a/vendor/github.com/miekg/dns/zmsg.go b/vendor/github.com/miekg/dns/zmsg.go index c4cf4757..02a5dfa4 100644 --- a/vendor/github.com/miekg/dns/zmsg.go +++ b/vendor/github.com/miekg/dns/zmsg.go @@ -36,6 +36,14 @@ func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bo return off, nil } +func (rr *APL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { + off, err = packDataApl(rr.Prefixes, msg, off) + if err != nil { + return off, err + } + return off, nil +} + func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { off, err = packStringTxt(rr.Txt, msg, off) if err != nil { @@ -1127,6 +1135,17 @@ func (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) { return off, nil } +func (rr *APL) unpack(msg []byte, off int) (off1 int, err error) { + rdStart := off + _ = rdStart + + rr.Prefixes, off, err = unpackDataApl(msg, off) + if err != nil { + return off, err + } + return off, nil +} + func (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) { rdStart := off _ = rdStart diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go index f7ec8352..1cbd6d3f 100644 --- a/vendor/github.com/miekg/dns/ztypes.go +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -13,6 +13,7 @@ var TypeToRR = map[uint16]func() RR{ TypeAAAA: func() RR { return new(AAAA) }, TypeAFSDB: func() RR { return new(AFSDB) }, TypeANY: func() RR { return new(ANY) }, + TypeAPL: func() RR { return new(APL) }, TypeAVC: func() RR { return new(AVC) }, TypeCAA: func() RR { return new(CAA) }, TypeCDNSKEY: func() RR { return new(CDNSKEY) }, @@ -87,6 +88,7 @@ var TypeToString = map[uint16]string{ TypeAAAA: "AAAA", TypeAFSDB: "AFSDB", TypeANY: "ANY", + TypeAPL: "APL", TypeATMA: "ATMA", TypeAVC: "AVC", TypeAXFR: "AXFR", @@ -169,6 +171,7 @@ func (rr *A) Header() *RR_Header { return &rr.Hdr } func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } func (rr *ANY) Header() *RR_Header { return &rr.Hdr } +func (rr *APL) Header() *RR_Header { return &rr.Hdr } func (rr *AVC) Header() *RR_Header { return &rr.Hdr } func (rr *CAA) Header() *RR_Header { return &rr.Hdr } func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr } @@ -262,6 +265,13 @@ func (rr *ANY) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) return l } +func (rr *APL) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + for _, x := range rr.Prefixes { + l += x.len() + } + return l +} func (rr *AVC) len(off int, compression map[string]struct{}) int { l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { @@ -673,6 +683,13 @@ func (rr *AFSDB) copy() RR { func (rr *ANY) copy() RR { return &ANY{rr.Hdr} } +func (rr *APL) copy() RR { + Prefixes := make([]APLPrefix, len(rr.Prefixes)) + for i := range rr.Prefixes { + Prefixes[i] = rr.Prefixes[i].copy() + } + return &APL{rr.Hdr, Prefixes} +} func (rr *AVC) copy() RR { Txt := make([]string, len(rr.Txt)) copy(Txt, rr.Txt) diff --git a/vendor/github.com/mitchellh/mapstructure/.travis.yml b/vendor/github.com/mitchellh/mapstructure/.travis.yml index 1689c7d7..5e31a95a 100644 --- a/vendor/github.com/mitchellh/mapstructure/.travis.yml +++ b/vendor/github.com/mitchellh/mapstructure/.travis.yml @@ -1,8 +1,9 @@ language: go go: - - "1.11.x" + - "1.14.x" - tip script: - go test + - go test -bench . -benchmem diff --git a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md index 3b3cb723..b3a36fef 100644 --- a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md +++ b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md @@ -1,3 +1,31 @@ +## 1.3.0 + +* Added `",omitempty"` support. This will ignore zero values in the source + structure when encoding. [GH-145] + +## 1.2.3 + +* Fix duplicate entries in Keys list with pointer values. [GH-185] + +## 1.2.2 + +* Do not add unsettable (unexported) values to the unused metadata key + or "remain" value. [GH-150] + +## 1.2.1 + +* Go modules checksum mismatch fix + +## 1.2.0 + +* Added support to capture unused values in a field using the `",remain"` value + in the mapstructure tag. There is an example to showcase usage. +* Added `DecoderConfig` option to always squash embedded structs +* `json.Number` can decode into `uint` types +* Empty slices are preserved and not replaced with nil slices +* Fix panic that can occur in when decoding a map into a nil slice of structs +* Improved package documentation for godoc + ## 1.1.2 * Fix error when decode hook decodes interface implementation into interface diff --git a/vendor/github.com/mitchellh/mapstructure/go.mod b/vendor/github.com/mitchellh/mapstructure/go.mod index d2a71256..a03ae973 100644 --- a/vendor/github.com/mitchellh/mapstructure/go.mod +++ b/vendor/github.com/mitchellh/mapstructure/go.mod @@ -1 +1,3 @@ module github.com/mitchellh/mapstructure + +go 1.14 diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go index 256ee63f..cc456138 100644 --- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go +++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go @@ -1,10 +1,150 @@ -// Package mapstructure exposes functionality to convert an arbitrary -// map[string]interface{} into a native Go structure. +// Package mapstructure exposes functionality to convert one arbitrary +// Go type into another, typically to convert a map[string]interface{} +// into a native Go structure. // // The Go structure can be arbitrarily complex, containing slices, // other structs, etc. and the decoder will properly decode nested // maps and so on into the proper structures in the native Go struct. // See the examples to see what the decoder is capable of. +// +// The simplest function to start with is Decode. +// +// Field Tags +// +// When decoding to a struct, mapstructure will use the field name by +// default to perform the mapping. For example, if a struct has a field +// "Username" then mapstructure will look for a key in the source value +// of "username" (case insensitive). +// +// type User struct { +// Username string +// } +// +// You can change the behavior of mapstructure by using struct tags. +// The default struct tag that mapstructure looks for is "mapstructure" +// but you can customize it using DecoderConfig. +// +// Renaming Fields +// +// To rename the key that mapstructure looks for, use the "mapstructure" +// tag and set a value directly. For example, to change the "username" example +// above to "user": +// +// type User struct { +// Username string `mapstructure:"user"` +// } +// +// Embedded Structs and Squashing +// +// Embedded structs are treated as if they're another field with that name. +// By default, the two structs below are equivalent when decoding with +// mapstructure: +// +// type Person struct { +// Name string +// } +// +// type Friend struct { +// Person +// } +// +// type Friend struct { +// Person Person +// } +// +// This would require an input that looks like below: +// +// map[string]interface{}{ +// "person": map[string]interface{}{"name": "alice"}, +// } +// +// If your "person" value is NOT nested, then you can append ",squash" to +// your tag value and mapstructure will treat it as if the embedded struct +// were part of the struct directly. Example: +// +// type Friend struct { +// Person `mapstructure:",squash"` +// } +// +// Now the following input would be accepted: +// +// map[string]interface{}{ +// "name": "alice", +// } +// +// DecoderConfig has a field that changes the behavior of mapstructure +// to always squash embedded structs. +// +// Remainder Values +// +// If there are any unmapped keys in the source value, mapstructure by +// default will silently ignore them. You can error by setting ErrorUnused +// in DecoderConfig. If you're using Metadata you can also maintain a slice +// of the unused keys. +// +// You can also use the ",remain" suffix on your tag to collect all unused +// values in a map. The field with this tag MUST be a map type and should +// probably be a "map[string]interface{}" or "map[interface{}]interface{}". +// See example below: +// +// type Friend struct { +// Name string +// Other map[string]interface{} `mapstructure:",remain"` +// } +// +// Given the input below, Other would be populated with the other +// values that weren't used (everything but "name"): +// +// map[string]interface{}{ +// "name": "bob", +// "address": "123 Maple St.", +// } +// +// Omit Empty Values +// +// When decoding from a struct to any other value, you may use the +// ",omitempty" suffix on your tag to omit that value if it equates to +// the zero value. The zero value of all types is specified in the Go +// specification. +// +// For example, the zero type of a numeric type is zero ("0"). If the struct +// field value is zero and a numeric type, the field is empty, and it won't +// be encoded into the destination type. +// +// type Source { +// Age int `mapstructure:",omitempty"` +// } +// +// Unexported fields +// +// Since unexported (private) struct fields cannot be set outside the package +// where they are defined, the decoder will simply skip them. +// +// For this output type definition: +// +// type Exported struct { +// private string // this unexported field will be skipped +// Public string +// } +// +// Using this map as input: +// +// map[string]interface{}{ +// "private": "I will be ignored", +// "Public": "I made it through!", +// } +// +// The following struct will be decoded: +// +// type Exported struct { +// private: "" // field is left with an empty string (zero value) +// Public: "I made it through!" +// } +// +// Other Configuration +// +// mapstructure is highly configurable. See the DecoderConfig struct +// for other features and options that are supported. package mapstructure import ( @@ -80,6 +220,14 @@ type DecoderConfig struct { // WeaklyTypedInput bool + // Squash will squash embedded structs. A squash tag may also be + // added to an individual struct field using a tag. For example: + // + // type Parent struct { + // Child `mapstructure:",squash"` + // } + Squash bool + // Metadata is the struct that will contain extra metadata about // the decoding. If this is nil, then no metadata will be tracked. Metadata *Metadata @@ -271,6 +419,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e var err error outputKind := getKind(outVal) + addMetaKey := true switch outputKind { case reflect.Bool: err = d.decodeBool(name, input, outVal) @@ -289,7 +438,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e case reflect.Map: err = d.decodeMap(name, input, outVal) case reflect.Ptr: - err = d.decodePtr(name, input, outVal) + addMetaKey, err = d.decodePtr(name, input, outVal) case reflect.Slice: err = d.decodeSlice(name, input, outVal) case reflect.Array: @@ -303,7 +452,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e // If we reached here, then we successfully decoded SOMETHING, so // mark the key as used if we're tracking metainput. - if d.config.Metadata != nil && name != "" { + if addMetaKey && d.config.Metadata != nil && name != "" { d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) } @@ -438,6 +587,7 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) + dataType := dataVal.Type() switch { case dataKind == reflect.Int: @@ -469,6 +619,18 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e } else { return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Int64() + if err != nil { + return fmt.Errorf( + "error decoding json.Number into %s: %s", name, err) + } + if i < 0 && !d.config.WeaklyTypedInput { + return fmt.Errorf("cannot parse '%s', %d overflows uint", + name, i) + } + val.SetUint(uint64(i)) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s'", @@ -680,6 +842,18 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re tagValue := f.Tag.Get(d.config.TagName) tagParts := strings.Split(tagValue, ",") + // If "omitempty" is specified in the tag, it ignores empty values. + omitempty := false + for _, tag := range tagParts[1:] { + if tag == "omitempty" { + omitempty = true + break + } + } + if omitempty && isEmptyValue(v) { + continue + } + // Determine the name of the key in the map keyName := f.Name if tagParts[0] != "" { @@ -689,16 +863,19 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re keyName = tagParts[0] } + // If Squash is set in the config, we squash the field down. + squash := d.config.Squash && v.Kind() == reflect.Struct // If "squash" is specified in the tag, we squash the field down. - squash := false - for _, tag := range tagParts[1:] { - if tag == "squash" { - squash = true - break + if !squash { + for _, tag := range tagParts[1:] { + if tag == "squash" { + squash = true + break + } + } + if squash && v.Kind() != reflect.Struct { + return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) } - } - if squash && v.Kind() != reflect.Struct { - return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) } switch v.Kind() { @@ -738,7 +915,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re return nil } -func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (bool, error) { // If the input data is nil, then we want to just set the output // pointer to be nil as well. isNil := data == nil @@ -759,7 +936,7 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er val.Set(nilValue) } - return nil + return true, nil } // Create an element of the concrete (non pointer) type and decode @@ -773,16 +950,16 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er } if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { - return err + return false, err } val.Set(realVal) } else { if err := d.decode(name, data, reflect.Indirect(val)); err != nil { - return err + return false, err } } - return nil + return false, nil } func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { @@ -805,8 +982,8 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) valElemType := valType.Elem() sliceType := reflect.SliceOf(valElemType) - valSlice := val - if valSlice.IsNil() || d.config.ZeroFields { + // If we have a non array/slice type then we first attempt to convert. + if dataValKind != reflect.Array && dataValKind != reflect.Slice { if d.config.WeaklyTypedInput { switch { // Slice and array we use the normal logic @@ -833,18 +1010,17 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) } } - // Check input type - if dataValKind != reflect.Array && dataValKind != reflect.Slice { - return fmt.Errorf( - "'%s': source data must be an array or slice, got %s", name, dataValKind) + return fmt.Errorf( + "'%s': source data must be an array or slice, got %s", name, dataValKind) + } - } - - // If the input value is empty, then don't allocate since non-nil != nil - if dataVal.Len() == 0 { - return nil - } + // If the input value is nil, then don't allocate since empty != nil + if dataVal.IsNil() { + return nil + } + valSlice := val + if valSlice.IsNil() || d.config.ZeroFields { // Make a new slice to hold our result, same size as the original data. valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) } @@ -1005,6 +1181,11 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e field reflect.StructField val reflect.Value } + + // remainField is set to a valid field set with the "remain" tag if + // we are keeping track of remaining values. + var remainField *field + fields := []field{} for len(structs) > 0 { structVal := structs[0] @@ -1017,13 +1198,21 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e fieldKind := fieldType.Type.Kind() // If "squash" is specified in the tag, we squash the field down. - squash := false + squash := d.config.Squash && fieldKind == reflect.Struct + remain := false + + // We always parse the tags cause we're looking for other tags too tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") for _, tag := range tagParts[1:] { if tag == "squash" { squash = true break } + + if tag == "remain" { + remain = true + break + } } if squash { @@ -1036,8 +1225,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e continue } - // Normal struct field, store it away - fields = append(fields, field{fieldType, structVal.Field(i)}) + // Build our field + fieldCurrent := field{fieldType, structVal.Field(i)} + if remain { + remainField = &fieldCurrent + } else { + // Normal struct field, store it away + fields = append(fields, field{fieldType, structVal.Field(i)}) + } } } @@ -1078,9 +1273,6 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } } - // Delete the key we're using from the unused map so we stop tracking - delete(dataValKeysUnused, rawMapKey.Interface()) - if !fieldValue.IsValid() { // This should never happen panic("field is not valid") @@ -1092,6 +1284,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e continue } + // Delete the key we're using from the unused map so we stop tracking + delete(dataValKeysUnused, rawMapKey.Interface()) + // If the name is empty string, then we're at the root, and we // don't dot-join the fields. if name != "" { @@ -1103,6 +1298,25 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } } + // If we have a "remain"-tagged field and we have unused keys then + // we put the unused keys directly into the remain field. + if remainField != nil && len(dataValKeysUnused) > 0 { + // Build a map of only the unused values + remain := map[interface{}]interface{}{} + for key := range dataValKeysUnused { + remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface() + } + + // Decode it as-if we were just decoding this map onto our map. + if err := d.decodeMap(name, remain, remainField.val); err != nil { + errors = appendErrors(errors, err) + } + + // Set the map to nil so we have none so that the next check will + // not error (ErrorUnused) + dataValKeysUnused = nil + } + if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { keys := make([]string, 0, len(dataValKeysUnused)) for rawKey := range dataValKeysUnused { @@ -1133,6 +1347,24 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e return nil } +func isEmptyValue(v reflect.Value) bool { + switch getKind(v) { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + func getKind(val reflect.Value) reflect.Kind { kind := val.Kind() diff --git a/vendor/github.com/pelletier/go-toml/.travis.yml b/vendor/github.com/pelletier/go-toml/.travis.yml deleted file mode 100644 index abb03e99..00000000 --- a/vendor/github.com/pelletier/go-toml/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -sudo: false -language: go -go: - - 1.11.x - - 1.12.x - - tip -matrix: - allow_failures: - - go: tip - fast_finish: true -env: - - GO111MODULE=on -script: - - if [ -n "$(go fmt ./...)" ]; then exit 1; fi - - go test github.com/pelletier/go-toml -race -coverprofile=coverage.txt -covermode=atomic - - go test github.com/pelletier/go-toml/cmd/tomljson - - go test github.com/pelletier/go-toml/cmd/tomll - - go test github.com/pelletier/go-toml/query - - ./benchmark.sh $TRAVIS_BRANCH https://github.com/$TRAVIS_REPO_SLUG.git - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/pelletier/go-toml/Dockerfile b/vendor/github.com/pelletier/go-toml/Dockerfile index 8f439d47..fffdb016 100644 --- a/vendor/github.com/pelletier/go-toml/Dockerfile +++ b/vendor/github.com/pelletier/go-toml/Dockerfile @@ -8,3 +8,4 @@ RUN go install ./... FROM scratch COPY --from=builder /go/bin/tomll /usr/bin/tomll COPY --from=builder /go/bin/tomljson /usr/bin/tomljson +COPY --from=builder /go/bin/jsontoml /usr/bin/jsontoml diff --git a/vendor/github.com/pelletier/go-toml/Makefile b/vendor/github.com/pelletier/go-toml/Makefile new file mode 100644 index 00000000..9e4503ae --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/Makefile @@ -0,0 +1,29 @@ +export CGO_ENABLED=0 +go := go +go.goos ?= $(shell echo `go version`|cut -f4 -d ' '|cut -d '/' -f1) +go.goarch ?= $(shell echo `go version`|cut -f4 -d ' '|cut -d '/' -f2) + +out.tools := tomll tomljson jsontoml +out.dist := $(out.tools:=_$(go.goos)_$(go.goarch).tar.xz) +sources := $(wildcard **/*.go) + + +.PHONY: +tools: $(out.tools) + +$(out.tools): $(sources) + GOOS=$(go.goos) GOARCH=$(go.goarch) $(go) build ./cmd/$@ + +.PHONY: +dist: $(out.dist) + +$(out.dist):%_$(go.goos)_$(go.goarch).tar.xz: % + if [ "$(go.goos)" = "windows" ]; then \ + tar -cJf $@ $^.exe; \ + else \ + tar -cJf $@ $^; \ + fi + +.PHONY: +clean: + rm -rf $(out.tools) $(out.dist) diff --git a/vendor/github.com/pelletier/go-toml/README.md b/vendor/github.com/pelletier/go-toml/README.md index f0311b99..4ef303af 100644 --- a/vendor/github.com/pelletier/go-toml/README.md +++ b/vendor/github.com/pelletier/go-toml/README.md @@ -3,12 +3,11 @@ Go library for the [TOML](https://github.com/mojombo/toml) format. This library supports TOML version -[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md) +[v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) [![GoDoc](https://godoc.org/github.com/pelletier/go-toml?status.svg)](http://godoc.org/github.com/pelletier/go-toml) [![license](https://img.shields.io/github/license/pelletier/go-toml.svg)](https://github.com/pelletier/go-toml/blob/master/LICENSE) -[![Build Status](https://travis-ci.org/pelletier/go-toml.svg?branch=master)](https://travis-ci.org/pelletier/go-toml) -[![Windows Build status](https://ci.appveyor.com/api/projects/status/4aepwwjori266hkt/branch/master?svg=true)](https://ci.appveyor.com/project/pelletier/go-toml/branch/master) +[![Build Status](https://dev.azure.com/pelletierthomas/go-toml-ci/_apis/build/status/pelletier.go-toml?branchName=master)](https://dev.azure.com/pelletierthomas/go-toml-ci/_build/latest?definitionId=1&branchName=master) [![codecov](https://codecov.io/gh/pelletier/go-toml/branch/master/graph/badge.svg)](https://codecov.io/gh/pelletier/go-toml) [![Go Report Card](https://goreportcard.com/badge/github.com/pelletier/go-toml)](https://goreportcard.com/report/github.com/pelletier/go-toml) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml?ref=badge_shield) @@ -100,6 +99,13 @@ Go-toml provides two handy command line tools: go install github.com/pelletier/go-toml/cmd/tomljson tomljson --help ``` + + * `jsontoml`: Reads a JSON file and outputs a TOML representation. + + ``` + go install github.com/pelletier/go-toml/cmd/jsontoml + jsontoml --help + ``` ### Docker image diff --git a/vendor/github.com/pelletier/go-toml/appveyor.yml b/vendor/github.com/pelletier/go-toml/appveyor.yml deleted file mode 100644 index 40e8a415..00000000 --- a/vendor/github.com/pelletier/go-toml/appveyor.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: "{build}" - -# Source Config -clone_folder: c:\gopath\src\github.com\pelletier\go-toml - -# Build host -environment: - GOPATH: c:\gopath - DEPTESTBYPASS501: 1 - GOVERSION: 1.12 - GO111MODULE: on - -init: - - git config --global core.autocrlf input - -# Build -install: - # Install the specific Go version. - - rmdir c:\go /s /q - - appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi - - msiexec /i go%GOVERSION%.windows-amd64.msi /q - - choco install bzr - - set Path=c:\go\bin;c:\gopath\bin;C:\Program Files (x86)\Bazaar\;C:\Program Files\Mercurial\%Path% - - go version - - go env - -build: false -deploy: false - -test_script: - - go test github.com/pelletier/go-toml - - go test github.com/pelletier/go-toml/cmd/tomljson - - go test github.com/pelletier/go-toml/cmd/tomll - - go test github.com/pelletier/go-toml/query diff --git a/vendor/github.com/pelletier/go-toml/azure-pipelines.yml b/vendor/github.com/pelletier/go-toml/azure-pipelines.yml new file mode 100644 index 00000000..242b5b54 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/azure-pipelines.yml @@ -0,0 +1,230 @@ +trigger: +- master + +stages: +- stage: fuzzit + displayName: "Run Fuzzit" + dependsOn: [] + condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master')) + jobs: + - job: submit + displayName: "Submit" + pool: + vmImage: ubuntu-latest + steps: + - task: GoTool@0 + displayName: "Install Go 1.14" + inputs: + version: "1.14" + - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" + - script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml + - script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml + - task: Bash@3 + inputs: + filePath: './fuzzit.sh' + env: + TYPE: fuzzing + FUZZIT_API_KEY: $(FUZZIT_API_KEY) + +- stage: run_checks + displayName: "Check" + dependsOn: [] + jobs: + - job: fmt + displayName: "fmt" + pool: + vmImage: ubuntu-latest + steps: + - task: GoTool@0 + displayName: "Install Go 1.14" + inputs: + version: "1.14" + - task: Go@0 + displayName: "go fmt ./..." + inputs: + command: 'custom' + customCommand: 'fmt' + arguments: './...' + - job: coverage + displayName: "coverage" + pool: + vmImage: ubuntu-latest + steps: + - task: GoTool@0 + displayName: "Install Go 1.14" + inputs: + version: "1.14" + - task: Go@0 + displayName: "Generate coverage" + inputs: + command: 'test' + arguments: "-race -coverprofile=coverage.txt -covermode=atomic" + - task: Bash@3 + inputs: + targetType: 'inline' + script: 'bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}' + env: + CODECOV_TOKEN: $(CODECOV_TOKEN) + - job: benchmark + displayName: "benchmark" + pool: + vmImage: ubuntu-latest + steps: + - task: GoTool@0 + displayName: "Install Go 1.14" + inputs: + version: "1.14" + - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" + - task: Bash@3 + inputs: + filePath: './benchmark.sh' + arguments: "master $(Build.Repository.Uri)" + + - job: fuzzing + displayName: "fuzzing" + pool: + vmImage: ubuntu-latest + steps: + - task: GoTool@0 + displayName: "Install Go 1.14" + inputs: + version: "1.14" + - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" + - script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml + - script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml + - task: Bash@3 + inputs: + filePath: './fuzzit.sh' + env: + TYPE: local-regression + + - job: go_unit_tests + displayName: "unit tests" + strategy: + matrix: + linux 1.14: + goVersion: '1.14' + imageName: 'ubuntu-latest' + mac 1.14: + goVersion: '1.14' + imageName: 'macOS-latest' + windows 1.14: + goVersion: '1.14' + imageName: 'windows-latest' + linux 1.13: + goVersion: '1.13' + imageName: 'ubuntu-latest' + mac 1.13: + goVersion: '1.13' + imageName: 'macOS-latest' + windows 1.13: + goVersion: '1.13' + imageName: 'windows-latest' + pool: + vmImage: $(imageName) + steps: + - task: GoTool@0 + displayName: "Install Go $(goVersion)" + inputs: + version: $(goVersion) + - task: Go@0 + displayName: "go test ./..." + inputs: + command: 'test' + arguments: './...' +- stage: build_binaries + displayName: "Build binaries" + dependsOn: run_checks + jobs: + - job: build_binary + displayName: "Build binary" + strategy: + matrix: + linux_amd64: + GOOS: linux + GOARCH: amd64 + darwin_amd64: + GOOS: darwin + GOARCH: amd64 + windows_amd64: + GOOS: windows + GOARCH: amd64 + pool: + vmImage: ubuntu-latest + steps: + - task: GoTool@0 + displayName: "Install Go" + inputs: + version: 1.14 + - task: Bash@3 + inputs: + targetType: inline + script: "make dist" + env: + go.goos: $(GOOS) + go.goarch: $(GOARCH) + - task: CopyFiles@2 + inputs: + sourceFolder: '$(Build.SourcesDirectory)' + contents: '*.tar.xz' + TargetFolder: '$(Build.ArtifactStagingDirectory)' + - task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: '$(Build.ArtifactStagingDirectory)' + artifactName: binaries +- stage: build_binaries_manifest + displayName: "Build binaries manifest" + dependsOn: build_binaries + jobs: + - job: build_manifest + displayName: "Build binaries manifest" + steps: + - task: DownloadBuildArtifacts@0 + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'binaries' + downloadPath: '$(Build.SourcesDirectory)' + - task: Bash@3 + inputs: + targetType: inline + script: "cd binaries && sha256sum --binary *.tar.xz | tee $(Build.ArtifactStagingDirectory)/sha256sums.txt" + - task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: '$(Build.ArtifactStagingDirectory)' + artifactName: manifest + +- stage: build_docker_image + displayName: "Build Docker image" + dependsOn: run_checks + jobs: + - job: build + displayName: "Build" + pool: + vmImage: ubuntu-latest + steps: + - task: Docker@2 + inputs: + command: 'build' + Dockerfile: 'Dockerfile' + buildContext: '.' + addPipelineData: false + +- stage: publish_docker_image + displayName: "Publish Docker image" + dependsOn: build_docker_image + condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master')) + jobs: + - job: publish + displayName: "Publish" + pool: + vmImage: ubuntu-latest + steps: + - task: Docker@2 + inputs: + containerRegistry: 'DockerHub' + repository: 'pelletier/go-toml' + command: 'buildAndPush' + Dockerfile: 'Dockerfile' + buildContext: '.' + tags: 'latest' diff --git a/vendor/github.com/pelletier/go-toml/benchmark.sh b/vendor/github.com/pelletier/go-toml/benchmark.sh index 8b8bb528..7914fff4 100644 --- a/vendor/github.com/pelletier/go-toml/benchmark.sh +++ b/vendor/github.com/pelletier/go-toml/benchmark.sh @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -ex reference_ref=${1:-master} reference_git=${2:-.} @@ -8,7 +8,6 @@ reference_git=${2:-.} if ! `hash benchstat 2>/dev/null`; then echo "Installing benchstat" go get golang.org/x/perf/cmd/benchstat - go install golang.org/x/perf/cmd/benchstat fi tempdir=`mktemp -d /tmp/go-toml-benchmark-XXXXXX` @@ -29,4 +28,4 @@ go test -bench=. -benchmem | tee ${local_benchmark} echo "" echo "=== diff" -benchstat -delta-test=none ${ref_benchmark} ${local_benchmark} \ No newline at end of file +benchstat -delta-test=none ${ref_benchmark} ${local_benchmark} diff --git a/vendor/github.com/pelletier/go-toml/doc.go b/vendor/github.com/pelletier/go-toml/doc.go index d5fd98c0..a1406a32 100644 --- a/vendor/github.com/pelletier/go-toml/doc.go +++ b/vendor/github.com/pelletier/go-toml/doc.go @@ -1,7 +1,7 @@ // Package toml is a TOML parser and manipulation library. // // This version supports the specification as described in -// https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md +// https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md // // Marshaling // diff --git a/vendor/github.com/pelletier/go-toml/fuzzit.sh b/vendor/github.com/pelletier/go-toml/fuzzit.sh new file mode 100644 index 00000000..b575a608 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/fuzzit.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -xe + +# go-fuzz doesn't support modules yet, so ensure we do everything +# in the old style GOPATH way +export GO111MODULE="off" + +# install go-fuzz +go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build + +# target name can only contain lower-case letters (a-z), digits (0-9) and a dash (-) +# to add another target, make sure to create it with `fuzzit create target` +# before using `fuzzit create job` +TARGET=toml-fuzzer + +go-fuzz-build -libfuzzer -o ${TARGET}.a github.com/pelletier/go-toml +clang -fsanitize=fuzzer ${TARGET}.a -o ${TARGET} + +# install fuzzit for talking to fuzzit.dev service +# or latest version: +# https://github.com/fuzzitdev/fuzzit/releases/latest/download/fuzzit_Linux_x86_64 +wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.52/fuzzit_Linux_x86_64 +chmod a+x fuzzit + +# TODO: change kkowalczyk to go-toml and create toml-fuzzer target there +./fuzzit create job --type $TYPE go-toml/${TARGET} ${TARGET} diff --git a/vendor/github.com/pelletier/go-toml/go.mod b/vendor/github.com/pelletier/go-toml/go.mod index f4690e19..07a258be 100644 --- a/vendor/github.com/pelletier/go-toml/go.mod +++ b/vendor/github.com/pelletier/go-toml/go.mod @@ -5,5 +5,5 @@ go 1.12 require ( github.com/BurntSushi/toml v0.3.1 github.com/davecgh/go-spew v1.1.1 - gopkg.in/yaml.v2 v2.2.2 + gopkg.in/yaml.v2 v2.2.8 ) diff --git a/vendor/github.com/pelletier/go-toml/go.sum b/vendor/github.com/pelletier/go-toml/go.sum index 8d91a478..b30bc766 100644 --- a/vendor/github.com/pelletier/go-toml/go.sum +++ b/vendor/github.com/pelletier/go-toml/go.sum @@ -5,3 +5,13 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/pelletier/go-toml/lexer.go b/vendor/github.com/pelletier/go-toml/lexer.go index 6254d390..88fd91ee 100644 --- a/vendor/github.com/pelletier/go-toml/lexer.go +++ b/vendor/github.com/pelletier/go-toml/lexer.go @@ -223,9 +223,12 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn { } possibleDate := l.peekString(35) - dateMatch := dateRegexp.FindString(possibleDate) - if dateMatch != "" { - l.fastForward(len(dateMatch)) + dateSubmatches := dateRegexp.FindStringSubmatch(possibleDate) + if dateSubmatches != nil && dateSubmatches[0] != "" { + l.fastForward(len(dateSubmatches[0])) + if dateSubmatches[2] == "" { // no timezone information => local date + return l.lexLocalDate + } return l.lexDate } @@ -247,7 +250,7 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn { func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn { l.next() l.emit(tokenLeftCurlyBrace) - return l.lexRvalue + return l.lexVoid } func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn { @@ -261,6 +264,11 @@ func (l *tomlLexer) lexDate() tomlLexStateFn { return l.lexRvalue } +func (l *tomlLexer) lexLocalDate() tomlLexStateFn { + l.emit(tokenLocalDate) + return l.lexRvalue +} + func (l *tomlLexer) lexTrue() tomlLexStateFn { l.fastForward(4) l.emit(tokenTrue) @@ -733,7 +741,27 @@ func (l *tomlLexer) run() { } func init() { - dateRegexp = regexp.MustCompile(`^\d{1,4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})`) + // Regexp for all date/time formats supported by TOML. + // Group 1: nano precision + // Group 2: timezone + // + // /!\ also matches the empty string + // + // Example matches: + //1979-05-27T07:32:00Z + //1979-05-27T00:32:00-07:00 + //1979-05-27T00:32:00.999999-07:00 + //1979-05-27 07:32:00Z + //1979-05-27 00:32:00-07:00 + //1979-05-27 00:32:00.999999-07:00 + //1979-05-27T07:32:00 + //1979-05-27T00:32:00.999999 + //1979-05-27 07:32:00 + //1979-05-27 00:32:00.999999 + //1979-05-27 + //07:32:00 + //00:32:00.999999 + dateRegexp = regexp.MustCompile(`^(?:\d{1,4}-\d{2}-\d{2})?(?:[T ]?\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})?)?`) } // Entry point diff --git a/vendor/github.com/pelletier/go-toml/localtime.go b/vendor/github.com/pelletier/go-toml/localtime.go new file mode 100644 index 00000000..a2149e96 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/localtime.go @@ -0,0 +1,281 @@ +// Implementation of TOML's local date/time. +// Copied over from https://github.com/googleapis/google-cloud-go/blob/master/civil/civil.go +// to avoid pulling all the Google dependencies. +// +// Copyright 2016 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package civil implements types for civil time, a time-zone-independent +// representation of time that follows the rules of the proleptic +// Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second +// minutes. +// +// Because they lack location information, these types do not represent unique +// moments or intervals of time. Use time.Time for that purpose. +package toml + +import ( + "fmt" + "time" +) + +// A LocalDate represents a date (year, month, day). +// +// This type does not include location information, and therefore does not +// describe a unique 24-hour timespan. +type LocalDate struct { + Year int // Year (e.g., 2014). + Month time.Month // Month of the year (January = 1, ...). + Day int // Day of the month, starting at 1. +} + +// LocalDateOf returns the LocalDate in which a time occurs in that time's location. +func LocalDateOf(t time.Time) LocalDate { + var d LocalDate + d.Year, d.Month, d.Day = t.Date() + return d +} + +// ParseLocalDate parses a string in RFC3339 full-date format and returns the date value it represents. +func ParseLocalDate(s string) (LocalDate, error) { + t, err := time.Parse("2006-01-02", s) + if err != nil { + return LocalDate{}, err + } + return LocalDateOf(t), nil +} + +// String returns the date in RFC3339 full-date format. +func (d LocalDate) String() string { + return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day) +} + +// IsValid reports whether the date is valid. +func (d LocalDate) IsValid() bool { + return LocalDateOf(d.In(time.UTC)) == d +} + +// In returns the time corresponding to time 00:00:00 of the date in the location. +// +// In is always consistent with time.LocalDate, even when time.LocalDate returns a time +// on a different day. For example, if loc is America/Indiana/Vincennes, then both +// time.LocalDate(1955, time.May, 1, 0, 0, 0, 0, loc) +// and +// civil.LocalDate{Year: 1955, Month: time.May, Day: 1}.In(loc) +// return 23:00:00 on April 30, 1955. +// +// In panics if loc is nil. +func (d LocalDate) In(loc *time.Location) time.Time { + return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, loc) +} + +// AddDays returns the date that is n days in the future. +// n can also be negative to go into the past. +func (d LocalDate) AddDays(n int) LocalDate { + return LocalDateOf(d.In(time.UTC).AddDate(0, 0, n)) +} + +// DaysSince returns the signed number of days between the date and s, not including the end day. +// This is the inverse operation to AddDays. +func (d LocalDate) DaysSince(s LocalDate) (days int) { + // We convert to Unix time so we do not have to worry about leap seconds: + // Unix time increases by exactly 86400 seconds per day. + deltaUnix := d.In(time.UTC).Unix() - s.In(time.UTC).Unix() + return int(deltaUnix / 86400) +} + +// Before reports whether d1 occurs before d2. +func (d1 LocalDate) Before(d2 LocalDate) bool { + if d1.Year != d2.Year { + return d1.Year < d2.Year + } + if d1.Month != d2.Month { + return d1.Month < d2.Month + } + return d1.Day < d2.Day +} + +// After reports whether d1 occurs after d2. +func (d1 LocalDate) After(d2 LocalDate) bool { + return d2.Before(d1) +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The output is the result of d.String(). +func (d LocalDate) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The date is expected to be a string in a format accepted by ParseLocalDate. +func (d *LocalDate) UnmarshalText(data []byte) error { + var err error + *d, err = ParseLocalDate(string(data)) + return err +} + +// A LocalTime represents a time with nanosecond precision. +// +// This type does not include location information, and therefore does not +// describe a unique moment in time. +// +// This type exists to represent the TIME type in storage-based APIs like BigQuery. +// Most operations on Times are unlikely to be meaningful. Prefer the LocalDateTime type. +type LocalTime struct { + Hour int // The hour of the day in 24-hour format; range [0-23] + Minute int // The minute of the hour; range [0-59] + Second int // The second of the minute; range [0-59] + Nanosecond int // The nanosecond of the second; range [0-999999999] +} + +// LocalTimeOf returns the LocalTime representing the time of day in which a time occurs +// in that time's location. It ignores the date. +func LocalTimeOf(t time.Time) LocalTime { + var tm LocalTime + tm.Hour, tm.Minute, tm.Second = t.Clock() + tm.Nanosecond = t.Nanosecond() + return tm +} + +// ParseLocalTime parses a string and returns the time value it represents. +// ParseLocalTime accepts an extended form of the RFC3339 partial-time format. After +// the HH:MM:SS part of the string, an optional fractional part may appear, +// consisting of a decimal point followed by one to nine decimal digits. +// (RFC3339 admits only one digit after the decimal point). +func ParseLocalTime(s string) (LocalTime, error) { + t, err := time.Parse("15:04:05.999999999", s) + if err != nil { + return LocalTime{}, err + } + return LocalTimeOf(t), nil +} + +// String returns the date in the format described in ParseLocalTime. If Nanoseconds +// is zero, no fractional part will be generated. Otherwise, the result will +// end with a fractional part consisting of a decimal point and nine digits. +func (t LocalTime) String() string { + s := fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second) + if t.Nanosecond == 0 { + return s + } + return s + fmt.Sprintf(".%09d", t.Nanosecond) +} + +// IsValid reports whether the time is valid. +func (t LocalTime) IsValid() bool { + // Construct a non-zero time. + tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC) + return LocalTimeOf(tm) == t +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The output is the result of t.String(). +func (t LocalTime) MarshalText() ([]byte, error) { + return []byte(t.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The time is expected to be a string in a format accepted by ParseLocalTime. +func (t *LocalTime) UnmarshalText(data []byte) error { + var err error + *t, err = ParseLocalTime(string(data)) + return err +} + +// A LocalDateTime represents a date and time. +// +// This type does not include location information, and therefore does not +// describe a unique moment in time. +type LocalDateTime struct { + Date LocalDate + Time LocalTime +} + +// Note: We deliberately do not embed LocalDate into LocalDateTime, to avoid promoting AddDays and Sub. + +// LocalDateTimeOf returns the LocalDateTime in which a time occurs in that time's location. +func LocalDateTimeOf(t time.Time) LocalDateTime { + return LocalDateTime{ + Date: LocalDateOf(t), + Time: LocalTimeOf(t), + } +} + +// ParseLocalDateTime parses a string and returns the LocalDateTime it represents. +// ParseLocalDateTime accepts a variant of the RFC3339 date-time format that omits +// the time offset but includes an optional fractional time, as described in +// ParseLocalTime. Informally, the accepted format is +// YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF] +// where the 'T' may be a lower-case 't'. +func ParseLocalDateTime(s string) (LocalDateTime, error) { + t, err := time.Parse("2006-01-02T15:04:05.999999999", s) + if err != nil { + t, err = time.Parse("2006-01-02t15:04:05.999999999", s) + if err != nil { + return LocalDateTime{}, err + } + } + return LocalDateTimeOf(t), nil +} + +// String returns the date in the format described in ParseLocalDate. +func (dt LocalDateTime) String() string { + return dt.Date.String() + "T" + dt.Time.String() +} + +// IsValid reports whether the datetime is valid. +func (dt LocalDateTime) IsValid() bool { + return dt.Date.IsValid() && dt.Time.IsValid() +} + +// In returns the time corresponding to the LocalDateTime in the given location. +// +// If the time is missing or ambigous at the location, In returns the same +// result as time.LocalDate. For example, if loc is America/Indiana/Vincennes, then +// both +// time.LocalDate(1955, time.May, 1, 0, 30, 0, 0, loc) +// and +// civil.LocalDateTime{ +// civil.LocalDate{Year: 1955, Month: time.May, Day: 1}}, +// civil.LocalTime{Minute: 30}}.In(loc) +// return 23:30:00 on April 30, 1955. +// +// In panics if loc is nil. +func (dt LocalDateTime) In(loc *time.Location) time.Time { + return time.Date(dt.Date.Year, dt.Date.Month, dt.Date.Day, dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc) +} + +// Before reports whether dt1 occurs before dt2. +func (dt1 LocalDateTime) Before(dt2 LocalDateTime) bool { + return dt1.In(time.UTC).Before(dt2.In(time.UTC)) +} + +// After reports whether dt1 occurs after dt2. +func (dt1 LocalDateTime) After(dt2 LocalDateTime) bool { + return dt2.Before(dt1) +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The output is the result of dt.String(). +func (dt LocalDateTime) MarshalText() ([]byte, error) { + return []byte(dt.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The datetime is expected to be a string in a format accepted by ParseLocalDateTime +func (dt *LocalDateTime) UnmarshalText(data []byte) error { + var err error + *dt, err = ParseLocalDateTime(string(data)) + return err +} diff --git a/vendor/github.com/pelletier/go-toml/marshal.go b/vendor/github.com/pelletier/go-toml/marshal.go index 0e1c57e8..dcddad8d 100644 --- a/vendor/github.com/pelletier/go-toml/marshal.go +++ b/vendor/github.com/pelletier/go-toml/marshal.go @@ -68,6 +68,9 @@ const ( var timeType = reflect.TypeOf(time.Time{}) var marshalerType = reflect.TypeOf(new(Marshaler)).Elem() +var localDateType = reflect.TypeOf(LocalDate{}) +var localTimeType = reflect.TypeOf(LocalTime{}) +var localDateTimeType = reflect.TypeOf(LocalDateTime{}) // Check if the given marshal type maps to a Tree primitive func isPrimitive(mtype reflect.Type) bool { @@ -85,29 +88,31 @@ func isPrimitive(mtype reflect.Type) bool { case reflect.String: return true case reflect.Struct: - return mtype == timeType || isCustomMarshaler(mtype) + return mtype == timeType || mtype == localDateType || mtype == localDateTimeType || mtype == localTimeType || isCustomMarshaler(mtype) default: return false } } -// Check if the given marshal type maps to a Tree slice -func isTreeSlice(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Slice: - return !isOtherSlice(mtype) - default: - return false - } -} - -// Check if the given marshal type maps to a non-Tree slice -func isOtherSlice(mtype reflect.Type) bool { +// Check if the given marshal type maps to a Tree slice or array +func isTreeSequence(mtype reflect.Type) bool { switch mtype.Kind() { case reflect.Ptr: - return isOtherSlice(mtype.Elem()) - case reflect.Slice: - return isPrimitive(mtype.Elem()) || isOtherSlice(mtype.Elem()) + return isTreeSequence(mtype.Elem()) + case reflect.Slice, reflect.Array: + return isTree(mtype.Elem()) + default: + return false + } +} + +// Check if the given marshal type maps to a non-Tree slice or array +func isOtherSequence(mtype reflect.Type) bool { + switch mtype.Kind() { + case reflect.Ptr: + return isOtherSequence(mtype.Elem()) + case reflect.Slice, reflect.Array: + return !isTreeSequence(mtype) default: return false } @@ -116,6 +121,8 @@ func isOtherSlice(mtype reflect.Type) bool { // Check if the given marshal type maps to a Tree func isTree(mtype reflect.Type) bool { switch mtype.Kind() { + case reflect.Ptr: + return isTree(mtype.Elem()) case reflect.Map: return true case reflect.Struct: @@ -170,7 +177,7 @@ Tree primitive types and corresponding marshal types: float64 float32, float64, pointers to same string string, pointers to same bool bool, pointers to same - time.Time time.Time{}, pointers to same + time.LocalTime time.LocalTime{}, pointers to same For additional flexibility, use the Encoder API. */ @@ -295,7 +302,7 @@ func (e *Encoder) marshal(v interface{}) ([]byte, error) { } var buf bytes.Buffer - _, err = t.writeToOrdered(&buf, "", "", 0, e.arraysOneElementPerLine, e.order) + _, err = t.writeToOrdered(&buf, "", "", 0, e.arraysOneElementPerLine, e.order, false) return buf.Bytes(), err } @@ -313,20 +320,25 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er tval := e.nextTree() switch mtype.Kind() { case reflect.Struct: - for i := 0; i < mtype.NumField(); i++ { - mtypef, mvalf := mtype.Field(i), mval.Field(i) - opts := tomlOptions(mtypef, e.annotation) - if opts.include && (!opts.omitempty || !isZero(mvalf)) { - val, err := e.valueToToml(mtypef.Type, mvalf) - if err != nil { - return nil, err - } + switch mval.Interface().(type) { + case Tree: + reflect.ValueOf(tval).Elem().Set(mval) + default: + for i := 0; i < mtype.NumField(); i++ { + mtypef, mvalf := mtype.Field(i), mval.Field(i) + opts := tomlOptions(mtypef, e.annotation) + if opts.include && ((mtypef.Type.Kind() != reflect.Interface && !opts.omitempty) || !isZero(mvalf)) { + val, err := e.valueToToml(mtypef.Type, mvalf) + if err != nil { + return nil, err + } - tval.SetWithOptions(opts.name, SetOptions{ - Comment: opts.comment, - Commented: opts.commented, - Multiline: opts.multiline, - }, val) + tval.SetWithOptions(opts.name, SetOptions{ + Comment: opts.comment, + Commented: opts.commented, + Multiline: opts.multiline, + }, val) + } } } case reflect.Map: @@ -351,12 +363,15 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er } for _, key := range keys { mvalf := mval.MapIndex(key) + if (mtype.Elem().Kind() == reflect.Ptr || mtype.Elem().Kind() == reflect.Interface) && mvalf.IsNil() { + continue + } val, err := e.valueToToml(mtype.Elem(), mvalf) if err != nil { return nil, err } if e.quoteMapKeys { - keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine) + keyStr, err := tomlValueStringRepresentation(key.String(), "", "", e.arraysOneElementPerLine) if err != nil { return nil, err } @@ -384,6 +399,9 @@ func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*T // Convert given marshal slice to slice of toml values func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) { + if mtype.Elem().Kind() == reflect.Interface { + return nil, fmt.Errorf("marshal can't handle []interface{}") + } tval := make([]interface{}, mval.Len(), mval.Len()) for i := 0; i < mval.Len(); i++ { val, err := e.valueToToml(mtype.Elem(), mval.Index(i)) @@ -401,14 +419,17 @@ func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface if mtype.Kind() == reflect.Ptr { return e.valueToToml(mtype.Elem(), mval.Elem()) } + if mtype.Kind() == reflect.Interface { + return e.valueToToml(mval.Elem().Type(), mval.Elem()) + } switch { case isCustomMarshaler(mtype): return callCustomMarshaler(mval) case isTree(mtype): return e.valueToTree(mtype, mval) - case isTreeSlice(mtype): + case isTreeSequence(mtype): return e.valueToTreeSlice(mtype, mval) - case isOtherSlice(mtype): + case isOtherSequence(mtype): return e.valueToOtherSlice(mtype, mval) default: switch mtype.Kind() { @@ -426,7 +447,7 @@ func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface case reflect.String: return mval.String(), nil case reflect.Struct: - return mval.Interface().(time.Time), nil + return mval.Interface(), nil default: return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind()) } @@ -445,8 +466,11 @@ func (t *Tree) Unmarshal(v interface{}) error { // See Marshal() documentation for types mapping table. func (t *Tree) Marshal() ([]byte, error) { var buf bytes.Buffer - err := NewEncoder(&buf).Encode(t) - return buf.Bytes(), err + _, err := t.WriteTo(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil } // Unmarshal parses the TOML-encoded data and stores the result in the value @@ -526,7 +550,9 @@ func (d *Decoder) unmarshal(v interface{}) error { return errors.New("only a pointer to struct or map can be unmarshaled from TOML") } - sval, err := d.valueFromTree(elem, d.tval) + vv := reflect.ValueOf(v).Elem() + + sval, err := d.valueFromTree(elem, d.tval, &vv) if err != nil { return err } @@ -534,20 +560,32 @@ func (d *Decoder) unmarshal(v interface{}) error { return nil } -// Convert toml tree to marshal struct or map, using marshal type -func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) { +// Convert toml tree to marshal struct or map, using marshal type. When mval1 +// is non-nil, merge fields into the given value instead of allocating a new one. +func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.Value) (reflect.Value, error) { if mtype.Kind() == reflect.Ptr { - return d.unwrapPointer(mtype, tval) + return d.unwrapPointer(mtype, tval, mval1) } var mval reflect.Value switch mtype.Kind() { case reflect.Struct: - mval = reflect.New(mtype).Elem() - for i := 0; i < mtype.NumField(); i++ { - mtypef := mtype.Field(i) - an := annotation{tag: d.tagName} - opts := tomlOptions(mtypef, an) - if opts.include { + if mval1 != nil { + mval = *mval1 + } else { + mval = reflect.New(mtype).Elem() + } + + switch mval.Interface().(type) { + case Tree: + mval.Set(reflect.ValueOf(tval).Elem()) + default: + for i := 0; i < mtype.NumField(); i++ { + mtypef := mtype.Field(i) + an := annotation{tag: d.tagName} + opts := tomlOptions(mtypef, an) + if !opts.include { + continue + } baseKey := opts.name keysToTry := []string{ baseKey, @@ -557,19 +595,22 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, } found := false - for _, key := range keysToTry { - exists := tval.Has(key) - if !exists { - continue + if tval != nil { + for _, key := range keysToTry { + exists := tval.Has(key) + if !exists { + continue + } + val := tval.Get(key) + fval := mval.Field(i) + mvalf, err := d.valueFromToml(mtypef.Type, val, &fval) + if err != nil { + return mval, formatError(err, tval.GetPosition(key)) + } + mval.Field(i).Set(mvalf) + found = true + break } - val := tval.Get(key) - mvalf, err := d.valueFromToml(mtypef.Type, val) - if err != nil { - return mval, formatError(err, tval.GetPosition(key)) - } - mval.Field(i).Set(mvalf) - found = true - break } if !found && opts.defaultValue != "" { @@ -604,6 +645,19 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, } mval.Field(i).Set(reflect.ValueOf(val)) } + + // save the old behavior above and try to check structs + if !found && opts.defaultValue == "" && mtypef.Type.Kind() == reflect.Struct { + tmpTval := tval + if !mtypef.Anonymous { + tmpTval = nil + } + v, err := d.valueFromTree(mtypef.Type, tmpTval, nil) + if err != nil { + return v, err + } + mval.Field(i).Set(v) + } } } case reflect.Map: @@ -611,7 +665,7 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, for _, key := range tval.Keys() { // TODO: path splits key val := tval.GetPath([]string{key}) - mvalf, err := d.valueFromToml(mtype.Elem(), val) + mvalf, err := d.valueFromToml(mtype.Elem(), val, nil) if err != nil { return mval, formatError(err, tval.GetPosition(key)) } @@ -625,7 +679,7 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) { mval := reflect.MakeSlice(mtype, len(tval), len(tval)) for i := 0; i < len(tval); i++ { - val, err := d.valueFromTree(mtype.Elem(), tval[i]) + val, err := d.valueFromTree(mtype.Elem(), tval[i], nil) if err != nil { return mval, err } @@ -638,7 +692,7 @@ func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect. func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) { mval := reflect.MakeSlice(mtype, len(tval), len(tval)) for i := 0; i < len(tval); i++ { - val, err := d.valueFromToml(mtype.Elem(), tval[i]) + val, err := d.valueFromToml(mtype.Elem(), tval[i], nil) if err != nil { return mval, err } @@ -647,33 +701,88 @@ func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (r return mval, nil } -// Convert toml value to marshal value, using marshal type -func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) { +// Convert toml value to marshal value, using marshal type. When mval1 is non-nil +// and the given type is a struct value, merge fields into it. +func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) { if mtype.Kind() == reflect.Ptr { - return d.unwrapPointer(mtype, tval) + return d.unwrapPointer(mtype, tval, mval1) } switch t := tval.(type) { case *Tree: - if isTree(mtype) { - return d.valueFromTree(mtype, t) + var mval11 *reflect.Value + if mtype.Kind() == reflect.Struct { + mval11 = mval1 } + + if isTree(mtype) { + return d.valueFromTree(mtype, t, mval11) + } + + if mtype.Kind() == reflect.Interface { + if mval1 == nil || mval1.IsNil() { + return d.valueFromTree(reflect.TypeOf(map[string]interface{}{}), t, nil) + } else { + return d.valueFromToml(mval1.Elem().Type(), t, nil) + } + } + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval) case []*Tree: - if isTreeSlice(mtype) { + if isTreeSequence(mtype) { return d.valueFromTreeSlice(mtype, t) } + if mtype.Kind() == reflect.Interface { + if mval1 == nil || mval1.IsNil() { + return d.valueFromTreeSlice(reflect.TypeOf([]map[string]interface{}{}), t) + } else { + ival := mval1.Elem() + return d.valueFromToml(mval1.Elem().Type(), t, &ival) + } + } return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval) case []interface{}: - if isOtherSlice(mtype) { + if isOtherSequence(mtype) { return d.valueFromOtherSlice(mtype, t) } + if mtype.Kind() == reflect.Interface { + if mval1 == nil || mval1.IsNil() { + return d.valueFromOtherSlice(reflect.TypeOf([]interface{}{}), t) + } else { + ival := mval1.Elem() + return d.valueFromToml(mval1.Elem().Type(), t, &ival) + } + } return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval) default: switch mtype.Kind() { case reflect.Bool, reflect.Struct: val := reflect.ValueOf(tval) - // if this passes for when mtype is reflect.Struct, tval is a time.Time + + switch val.Type() { + case localDateType: + localDate := val.Interface().(LocalDate) + switch mtype { + case timeType: + return reflect.ValueOf(time.Date(localDate.Year, localDate.Month, localDate.Day, 0, 0, 0, 0, time.Local)), nil + } + case localDateTimeType: + localDateTime := val.Interface().(LocalDateTime) + switch mtype { + case timeType: + return reflect.ValueOf(time.Date( + localDateTime.Date.Year, + localDateTime.Date.Month, + localDateTime.Date.Day, + localDateTime.Time.Hour, + localDateTime.Time.Minute, + localDateTime.Time.Second, + localDateTime.Time.Nanosecond, + time.Local)), nil + } + } + + // if this passes for when mtype is reflect.Struct, tval is a time.LocalTime if !val.Type().ConvertibleTo(mtype) { return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) } @@ -728,14 +837,28 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.V } return val.Convert(mtype), nil + case reflect.Interface: + if mval1 == nil || mval1.IsNil() { + return reflect.ValueOf(tval), nil + } else { + ival := mval1.Elem() + return d.valueFromToml(mval1.Elem().Type(), t, &ival) + } default: return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind()) } } } -func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) { - val, err := d.valueFromToml(mtype.Elem(), tval) +func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) { + var melem *reflect.Value + + if mval1 != nil && !mval1.IsNil() && (mtype.Elem().Kind() == reflect.Struct || mtype.Elem().Kind() == reflect.Interface) { + elem := mval1.Elem() + melem = &elem + } + + val, err := d.valueFromToml(mtype.Elem(), tval, melem) if err != nil { return reflect.ValueOf(nil), err } diff --git a/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_Map_test.toml b/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_Map_test.toml deleted file mode 100644 index a3bd5130..00000000 --- a/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_Map_test.toml +++ /dev/null @@ -1,17 +0,0 @@ -title = "TOML Marshal Testing" - -[basic_map] - one = "one" - two = "two" - -[long_map] - a7 = "1" - b3 = "2" - c8 = "3" - d4 = "4" - e6 = "5" - f5 = "6" - g10 = "7" - h1 = "8" - i2 = "9" - j9 = "10" diff --git a/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml b/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml index 9d68b599..792b72ed 100644 --- a/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml +++ b/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml @@ -27,6 +27,7 @@ title = "TOML Marshal Testing" uint = 5001 bool = true float = 123.4 + float64 = 123.456782132399 int = 5000 string = "Bite me" date = 1979-05-27T07:32:00Z diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.toml b/vendor/github.com/pelletier/go-toml/marshal_test.toml index 1c5f98e7..ba5e110b 100644 --- a/vendor/github.com/pelletier/go-toml/marshal_test.toml +++ b/vendor/github.com/pelletier/go-toml/marshal_test.toml @@ -4,6 +4,7 @@ title = "TOML Marshal Testing" bool = true date = 1979-05-27T07:32:00Z float = 123.4 + float64 = 123.456782132399 int = 5000 string = "Bite me" uint = 5001 diff --git a/vendor/github.com/pelletier/go-toml/parser.go b/vendor/github.com/pelletier/go-toml/parser.go index a7498e49..1b344fee 100644 --- a/vendor/github.com/pelletier/go-toml/parser.go +++ b/vendor/github.com/pelletier/go-toml/parser.go @@ -313,7 +313,41 @@ func (p *tomlParser) parseRvalue() interface{} { } return val case tokenDate: - val, err := time.ParseInLocation(time.RFC3339Nano, tok.val, time.UTC) + layout := time.RFC3339Nano + if !strings.Contains(tok.val, "T") { + layout = strings.Replace(layout, "T", " ", 1) + } + val, err := time.ParseInLocation(layout, tok.val, time.UTC) + if err != nil { + p.raiseError(tok, "%s", err) + } + return val + case tokenLocalDate: + v := strings.Replace(tok.val, " ", "T", -1) + isDateTime := false + isTime := false + for _, c := range v { + if c == 'T' || c == 't' { + isDateTime = true + break + } + if c == ':' { + isTime = true + break + } + } + + var val interface{} + var err error + + if isDateTime { + val, err = ParseLocalDateTime(v) + } else if isTime { + val, err = ParseLocalTime(v) + } else { + val, err = ParseLocalDate(v) + } + if err != nil { p.raiseError(tok, "%s", err) } @@ -356,12 +390,15 @@ Loop: } key := p.getToken() p.assume(tokenEqual) - value := p.parseRvalue() - tree.Set(key.val, value) - case tokenComma: - if previous == nil { - p.raiseError(follow, "inline table cannot start with a comma") + + parsedKey, err := parseKey(key.val) + if err != nil { + p.raiseError(key, "invalid key: %s", err) } + + value := p.parseRvalue() + tree.SetPath(parsedKey, value) + case tokenComma: if tokenIsComma(previous) { p.raiseError(follow, "need field between two commas in inline table") } diff --git a/vendor/github.com/pelletier/go-toml/token.go b/vendor/github.com/pelletier/go-toml/token.go index 1a908134..36a3fc88 100644 --- a/vendor/github.com/pelletier/go-toml/token.go +++ b/vendor/github.com/pelletier/go-toml/token.go @@ -2,7 +2,6 @@ package toml import ( "fmt" - "strconv" "unicode" ) @@ -35,6 +34,7 @@ const ( tokenDoubleLeftBracket tokenDoubleRightBracket tokenDate + tokenLocalDate tokenKeyGroup tokenKeyGroupArray tokenComma @@ -68,7 +68,8 @@ var tokenTypeNames = []string{ ")", "]]", "[[", - "Date", + "LocalDate", + "LocalDate", "KeyGroup", "KeyGroupArray", ",", @@ -95,14 +96,6 @@ func (tt tokenType) String() string { return "Unknown" } -func (t token) Int() int { - if result, err := strconv.Atoi(t.val); err != nil { - panic(err) - } else { - return result - } -} - func (t token) String() string { switch t.typ { case tokenEOF: diff --git a/vendor/github.com/pelletier/go-toml/toml.go b/vendor/github.com/pelletier/go-toml/toml.go index 358a9be5..f4d56870 100644 --- a/vendor/github.com/pelletier/go-toml/toml.go +++ b/vendor/github.com/pelletier/go-toml/toml.go @@ -222,8 +222,12 @@ func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interfac switch v := value.(type) { case *Tree: v.comment = opts.Comment + v.commented = opts.Commented toInsert = value case []*Tree: + for i := range v { + v[i].commented = opts.Commented + } toInsert = value case *tomlValue: v.comment = opts.Comment diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write.go b/vendor/github.com/pelletier/go-toml/tomltree_write.go index 198d5ac1..16c19867 100644 --- a/vendor/github.com/pelletier/go-toml/tomltree_write.go +++ b/vendor/github.com/pelletier/go-toml/tomltree_write.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "math" + "math/big" "reflect" "sort" "strconv" @@ -27,9 +28,10 @@ type sortNode struct { // Encodes a string to a TOML-compliant multi-line string value // This function is a clone of the existing encodeTomlString function, except that whitespace characters // are preserved. Quotation marks and backslashes are also not escaped. -func encodeMultilineTomlString(value string) string { +func encodeMultilineTomlString(value string, commented string) string { var b bytes.Buffer + b.WriteString(commented) for _, rr := range value { switch rr { case '\b': @@ -37,7 +39,7 @@ func encodeMultilineTomlString(value string) string { case '\t': b.WriteString("\t") case '\n': - b.WriteString("\n") + b.WriteString("\n" + commented) case '\f': b.WriteString(`\f`) case '\r': @@ -90,7 +92,7 @@ func encodeTomlString(value string) string { return b.String() } -func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) { +func tomlValueStringRepresentation(v interface{}, commented string, indent string, arraysOneElementPerLine bool) (string, error) { // this interface check is added to dereference the change made in the writeTo function. // That change was made to allow this function to see formatting options. tv, ok := v.(*tomlValue) @@ -106,20 +108,28 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen case int64: return strconv.FormatInt(value, 10), nil case float64: - // Ensure a round float does contain a decimal point. Otherwise feeding - // the output back to the parser would convert to an integer. - if math.Trunc(value) == value { - return strings.ToLower(strconv.FormatFloat(value, 'f', 1, 32)), nil + // Default bit length is full 64 + bits := 64 + // Float panics if nan is used + if !math.IsNaN(value) { + // if 32 bit accuracy is enough to exactly show, use 32 + _, acc := big.NewFloat(value).Float32() + if acc == big.Exact { + bits = 32 + } } - return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil + if math.Trunc(value) == value { + return strings.ToLower(strconv.FormatFloat(value, 'f', 1, bits)), nil + } + return strings.ToLower(strconv.FormatFloat(value, 'f', -1, bits)), nil case string: if tv.multiline { - return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil + return "\"\"\"\n" + encodeMultilineTomlString(value, commented) + "\"\"\"", nil } return "\"" + encodeTomlString(value) + "\"", nil case []byte: b, _ := v.([]byte) - return tomlValueStringRepresentation(string(b), indent, arraysOneElementPerLine) + return tomlValueStringRepresentation(string(b), commented, indent, arraysOneElementPerLine) case bool: if value { return "true", nil @@ -127,6 +137,12 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen return "false", nil case time.Time: return value.Format(time.RFC3339), nil + case LocalDate: + return value.String(), nil + case LocalDateTime: + return value.String(), nil + case LocalTime: + return value.String(), nil case nil: return "", nil } @@ -137,7 +153,7 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen var values []string for i := 0; i < rv.Len(); i++ { item := rv.Index(i).Interface() - itemRepr, err := tomlValueStringRepresentation(item, indent, arraysOneElementPerLine) + itemRepr, err := tomlValueStringRepresentation(item, commented, indent, arraysOneElementPerLine) if err != nil { return "", err } @@ -151,12 +167,12 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen for _, value := range values { stringBuffer.WriteString(valueIndent) - stringBuffer.WriteString(value) + stringBuffer.WriteString(commented + value) stringBuffer.WriteString(`,`) stringBuffer.WriteString("\n") } - stringBuffer.WriteString(indent + "]") + stringBuffer.WriteString(indent + commented + "]") return stringBuffer.String(), nil } @@ -255,10 +271,10 @@ func sortAlphabetical(t *Tree) (vals []sortNode) { } func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) { - return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical) + return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical, false) } -func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder) (int64, error) { +func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder, parentCommented bool) (int64, error) { var orderedVals []sortNode switch ord { @@ -278,10 +294,6 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i if keyspace != "" { combinedKey = keyspace + "." + combinedKey } - var commented string - if t.commented { - commented = "# " - } switch node := v.(type) { // node has to be of those two types given how keys are sorted above @@ -302,24 +314,33 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i return bytesCount, errc } } + + var commented string + if parentCommented || t.commented || tv.commented { + commented = "# " + } writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err } - bytesCount, err = node.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord) + bytesCount, err = node.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord, parentCommented || t.commented || tv.commented) if err != nil { return bytesCount, err } case []*Tree: for _, subTree := range node { + var commented string + if parentCommented || t.commented || subTree.commented { + commented = "# " + } writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err } - bytesCount, err = subTree.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord) + bytesCount, err = subTree.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord, parentCommented || t.commented || subTree.commented) if err != nil { return bytesCount, err } @@ -332,7 +353,11 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) } - repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine) + var commented string + if parentCommented || t.commented || v.commented { + commented = "# " + } + repr, err := tomlValueStringRepresentation(v, commented, indent, arraysOneElementPerLine) if err != nil { return bytesCount, err } @@ -350,11 +375,8 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i } } - var commented string - if v.commented { - commented = "# " - } - writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n") + quotedKey := quoteKeyIfNeeded(k) + writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err @@ -365,6 +387,32 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i return bytesCount, nil } +// quote a key if it does not fit the bare key format (A-Za-z0-9_-) +// quoted keys use the same rules as strings +func quoteKeyIfNeeded(k string) string { + // when encoding a map with the 'quoteMapKeys' option enabled, the tree will contain + // keys that have already been quoted. + // not an ideal situation, but good enough of a stop gap. + if len(k) >= 2 && k[0] == '"' && k[len(k)-1] == '"' { + return k + } + isBare := true + for _, r := range k { + if !isValidBareChar(r) { + isBare = false + break + } + } + if isBare { + return k + } + return quoteKey(k) +} + +func quoteKey(k string) string { + return "\"" + encodeTomlString(k) + "\"" +} + func writeStrings(w io.Writer, s ...string) (int, error) { var n int for i := range s { @@ -387,12 +435,11 @@ func (t *Tree) WriteTo(w io.Writer) (int64, error) { // Output spans multiple lines, and is suitable for ingest by a TOML parser. // If the conversion cannot be performed, ToString returns a non-nil error. func (t *Tree) ToTomlString() (string, error) { - var buf bytes.Buffer - _, err := t.WriteTo(&buf) + b, err := t.Marshal() if err != nil { return "", err } - return buf.String(), nil + return string(b), nil } // String generates a human-readable representation of the current tree. diff --git a/vendor/github.com/spf13/cast/.travis.yml b/vendor/github.com/spf13/cast/.travis.yml index 6420d1c2..833a4879 100644 --- a/vendor/github.com/spf13/cast/.travis.yml +++ b/vendor/github.com/spf13/cast/.travis.yml @@ -4,6 +4,7 @@ env: sudo: required go: - "1.11.x" + - "1.12.x" - tip os: - linux diff --git a/vendor/github.com/spf13/cast/Makefile b/vendor/github.com/spf13/cast/Makefile index 7ccf8930..f01a5dbb 100644 --- a/vendor/github.com/spf13/cast/Makefile +++ b/vendor/github.com/spf13/cast/Makefile @@ -1,4 +1,4 @@ -# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html +GOVERSION := $(shell go version | cut -d ' ' -f 3 | cut -d '.' -f 2) .PHONY: check fmt lint test test-race vet test-cover-html help .DEFAULT_GOAL := help @@ -12,11 +12,13 @@ test-race: ## Run tests with race detector go test -race ./... fmt: ## Run gofmt linter +ifeq "$(GOVERSION)" "12" @for d in `go list` ; do \ if [ "`gofmt -l -s $$GOPATH/src/$$d | tee /dev/stderr`" ]; then \ echo "^ improperly formatted go files" && echo && exit 1; \ fi \ done +endif lint: ## Run golint linter @for d in `go list` ; do \ diff --git a/vendor/github.com/spf13/cast/caste.go b/vendor/github.com/spf13/cast/caste.go index a4859fb0..70c7291b 100644 --- a/vendor/github.com/spf13/cast/caste.go +++ b/vendor/github.com/spf13/cast/caste.go @@ -819,15 +819,15 @@ func ToStringE(i interface{}) (string, error) { case int8: return strconv.FormatInt(int64(s), 10), nil case uint: - return strconv.FormatInt(int64(s), 10), nil + return strconv.FormatUint(uint64(s), 10), nil case uint64: - return strconv.FormatInt(int64(s), 10), nil + return strconv.FormatUint(uint64(s), 10), nil case uint32: - return strconv.FormatInt(int64(s), 10), nil + return strconv.FormatUint(uint64(s), 10), nil case uint16: - return strconv.FormatInt(int64(s), 10), nil + return strconv.FormatUint(uint64(s), 10), nil case uint8: - return strconv.FormatInt(int64(s), 10), nil + return strconv.FormatUint(uint64(s), 10), nil case []byte: return string(s), nil case template.HTML: diff --git a/vendor/github.com/spf13/viper/.gitignore b/vendor/github.com/spf13/viper/.gitignore index 01b5c44b..d6941f32 100644 --- a/vendor/github.com/spf13/viper/.gitignore +++ b/vendor/github.com/spf13/viper/.gitignore @@ -1,29 +1,20 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so +/bin/ +/build/ +/var/ +/vendor/ -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.bench - -.vscode - -# exclude dependencies in the `/vendor` folder -vendor +# IDE integration +/.vscode/* +!/.vscode/launch.json +!/.vscode/tasks.json +/.idea/* +!/.idea/codeStyles/ +!/.idea/copyright/ +!/.idea/dataSources.xml +!/.idea/*.iml +!/.idea/externalDependencies.xml +!/.idea/go.imports.xml +!/.idea/modules.xml +!/.idea/runConfigurations/ +!/.idea/scopes/ +!/.idea/sqldialects.xml diff --git a/vendor/github.com/spf13/viper/.golangci.yml b/vendor/github.com/spf13/viper/.golangci.yml new file mode 100644 index 00000000..0ea9249e --- /dev/null +++ b/vendor/github.com/spf13/viper/.golangci.yml @@ -0,0 +1,24 @@ +linters-settings: + golint: + min-confidence: 0.1 + goimports: + local-prefixes: github.com/spf13/viper + +linters: + enable-all: true + disable: + - funlen + - maligned + + # TODO: fix me + - wsl + - gochecknoinits + - gosimple + - gochecknoglobals + - errcheck + - lll + - godox + - scopelint + - gocyclo + - gocognit + - gocritic \ No newline at end of file diff --git a/vendor/github.com/spf13/viper/.travis.yml b/vendor/github.com/spf13/viper/.travis.yml index bb83057b..ed677bbb 100644 --- a/vendor/github.com/spf13/viper/.travis.yml +++ b/vendor/github.com/spf13/viper/.travis.yml @@ -4,10 +4,13 @@ language: go env: global: - - GO111MODULE="on" + - GO111MODULE="on" + - GOFLAGS="-mod=readonly" go: - 1.11.x + - 1.12.x + - 1.13.x - tip os: @@ -27,5 +30,3 @@ script: after_success: - go get -u -d github.com/spf13/hugo - cd $GOPATH/src/github.com/spf13/hugo && make && ./hugo -s docs && cd - - -sudo: false diff --git a/vendor/github.com/spf13/viper/Makefile b/vendor/github.com/spf13/viper/Makefile new file mode 100644 index 00000000..1c2cab03 --- /dev/null +++ b/vendor/github.com/spf13/viper/Makefile @@ -0,0 +1,76 @@ +# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html + +OS = $(shell uname | tr A-Z a-z) +export PATH := $(abspath bin/):${PATH} + +# Build variables +BUILD_DIR ?= build +export CGO_ENABLED ?= 0 +export GOOS = $(shell go env GOOS) +ifeq (${VERBOSE}, 1) +ifeq ($(filter -v,${GOARGS}),) + GOARGS += -v +endif +TEST_FORMAT = short-verbose +endif + +# Dependency versions +GOTESTSUM_VERSION = 0.4.0 +GOLANGCI_VERSION = 1.21.0 + +# Add the ability to override some variables +# Use with care +-include override.mk + +.PHONY: clear +clear: ## Clear the working area and the project + rm -rf bin/ + +.PHONY: check +check: test lint ## Run tests and linters + +bin/gotestsum: bin/gotestsum-${GOTESTSUM_VERSION} + @ln -sf gotestsum-${GOTESTSUM_VERSION} bin/gotestsum +bin/gotestsum-${GOTESTSUM_VERSION}: + @mkdir -p bin + curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum-${GOTESTSUM_VERSION} && chmod +x ./bin/gotestsum-${GOTESTSUM_VERSION} + +TEST_PKGS ?= ./... +.PHONY: test +test: TEST_FORMAT ?= short +test: SHELL = /bin/bash +test: export CGO_ENABLED=1 +test: bin/gotestsum ## Run tests + @mkdir -p ${BUILD_DIR} + bin/gotestsum --no-summary=skipped --junitfile ${BUILD_DIR}/coverage.xml --format ${TEST_FORMAT} -- -race -coverprofile=${BUILD_DIR}/coverage.txt -covermode=atomic $(filter-out -v,${GOARGS}) $(if ${TEST_PKGS},${TEST_PKGS},./...) + +bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION} + @ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint +bin/golangci-lint-${GOLANGCI_VERSION}: + @mkdir -p bin + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION} + @mv bin/golangci-lint $@ + +.PHONY: lint +lint: bin/golangci-lint ## Run linter + bin/golangci-lint run + +.PHONY: fix +fix: bin/golangci-lint ## Fix lint violations + bin/golangci-lint run --fix + +# Add custom targets here +-include custom.mk + +.PHONY: list +list: ## List all make targets + @${MAKE} -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | sort + +.PHONY: help +.DEFAULT_GOAL := help +help: + @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +# Variable outputting/exporting rules +var-%: ; @echo $($*) +varexport-%: ; @echo $*=$($*) diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index 0208eac8..a574677f 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -2,6 +2,10 @@ Go configuration with fangs! +[![Actions](https://github.com/spf13/viper/workflows/CI/badge.svg)](https://github.com/spf13/viper) +[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper) + Many Go projects are built using Viper including: * [Hugo](http://gohugo.io) @@ -12,8 +16,14 @@ Many Go projects are built using Viper including: * [BloomApi](https://www.bloomapi.com/) * [doctl](https://github.com/digitalocean/doctl) * [Clairctl](https://github.com/jgsqware/clairctl) +* [Mercure](https://mercure.rocks) -[![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper) + +## Install + +```console +go get github.com/spf13/viper +``` ## What is Viper? @@ -23,7 +33,7 @@ to work within an application, and can handle all types of configuration needs and formats. It supports: * setting defaults -* reading from JSON, TOML, YAML, HCL, and Java properties config files +* reading from JSON, TOML, YAML, HCL, envfile and Java properties config files * live watching and re-reading of config files (optional) * reading from environment variables * reading from remote config systems (etcd or Consul), and watching changes @@ -31,8 +41,8 @@ and formats. It supports: * reading from buffer * setting explicit values -Viper can be thought of as a registry for all of your applications -configuration needs. +Viper can be thought of as a registry for all of your applications configuration needs. + ## Why Viper? @@ -42,34 +52,31 @@ Viper is here to help with that. Viper does the following for you: -1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, or Java properties formats. -2. Provide a mechanism to set default values for your different - configuration options. -3. Provide a mechanism to set override values for options specified through - command line flags. -4. Provide an alias system to easily rename parameters without breaking existing - code. -5. Make it easy to tell the difference between when a user has provided a - command line or config file which is the same as the default. +1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, INI, envfile or Java properties formats. +2. Provide a mechanism to set default values for your different configuration options. +3. Provide a mechanism to set override values for options specified through command line flags. +4. Provide an alias system to easily rename parameters without breaking existing code. +5. Make it easy to tell the difference between when a user has provided a command line or config file which is the same as the default. -Viper uses the following precedence order. Each item takes precedence over the -item below it: +Viper uses the following precedence order. Each item takes precedence over the item below it: - * explicit call to Set + * explicit call to `Set` * flag * env * config * key/value store * default -Viper configuration keys are case insensitive. +**Important:** Viper configuration keys are case insensitive. +There are ongoing discussions about making that optional. + ## Putting Values into Viper ### Establishing Defaults A good configuration system will support default values. A default value is not -required for a key, but it’s useful in the event that a key hasn’t been set via +required for a key, but it’s useful in the event that a key hasn't been set via config file, environment variable, remote configuration or flag. Examples: @@ -83,7 +90,7 @@ viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "cat ### Reading Config Files Viper requires minimal configuration so it knows where to look for config files. -Viper supports JSON, TOML, YAML, HCL, and Java Properties files. Viper can search multiple paths, but +Viper supports JSON, TOML, YAML, HCL, INI, envfile and Java Properties files. Viper can search multiple paths, but currently a single Viper instance only supports a single configuration file. Viper does not default to any configuration search paths leaving defaults decision to an application. @@ -94,6 +101,7 @@ where a configuration file is expected. ```go viper.SetConfigName("config") // name of config file (without extension) +viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name viper.AddConfigPath("/etc/appname/") // path to look for the config file in viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths viper.AddConfigPath(".") // optionally look for config in the working directory @@ -103,6 +111,44 @@ if err != nil { // Handle errors reading the config file } ``` +You can handle the specific case where no config file is found like this: + +```go +if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + // Config file not found; ignore error if desired + } else { + // Config file was found but another error was produced + } +} + +// Config file found and successfully parsed +``` + +*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmaticaly. For those configuration files that lie in the home of the user without any extension like `.bashrc` + +### Writing Config Files + +Reading from config files is useful, but at times you want to store all modifications made at run time. +For that, a bunch of commands are available, each with its own purpose: + +* WriteConfig - writes the current viper configuration to the predefined path, if exists. Errors if no predefined path. Will overwrite the current config file, if it exists. +* SafeWriteConfig - writes the current viper configuration to the predefined path. Errors if no predefined path. Will not overwrite the current config file, if it exists. +* WriteConfigAs - writes the current viper configuration to the given filepath. Will overwrite the given file, if it exists. +* SafeWriteConfigAs - writes the current viper configuration to the given filepath. Will not overwrite the given file, if it exists. + +As a rule of the thumb, everything marked with safe won't overwrite any file, but just create if not existent, whilst the default behavior is to create or truncate. + +A small examples section: + +```go +viper.WriteConfig() // writes current config to predefined path set by 'viper.AddConfigPath()' and 'viper.SetConfigName' +viper.SafeWriteConfig() +viper.WriteConfigAs("/path/to/my/.config") +viper.SafeWriteConfigAs("/path/to/my/.config") // will error since it has already been written +viper.SafeWriteConfigAs("/path/to/my/.other_config") +``` + ### Watching and re-reading config files Viper supports the ability to have your application live read a config file while running. @@ -186,7 +232,7 @@ with ENV: * `BindEnv(string...) : error` * `SetEnvPrefix(string)` * `SetEnvKeyReplacer(string...) *strings.Replacer` - * `AllowEmptyEnvVar(bool)` + * `AllowEmptyEnv(bool)` _When working with ENV variables, it’s important to recognize that Viper treats ENV variables as case sensitive._ @@ -199,9 +245,9 @@ prefix. `BindEnv` takes one or two parameters. The first parameter is the key name, the second is the name of the environment variable. The name of the environment variable is case sensitive. If the ENV variable name is not provided, then -Viper will automatically assume that the key name matches the ENV variable name, -but the ENV variable is IN ALL CAPS. When you explicitly provide the ENV -variable name, it **does not** automatically add the prefix. +Viper will automatically assume that the ENV variable matches the following format: prefix + "_" + the key name in ALL CAPS. When you explicitly provide the ENV variable name (the second parameter), +it **does not** automatically add the prefix. For example if the second parameter is "id", +Viper will look for the ENV variable "ID". One important thing to recognize when working with ENV variables is that the value will be read each time it is accessed. Viper does not fix the value when @@ -218,6 +264,9 @@ keys to an extent. This is useful if you want to use `-` or something in your `Get()` calls, but want your environmental variables to use `_` delimiters. An example of using it can be found in `viper_test.go`. +Alternatively, you can use `EnvKeyReplacer` with `NewWithOptions` factory function. +Unlike `SetEnvKeyReplacer`, it accepts a `StringReplacer` interface allowing you to write custom string replacing logic. + By default empty environment variables are considered unset and will fall back to the next configuration source. To treat empty environment variables as set, use the `AllowEmptyEnv` method. @@ -346,7 +395,7 @@ package: `import _ "github.com/spf13/viper/remote"` -Viper will read a config string (as JSON, TOML, YAML or HCL) retrieved from a path +Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path in a Key/Value store such as etcd or Consul. These values take precedence over default values, but are overridden by configuration values retrieved from disk, flags, or environment variables. @@ -381,7 +430,7 @@ how to use Consul. #### etcd ```go viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json") -viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop" +viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" err := viper.ReadRemoteConfig() ``` @@ -409,7 +458,7 @@ fmt.Println(viper.Get("hostname")) // myhostname.com ```go viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg") -viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop" +viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" err := viper.ReadRemoteConfig() ``` @@ -420,7 +469,7 @@ err := viper.ReadRemoteConfig() var runtime_viper = viper.New() runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml") -runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop" +runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" // read from remote config the first time. err := runtime_viper.ReadRemoteConfig() @@ -456,6 +505,7 @@ The following functions and methods exist: * `GetBool(key string) : bool` * `GetFloat64(key string) : float64` * `GetInt(key string) : int` + * `GetIntSlice(key string) : []int` * `GetString(key string) : string` * `GetStringMap(key string) : map[string]interface{}` * `GetStringMapString(key string) : map[string]string` @@ -611,15 +661,74 @@ type config struct { var C config -err := Unmarshal(&C) +err := viper.Unmarshal(&C) if err != nil { t.Fatalf("unable to decode into struct, %v", err) } ``` +If you want to unmarshal configuration where the keys themselves contain dot (the default key delimiter), +you have to change the delimiter: + +```go +v := viper.NewWithOptions(viper.KeyDelimiter("::")) + +v.SetDefault("chart::values", map[string]interface{}{ + "ingress": map[string]interface{}{ + "annotations": map[string]interface{}{ + "traefik.frontend.rule.type": "PathPrefix", + "traefik.ingress.kubernetes.io/ssl-redirect": "true", + }, + }, +}) + +type config struct { + Chart struct{ + Values map[string]interface{} + } +} + +var C config + +v.Unmarshal(&C) +``` + +Viper also supports unmarshaling into embedded structs: + +```go +/* +Example config: + +module: + enabled: true + token: 89h3f98hbwf987h3f98wenf89ehf +*/ +type config struct { + Module struct { + Enabled bool + + moduleConfig `mapstructure:",squash"` + } +} + +// moduleConfig could be in a module specific package +type moduleConfig struct { + Token string +} + +var C config + +err := viper.Unmarshal(&C) +if err != nil { + t.Fatalf("unable to decode into struct, %v", err) +} +``` + +Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. + ### Marshalling to string -You may need to marhsal all the settings held in viper into a string rather than write them to a file. +You may need to marshal all the settings held in viper into a string rather than write them to a file. You can use your favorite format's marshaller with the config returned by `AllSettings()`. ```go @@ -630,11 +739,11 @@ import ( func yamlStringSettings() string { c := viper.AllSettings() - bs, err := yaml.Marshal(c) - if err != nil { - t.Fatalf("unable to marshal config to YAML: %v", err) + bs, err := yaml.Marshal(c) + if err != nil { + log.Fatalf("unable to marshal config to YAML: %v", err) } - return string(bs) + return string(bs) } ``` @@ -672,13 +781,6 @@ different vipers. ## Q & A -Q: Why not INI files? - -A: Ini files are pretty awful. There’s no standard format, and they are hard to -validate. Viper is designed to work with JSON, TOML or YAML files. If someone -really wants to add this feature, I’d be happy to merge it. It’s easy to specify -which formats your application will permit. - Q: Why is it called “Viper”? A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe)) diff --git a/vendor/github.com/spf13/viper/flags.go b/vendor/github.com/spf13/viper/flags.go index dd32f4e1..b5ddbf5d 100644 --- a/vendor/github.com/spf13/viper/flags.go +++ b/vendor/github.com/spf13/viper/flags.go @@ -36,7 +36,7 @@ type pflagValue struct { flag *pflag.Flag } -// HasChanges returns whether the flag has changes or not. +// HasChanged returns whether the flag has changes or not. func (p pflagValue) HasChanged() bool { return p.flag.Changed } diff --git a/vendor/github.com/spf13/viper/go.mod b/vendor/github.com/spf13/viper/go.mod index 27943005..3d51b8c0 100644 --- a/vendor/github.com/spf13/viper/go.mod +++ b/vendor/github.com/spf13/viper/go.mod @@ -1,9 +1,11 @@ module github.com/spf13/viper +go 1.12 + require ( github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect github.com/coreos/bbolt v1.3.2 // indirect - github.com/coreos/etcd v3.3.10+incompatible // indirect + github.com/coreos/etcd v3.3.13+incompatible // indirect github.com/coreos/go-semver v0.2.0 // indirect github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect @@ -18,18 +20,21 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.9.0 // indirect github.com/hashicorp/hcl v1.0.0 github.com/jonboulle/clockwork v0.1.0 // indirect - github.com/magiconair/properties v1.8.0 + github.com/json-iterator/go v1.1.9 // indirect + github.com/magiconair/properties v1.8.1 github.com/mitchellh/mapstructure v1.1.2 + github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pelletier/go-toml v1.2.0 github.com/prometheus/client_golang v0.9.3 // indirect + github.com/smartystreets/goconvey v1.6.4 // indirect github.com/soheilhy/cmux v0.1.4 // indirect github.com/spf13/afero v1.1.2 github.com/spf13/cast v1.3.0 github.com/spf13/jwalterweatherman v1.0.0 github.com/spf13/pflag v1.0.3 - github.com/stretchr/testify v1.2.2 + github.com/stretchr/testify v1.3.0 + github.com/subosito/gotenv v1.2.0 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect - github.com/ugorji/go v1.1.4 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 go.etcd.io/bbolt v1.3.2 // indirect @@ -39,5 +44,6 @@ require ( golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect google.golang.org/grpc v1.21.0 // indirect - gopkg.in/yaml.v2 v2.2.2 + gopkg.in/ini.v1 v1.51.0 + gopkg.in/yaml.v2 v2.2.4 ) diff --git a/vendor/github.com/spf13/viper/go.sum b/vendor/github.com/spf13/viper/go.sum index 97afaffe..098f5d3a 100644 --- a/vendor/github.com/spf13/viper/go.sum +++ b/vendor/github.com/spf13/viper/go.sum @@ -13,14 +13,15 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= @@ -48,6 +49,9 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= @@ -60,6 +64,10 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -71,12 +79,17 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= @@ -101,6 +114,10 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -112,13 +129,16 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= @@ -159,6 +179,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -170,9 +192,11 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go index 952cad44..b7889696 100644 --- a/vendor/github.com/spf13/viper/util.go +++ b/vendor/github.com/spf13/viper/util.go @@ -114,11 +114,11 @@ func absPathify(inPath string) string { return "" } -// Check if File / Directory Exists +// Check if file Exists func exists(fs afero.Fs, path string) (bool, error) { - _, err := fs.Stat(path) + stat, err := fs.Stat(path) if err == nil { - return true, nil + return !stat.IsDir(), nil } if os.IsNotExist(err) { return false, nil diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index a3d37f8c..265eae7f 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -3,7 +3,7 @@ // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. -// Viper is a application configuration system. +// Viper is an application configuration system. // It believes that applications can be configured a variety of ways // via flags, ENVIRONMENT variables, configuration files retrieved // from the file system, or a remote key/value store. @@ -23,6 +23,7 @@ import ( "bytes" "encoding/csv" "encoding/json" + "errors" "fmt" "io" "log" @@ -33,18 +34,19 @@ import ( "sync" "time" - yaml "gopkg.in/yaml.v2" - "github.com/fsnotify/fsnotify" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/printer" "github.com/magiconair/properties" "github.com/mitchellh/mapstructure" - toml "github.com/pelletier/go-toml" + "github.com/pelletier/go-toml" "github.com/spf13/afero" "github.com/spf13/cast" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/pflag" + "github.com/subosito/gotenv" + "gopkg.in/ini.v1" + "gopkg.in/yaml.v2" ) // ConfigMarshalError happens when failing to marshal the configuration. @@ -114,6 +116,14 @@ func (fnfe ConfigFileNotFoundError) Error() string { return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) } +// ConfigFileAlreadyExistsError denotes failure to write new configuration file. +type ConfigFileAlreadyExistsError string + +// Error returns the formatted error when configuration already exists. +func (faee ConfigFileAlreadyExistsError) Error() string { + return fmt.Sprintf("Config File %q Already Exists", string(faee)) +} + // A DecoderConfigOption can be passed to viper.Unmarshal to configure // mapstructure.DecoderConfig options type DecoderConfigOption func(*mapstructure.DecoderConfig) @@ -187,7 +197,7 @@ type Viper struct { envPrefix string automaticEnvApplied bool - envKeyReplacer *strings.Replacer + envKeyReplacer StringReplacer allowEmptyEnv bool config map[string]interface{} @@ -225,12 +235,58 @@ func New() *Viper { return v } -// Intended for testing, will reset all to default settings. +// Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney. +// If you're unfamiliar with this style, +// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and +// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis. +type Option interface { + apply(v *Viper) +} + +type optionFunc func(v *Viper) + +func (fn optionFunc) apply(v *Viper) { + fn(v) +} + +// KeyDelimiter sets the delimiter used for determining key parts. +// By default it's value is ".". +func KeyDelimiter(d string) Option { + return optionFunc(func(v *Viper) { + v.keyDelim = d + }) +} + +// StringReplacer applies a set of replacements to a string. +type StringReplacer interface { + // Replace returns a copy of s with all replacements performed. + Replace(s string) string +} + +// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys. +func EnvKeyReplacer(r StringReplacer) Option { + return optionFunc(func(v *Viper) { + v.envKeyReplacer = r + }) +} + +// NewWithOptions creates a new Viper instance. +func NewWithOptions(opts ...Option) *Viper { + v := New() + + for _, opt := range opts { + opt.apply(v) + } + + return v +} + +// Reset is intended for testing, will reset all to default settings. // In the public interface for the viper package so applications // can use it in their testing as well. func Reset() { v = New() - SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} + SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} SupportedRemoteProviders = []string{"etcd", "consul"} } @@ -269,7 +325,7 @@ type RemoteProvider interface { } // SupportedExts are universally supported extensions. -var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} +var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} // SupportedRemoteProviders are universally supported remote providers. var SupportedRemoteProviders = []string{"etcd", "consul"} @@ -294,6 +350,7 @@ func (v *Viper) WatchConfig() { filename, err := v.getConfigFile() if err != nil { log.Printf("error: %v\n", err) + initWG.Done() return } @@ -343,7 +400,7 @@ func (v *Viper) WatchConfig() { } }() watcher.Add(configDir) - initWG.Done() // done initalizing the watch in this go routine, so the parent routine can move on... + initWG.Done() // done initializing the watch in this go routine, so the parent routine can move on... eventsWG.Wait() // now, wait for event loop to end in this go-routine... }() initWG.Wait() // make sure that the go routine above fully ended before returning @@ -668,7 +725,7 @@ func GetViper() *Viper { func Get(key string) interface{} { return v.Get(key) } func (v *Viper) Get(key string) interface{} { lcaseKey := strings.ToLower(key) - val := v.find(lcaseKey) + val := v.find(lcaseKey, true) if val == nil { return nil } @@ -705,6 +762,8 @@ func (v *Viper) Get(key string) interface{} { return cast.ToDuration(val) case []string: return cast.ToStringSlice(val) + case []int: + return cast.ToIntSlice(val) } } @@ -794,6 +853,12 @@ func (v *Viper) GetDuration(key string) time.Duration { return cast.ToDuration(v.Get(key)) } +// GetIntSlice returns the value associated with the key as a slice of int values. +func GetIntSlice(key string) []int { return v.GetIntSlice(key) } +func (v *Viper) GetIntSlice(key string) []int { + return cast.ToIntSlice(v.Get(key)) +} + // GetStringSlice returns the value associated with the key as a slice of strings. func GetStringSlice(key string) []string { return v.GetStringSlice(key) } func (v *Viper) GetStringSlice(key string) []string { @@ -884,8 +949,11 @@ func decode(input interface{}, config *mapstructure.DecoderConfig) error { // UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent // in the destination struct. -func (v *Viper) UnmarshalExact(rawVal interface{}) error { - config := defaultDecoderConfig(rawVal) +func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error { + return v.UnmarshalExact(rawVal, opts...) +} +func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error { + config := defaultDecoderConfig(rawVal, opts...) config.ErrorUnused = true err := decode(v.AllSettings(), config) @@ -950,7 +1018,7 @@ func BindEnv(input ...string) error { return v.BindEnv(input...) } func (v *Viper) BindEnv(input ...string) error { var key, envkey string if len(input) == 0 { - return fmt.Errorf("BindEnv missing key to bind to") + return fmt.Errorf("missing key to bind to") } key = strings.ToLower(input[0]) @@ -967,12 +1035,15 @@ func (v *Viper) BindEnv(input ...string) error { } // Given a key, find the value. -// Viper will check in the following order: -// flag, env, config file, key/value store, default. +// // Viper will check to see if an alias exists first. +// Viper will then check in the following order: +// flag, env, config file, key/value store. +// Lastly, if no value was found and flagDefault is true, and if the key +// corresponds to a flag, the flag's default value is returned. +// // Note: this assumes a lower-cased key given. -func (v *Viper) find(lcaseKey string) interface{} { - +func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} { var ( val interface{} exists bool @@ -1012,6 +1083,11 @@ func (v *Viper) find(lcaseKey string) interface{} { s = strings.TrimSuffix(s, "]") res, _ := readAsCSV(s) return res + case "intSlice": + s := strings.TrimPrefix(flag.ValueString(), "[") + s = strings.TrimSuffix(s, "]") + res, _ := readAsCSV(s) + return cast.ToIntSlice(res) default: return flag.ValueString() } @@ -1068,24 +1144,31 @@ func (v *Viper) find(lcaseKey string) interface{} { return nil } - // last chance: if no other value is returned and a flag does exist for the value, - // get the flag's value even if the flag's value has not changed - if flag, exists := v.pflags[lcaseKey]; exists { - switch flag.ValueType() { - case "int", "int8", "int16", "int32", "int64": - return cast.ToInt(flag.ValueString()) - case "bool": - return cast.ToBool(flag.ValueString()) - case "stringSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return res - default: - return flag.ValueString() + if flagDefault { + // last chance: if no value is found and a flag does exist for the key, + // get the flag's default value even if the flag's value has not been set. + if flag, exists := v.pflags[lcaseKey]; exists { + switch flag.ValueType() { + case "int", "int8", "int16", "int32", "int64": + return cast.ToInt(flag.ValueString()) + case "bool": + return cast.ToBool(flag.ValueString()) + case "stringSlice": + s := strings.TrimPrefix(flag.ValueString(), "[") + s = strings.TrimSuffix(s, "]") + res, _ := readAsCSV(s) + return res + case "intSlice": + s := strings.TrimPrefix(flag.ValueString(), "[") + s = strings.TrimSuffix(s, "]") + res, _ := readAsCSV(s) + return cast.ToIntSlice(res) + default: + return flag.ValueString() + } } + // last item, no need to check shadowing } - // last item, no need to check shadowing return nil } @@ -1104,7 +1187,7 @@ func readAsCSV(val string) ([]string, error) { func IsSet(key string) bool { return v.IsSet(key) } func (v *Viper) IsSet(key string) bool { lcaseKey := strings.ToLower(key) - val := v.find(lcaseKey) + val := v.find(lcaseKey, false) return val != nil } @@ -1123,8 +1206,8 @@ func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) { v.envKeyReplacer = r } -// Aliases provide another accessor for the same key. -// This enables one to change a name without breaking the application +// RegisterAlias creates an alias that provides another accessor for the same key. +// This enables one to change a name without breaking the application. func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) } func (v *Viper) RegisterAlias(alias string, key string) { v.registerAlias(alias, strings.ToLower(key)) @@ -1311,11 +1394,10 @@ func (v *Viper) WriteConfig() error { // SafeWriteConfig writes current configuration to file only if the file does not exist. func SafeWriteConfig() error { return v.SafeWriteConfig() } func (v *Viper) SafeWriteConfig() error { - filename, err := v.getConfigFile() - if err != nil { - return err + if len(v.configPaths) < 1 { + return errors.New("missing configuration for 'configPath'") } - return v.writeConfig(filename, false) + return v.SafeWriteConfigAs(filepath.Join(v.configPaths[0], v.configName+"."+v.configType)) } // WriteConfigAs writes current configuration to a given filename. @@ -1327,15 +1409,18 @@ func (v *Viper) WriteConfigAs(filename string) error { // SafeWriteConfigAs writes current configuration to a given filename if it does not exist. func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } func (v *Viper) SafeWriteConfigAs(filename string) error { + alreadyExists, err := afero.Exists(v.fs, filename) + if alreadyExists && err == nil { + return ConfigFileAlreadyExistsError(filename) + } return v.writeConfig(filename, false) } -func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) } func (v *Viper) writeConfig(filename string, force bool) error { jww.INFO.Println("Attempting to write configuration to file.") ext := filepath.Ext(filename) if len(ext) <= 1 { - return fmt.Errorf("Filename: %s requires valid extension.", filename) + return fmt.Errorf("filename: %s requires valid extension", filename) } configType := ext[1:] if !stringInSlice(configType, SupportedExts) { @@ -1344,21 +1429,21 @@ func (v *Viper) writeConfig(filename string, force bool) error { if v.config == nil { v.config = make(map[string]interface{}) } - var flags int - if force == true { - flags = os.O_CREATE | os.O_TRUNC | os.O_WRONLY - } else { - if _, err := os.Stat(filename); os.IsNotExist(err) { - flags = os.O_WRONLY - } else { - return fmt.Errorf("File: %s exists. Use WriteConfig to overwrite.", filename) - } + flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY + if !force { + flags |= os.O_EXCL } f, err := v.fs.OpenFile(filename, flags, v.configPermissions) if err != nil { return err } - return v.marshalWriter(f, configType) + defer f.Close() + + if err := v.marshalWriter(f, configType); err != nil { + return err + } + + return f.Sync() } // Unmarshal a Reader into a map. @@ -1382,7 +1467,7 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { } case "hcl": - obj, err := hcl.Parse(string(buf.Bytes())) + obj, err := hcl.Parse(buf.String()) if err != nil { return ConfigParseError{err} } @@ -1400,6 +1485,15 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { c[k] = v } + case "dotenv", "env": + env, err := gotenv.StrictParse(buf) + if err != nil { + return ConfigParseError{err} + } + for k, v := range env { + c[k] = v + } + case "properties", "props", "prop": v.properties = properties.NewProperties() var err error @@ -1415,6 +1509,23 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { // set innermost value deepestMap[lastKey] = value } + + case "ini": + cfg := ini.Empty() + err := cfg.Append(buf.Bytes()) + if err != nil { + return ConfigParseError{err} + } + sections := cfg.Sections() + for i := 0; i < len(sections); i++ { + section := sections[i] + keys := section.Keys() + for j := 0; j < len(keys); j++ { + key := keys[j] + value := cfg.Section(section.Name()).Key(key.Name()).String() + c[section.Name()+"."+key.Name()] = value + } + } } insensitiviseMap(c) @@ -1422,9 +1533,6 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { } // Marshal a map into Writer. -func marshalWriter(f afero.File, configType string) error { - return v.marshalWriter(f, configType) -} func (v *Viper) marshalWriter(f afero.File, configType string) error { c := v.AllSettings() switch configType { @@ -1440,6 +1548,9 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error { case "hcl": b, err := json.Marshal(c) + if err != nil { + return ConfigMarshalError{err} + } ast, err := hcl.Parse(string(b)) if err != nil { return ConfigMarshalError{err} @@ -1465,6 +1576,18 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error { return ConfigMarshalError{err} } + case "dotenv", "env": + lines := []string{} + for _, key := range v.AllKeys() { + envName := strings.ToUpper(strings.Replace(key, ".", "_", -1)) + val := v.Get(key) + lines = append(lines, fmt.Sprintf("%v=%v", envName, val)) + } + s := strings.Join(lines, "\n") + if _, err := f.WriteString(s); err != nil { + return ConfigMarshalError{err} + } + case "toml": t, err := toml.TreeFromMap(c) if err != nil { @@ -1483,6 +1606,22 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error { if _, err = f.WriteString(string(b)); err != nil { return ConfigMarshalError{err} } + + case "ini": + keys := v.AllKeys() + cfg := ini.Empty() + ini.PrettyFormat = false + for i := 0; i < len(keys); i++ { + key := keys[i] + lastSep := strings.LastIndex(key, ".") + sectionName := key[:(lastSep)] + keyName := key[(lastSep + 1):] + if sectionName == "default" { + sectionName = "" + } + cfg.Section(sectionName).Key(keyName).SetValue(Get(key).(string)) + } + cfg.WriteTo(f) } return nil } @@ -1629,7 +1768,7 @@ func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{} func (v *Viper) watchKeyValueConfigOnChannel() error { for _, rp := range v.remoteProviders { respc, _ := RemoteConfig.WatchChannel(rp) - //Todo: Add quit channel + // Todo: Add quit channel go func(rc <-chan *RemoteResponse) { for { b := <-rc @@ -1665,7 +1804,7 @@ func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface } // AllKeys returns all keys holding a value, regardless of where they are set. -// Nested keys are returned with a v.keyDelim (= ".") separator +// Nested keys are returned with a v.keyDelim separator func AllKeys() []string { return v.AllKeys() } func (v *Viper) AllKeys() []string { m := map[string]bool{} @@ -1679,7 +1818,7 @@ func (v *Viper) AllKeys() []string { m = v.flattenAndMergeMap(m, v.defaults, "") // convert set of paths to list - a := []string{} + a := make([]string, 0, len(m)) for x := range m { a = append(a, x) } @@ -1688,7 +1827,7 @@ func (v *Viper) AllKeys() []string { // flattenAndMergeMap recursively flattens the given map into a map[string]bool // of key paths (used as a set, easier to manipulate than a []string): -// - each path is merged into a single key string, delimited with v.keyDelim (= ".") +// - each path is merged into a single key string, delimited with v.keyDelim // - if a path is shadowed by an earlier value in the initial shadow map, // it is skipped. // The resulting set of paths is merged to the given shadow set at the same time. @@ -1728,7 +1867,7 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool { // scan keys outer: - for k, _ := range m { + for k := range m { path := strings.Split(k, v.keyDelim) // scan intermediate paths var parentKey string @@ -1837,6 +1976,12 @@ func (v *Viper) searchInPath(in string) (filename string) { } } + if v.configType != "" { + if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b { + return filepath.Join(in, v.configName) + } + } + return "" } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index e0364e9e..bf89ecd2 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -32,7 +32,8 @@ func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args return Contains(t, s, contains, append([]interface{}{msg}, args...)...) } -// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +// DirExistsf checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -160,7 +161,8 @@ func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { return False(t, value, append([]interface{}{msg}, args...)...) } -// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +// FileExistsf checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -267,7 +269,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -325,14 +327,6 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) } -// YAMLEqf asserts that two YAML strings are equivalent. -func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) -} - // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // @@ -369,6 +363,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) } +// Neverf asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Nilf asserts that the specified object is nil. // // assert.Nilf(t, err, "error message %s", "formatted") @@ -379,6 +384,15 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool return Nil(t, object, append([]interface{}{msg}, args...)...) } +// NoDirExistsf checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NoDirExists(t, path, append([]interface{}{msg}, args...)...) +} + // NoErrorf asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() @@ -392,6 +406,15 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { return NoError(t, err, append([]interface{}{msg}, args...)...) } +// NoFileExistsf checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NoFileExists(t, path, append([]interface{}{msg}, args...)...) +} + // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // @@ -462,6 +485,19 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) } +// NotSamef asserts that two pointers do not reference the same object. +// +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // @@ -491,6 +527,18 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool return Panics(t, f, append([]interface{}{msg}, args...)...) } +// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...) +} + // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // @@ -557,6 +605,14 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } +// YAMLEqf asserts that two YAML strings are equivalent. +func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // Zerof asserts that i is the zero value for its type. func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 26830403..75ecdcaa 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -53,7 +53,8 @@ func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, return Containsf(a.t, s, contains, msg, args...) } -// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -61,7 +62,8 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { return DirExists(a.t, path, msgAndArgs...) } -// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +// DirExistsf checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -309,7 +311,8 @@ func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { return Falsef(a.t, value, msg, args...) } -// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -317,7 +320,8 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { return FileExists(a.t, path, msgAndArgs...) } -// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +// FileExistsf checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -521,7 +525,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, (22 / 7.0), 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -563,7 +567,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -639,22 +643,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. return JSONEqf(a.t, expected, actual, msg, args...) } -// YAMLEq asserts that two YAML strings are equivalent. -func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return YAMLEq(a.t, expected, actual, msgAndArgs...) -} - -// YAMLEqf asserts that two YAML strings are equivalent. -func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return YAMLEqf(a.t, expected, actual, msg, args...) -} - // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // @@ -727,6 +715,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i return Lessf(a.t, e1, e2, msg, args...) } +// Never asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Never(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Neverf asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Neverf(a.t, condition, waitFor, tick, msg, args...) +} + // Nil asserts that the specified object is nil. // // a.Nil(err) @@ -747,6 +757,24 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b return Nilf(a.t, object, msg, args...) } +// NoDirExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoDirExists(a.t, path, msgAndArgs...) +} + +// NoDirExistsf checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoDirExistsf(a.t, path, msg, args...) +} + // NoError asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() @@ -773,6 +801,24 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { return NoErrorf(a.t, err, msg, args...) } +// NoFileExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoFileExists(a.t, path, msgAndArgs...) +} + +// NoFileExistsf checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoFileExistsf(a.t, path, msg, args...) +} + // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // @@ -913,6 +959,32 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg return NotRegexpf(a.t, rx, str, msg, args...) } +// NotSame asserts that two pointers do not reference the same object. +// +// a.NotSame(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotSame(a.t, expected, actual, msgAndArgs...) +} + +// NotSamef asserts that two pointers do not reference the same object. +// +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotSamef(a.t, expected, actual, msg, args...) +} + // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // @@ -961,6 +1033,30 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { return Panics(a.t, f, msgAndArgs...) } +// PanicsWithError asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return PanicsWithError(a.t, errString, f, msgAndArgs...) +} + +// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return PanicsWithErrorf(a.t, errString, f, msg, args...) +} + // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // @@ -1103,6 +1199,22 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta return WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// YAMLEq asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return YAMLEq(a.t, expected, actual, msgAndArgs...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return YAMLEqf(a.t, expected, actual, msg, args...) +} + // Zero asserts that i is the zero value for its type. func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 044da8b0..bdd81389 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -11,6 +11,7 @@ import ( "reflect" "regexp" "runtime" + "runtime/debug" "strings" "time" "unicode" @@ -21,7 +22,7 @@ import ( yaml "gopkg.in/yaml.v2" ) -//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl +//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" // TestingT is an interface wrapper around *testing.T type TestingT interface { @@ -351,6 +352,19 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) } +// validateEqualArgs checks whether provided arguments can be safely used in the +// Equal/NotEqual functions. +func validateEqualArgs(expected, actual interface{}) error { + if expected == nil && actual == nil { + return nil + } + + if isFunction(expected) || isFunction(actual) { + return errors.New("cannot take func type as argument") + } + return nil +} + // Same asserts that two pointers reference the same object. // // assert.Same(t, ptr1, ptr2) @@ -362,18 +376,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b h.Helper() } - expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual) - if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr { - return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...) - } - - expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual) - if expectedType != actualType { - return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v", - expectedType, actualType), msgAndArgs...) - } - - if expected != actual { + if !samePointers(expected, actual) { return Fail(t, fmt.Sprintf("Not same: \n"+ "expected: %p %#v\n"+ "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) @@ -382,6 +385,42 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b return true } +// NotSame asserts that two pointers do not reference the same object. +// +// assert.NotSame(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if samePointers(expected, actual) { + return Fail(t, fmt.Sprintf( + "Expected and actual point to the same object: %p %#v", + expected, expected), msgAndArgs...) + } + return true +} + +// samePointers compares two generic interface objects and returns whether +// they point to the same object +func samePointers(first, second interface{}) bool { + firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) + if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { + return false + } + + firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) + if firstType != secondType { + return false + } + + // compare pointer addresses + return first == second +} + // formatUnequalValues takes two values of arbitrary types and returns string // representations appropriate to be presented to the user. // @@ -393,9 +432,11 @@ func formatUnequalValues(expected, actual interface{}) (e string, a string) { return fmt.Sprintf("%T(%#v)", expected, expected), fmt.Sprintf("%T(%#v)", actual, actual) } - - return fmt.Sprintf("%#v", expected), - fmt.Sprintf("%#v", actual) + switch expected.(type) { + case time.Duration: + return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) + } + return fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual) } // EqualValues asserts that two objects are equal or convertable to the same types @@ -901,15 +942,17 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { type PanicTestFunc func() // didPanic returns true if the function passed to it panics. Otherwise, it returns false. -func didPanic(f PanicTestFunc) (bool, interface{}) { +func didPanic(f PanicTestFunc) (bool, interface{}, string) { didPanic := false var message interface{} + var stack string func() { defer func() { if message = recover(); message != nil { didPanic = true + stack = string(debug.Stack()) } }() @@ -918,7 +961,7 @@ func didPanic(f PanicTestFunc) (bool, interface{}) { }() - return didPanic, message + return didPanic, message, stack } @@ -930,7 +973,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { h.Helper() } - if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { + if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } @@ -946,12 +989,34 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr h.Helper() } - funcDidPanic, panicValue := didPanic(f) + funcDidPanic, panicValue, panickedStack := didPanic(f) if !funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } if panicValue != expected { - return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...) + return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...) + } + + return true +} + +// PanicsWithError asserts that the code inside the specified PanicTestFunc +// panics, and that the recovered panic value is an error that satisfies the +// EqualError comparison. +// +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + funcDidPanic, panicValue, panickedStack := didPanic(f) + if !funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) + } + panicErr, ok := panicValue.(error) + if !ok || panicErr.Error() != errString { + return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) } return true @@ -965,8 +1030,8 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { h.Helper() } - if funcDidPanic, panicValue := didPanic(f); funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...) + if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...) } return true @@ -1026,7 +1091,7 @@ func toFloat(x interface{}) (float64, bool) { // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1314,7 +1379,8 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { return true } -// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1332,7 +1398,24 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { return true } -// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +// NoFileExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + info, err := os.Lstat(path) + if err != nil { + return true + } + if info.IsDir() { + return true + } + return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...) +} + +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1350,6 +1433,25 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { return true } +// NoDirExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + info, err := os.Lstat(path) + if err != nil { + if os.IsNotExist(err) { + return true + } + return true + } + if !info.IsDir() { + return true + } + return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) +} + // JSONEq asserts that two JSON strings are equivalent. // // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) @@ -1439,15 +1541,6 @@ func diff(expected interface{}, actual interface{}) string { return "\n\nDiff:\n" + diff } -// validateEqualArgs checks whether provided arguments can be safely used in the -// Equal/NotEqual functions. -func validateEqualArgs(expected, actual interface{}) error { - if isFunction(expected) || isFunction(actual) { - return errors.New("cannot take func type as argument") - } - return nil -} - func isFunction(arg interface{}) bool { if arg == nil { return false @@ -1475,24 +1568,59 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t h.Helper() } + ch := make(chan bool, 1) + timer := time.NewTimer(waitFor) - ticker := time.NewTicker(tick) - checkPassed := make(chan bool) defer timer.Stop() + + ticker := time.NewTicker(tick) defer ticker.Stop() - defer close(checkPassed) - for { + + for tick := ticker.C; ; { select { case <-timer.C: return Fail(t, "Condition never satisfied", msgAndArgs...) - case result := <-checkPassed: - if result { + case <-tick: + tick = nil + go func() { ch <- condition() }() + case v := <-ch: + if v { return true } - case <-ticker.C: - go func() { - checkPassed <- condition() - }() + tick = ticker.C + } + } +} + +// Never asserts that the given condition doesn't satisfy in waitFor time, +// periodically checking the target function each tick. +// +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + return true + case <-tick: + tick = nil + go func() { ch <- condition() }() + case v := <-ch: + if v { + return Fail(t, "Condition satisfied", msgAndArgs...) + } + tick = ticker.C } } } diff --git a/vendor/github.com/stretchr/testify/assert/forward_assertions.go b/vendor/github.com/stretchr/testify/assert/forward_assertions.go index 9ad56851..df189d23 100644 --- a/vendor/github.com/stretchr/testify/assert/forward_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/forward_assertions.go @@ -13,4 +13,4 @@ func New(t TestingT) *Assertions { } } -//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs +//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" diff --git a/vendor/github.com/subosito/gotenv/.env b/vendor/github.com/subosito/gotenv/.env new file mode 100644 index 00000000..6405eca7 --- /dev/null +++ b/vendor/github.com/subosito/gotenv/.env @@ -0,0 +1 @@ +HELLO=world diff --git a/vendor/github.com/subosito/gotenv/.env.invalid b/vendor/github.com/subosito/gotenv/.env.invalid new file mode 100644 index 00000000..016d5e0c --- /dev/null +++ b/vendor/github.com/subosito/gotenv/.env.invalid @@ -0,0 +1 @@ +lol$wut diff --git a/vendor/github.com/subosito/gotenv/.gitignore b/vendor/github.com/subosito/gotenv/.gitignore new file mode 100644 index 00000000..2b8d4561 --- /dev/null +++ b/vendor/github.com/subosito/gotenv/.gitignore @@ -0,0 +1,3 @@ +*.test +*.out +annotate.json diff --git a/vendor/github.com/subosito/gotenv/.travis.yml b/vendor/github.com/subosito/gotenv/.travis.yml new file mode 100644 index 00000000..3370d5f4 --- /dev/null +++ b/vendor/github.com/subosito/gotenv/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.x +os: + - linux + - osx +script: + - go test -test.v -coverprofile=coverage.out -covermode=count +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/subosito/gotenv/CHANGELOG.md b/vendor/github.com/subosito/gotenv/CHANGELOG.md new file mode 100644 index 00000000..67f68738 --- /dev/null +++ b/vendor/github.com/subosito/gotenv/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog + +## [1.2.0] - 2019-08-03 + +### Added + +- Add `Must` helper to raise an error as panic. It can be used with `Load` and `OverLoad`. +- Add more tests to be 100% coverage. +- Add CHANGELOG +- Add more OS for the test: OSX and Windows + +### Changed + +- Reduce complexity and improve source code for having `A+` score in [goreportcard](https://goreportcard.com/report/github.com/subosito/gotenv). +- Updated README with mentions to all available functions + +### Removed + +- Remove `ErrFormat` +- Remove `MustLoad` and `MustOverload`, replaced with `Must` helper. + +## [1.1.1] - 2018-06-05 + +### Changed + +- Replace `os.Getenv` with `os.LookupEnv` to ensure that the environment variable is not set, by [radding](https://github.com/radding) + +## [1.1.0] - 2017-03-20 + +### Added + +- Supports carriage return in env +- Handle files with UTF-8 BOM + +### Changed + +- Whitespace handling + +### Fixed + +- Incorrect variable expansion +- Handling escaped '$' characters + +## [1.0.0] - 2014-10-05 + +First stable release. + diff --git a/vendor/github.com/subosito/gotenv/LICENSE b/vendor/github.com/subosito/gotenv/LICENSE new file mode 100644 index 00000000..f64ccaed --- /dev/null +++ b/vendor/github.com/subosito/gotenv/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Alif Rachmawadi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/subosito/gotenv/README.md b/vendor/github.com/subosito/gotenv/README.md new file mode 100644 index 00000000..d610cdf0 --- /dev/null +++ b/vendor/github.com/subosito/gotenv/README.md @@ -0,0 +1,131 @@ +# gotenv + +[![Build Status](https://travis-ci.org/subosito/gotenv.svg?branch=master)](https://travis-ci.org/subosito/gotenv) +[![Build status](https://ci.appveyor.com/api/projects/status/wb2e075xkfl0m0v2/branch/master?svg=true)](https://ci.appveyor.com/project/subosito/gotenv/branch/master) +[![Coverage Status](https://badgen.net/codecov/c/github/subosito/gotenv)](https://codecov.io/gh/subosito/gotenv) +[![Go Report Card](https://goreportcard.com/badge/github.com/subosito/gotenv)](https://goreportcard.com/report/github.com/subosito/gotenv) +[![GoDoc](https://godoc.org/github.com/subosito/gotenv?status.svg)](https://godoc.org/github.com/subosito/gotenv) + +Load environment variables dynamically in Go. + +## Usage + +Put the gotenv package on your `import` statement: + +```go +import "github.com/subosito/gotenv" +``` + +To modify your app environment variables, `gotenv` expose 2 main functions: + +- `gotenv.Load` +- `gotenv.Apply` + +By default, `gotenv.Load` will look for a file called `.env` in the current working directory. + +Behind the scene, it will then load `.env` file and export the valid variables to the environment variables. Make sure you call the method as soon as possible to ensure it loads all variables, say, put it on `init()` function. + +Once loaded you can use `os.Getenv()` to get the value of the variable. + +Let's say you have `.env` file: + +``` +APP_ID=1234567 +APP_SECRET=abcdef +``` + +Here's the example of your app: + +```go +package main + +import ( + "github.com/subosito/gotenv" + "log" + "os" +) + +func init() { + gotenv.Load() +} + +func main() { + log.Println(os.Getenv("APP_ID")) // "1234567" + log.Println(os.Getenv("APP_SECRET")) // "abcdef" +} +``` + +You can also load other than `.env` file if you wish. Just supply filenames when calling `Load()`. It will load them in order and the first value set for a variable will win.: + +```go +gotenv.Load(".env.production", "credentials") +``` + +While `gotenv.Load` loads entries from `.env` file, `gotenv.Apply` allows you to use any `io.Reader`: + +```go +gotenv.Apply(strings.NewReader("APP_ID=1234567")) + +log.Println(os.Getenv("APP_ID")) +// Output: "1234567" +``` + +Both `gotenv.Load` and `gotenv.Apply` **DO NOT** overrides existing environment variables. If you want to override existing ones, you can see section below. + +### Environment Overrides + +Besides above functions, `gotenv` also provides another functions that overrides existing: + +- `gotenv.OverLoad` +- `gotenv.OverApply` + + +Here's the example of this overrides behavior: + +```go +os.Setenv("HELLO", "world") + +// NOTE: using Apply existing value will be reserved +gotenv.Apply(strings.NewReader("HELLO=universe")) +fmt.Println(os.Getenv("HELLO")) +// Output: "world" + +// NOTE: using OverApply existing value will be overridden +gotenv.OverApply(strings.NewReader("HELLO=universe")) +fmt.Println(os.Getenv("HELLO")) +// Output: "universe" +``` + +### Throw a Panic + +Both `gotenv.Load` and `gotenv.OverLoad` returns an error on something wrong occurred, like your env file is not exist, and so on. To make it easier to use, `gotenv` also provides `gotenv.Must` helper, to let it panic when an error returned. + +```go +err := gotenv.Load(".env-is-not-exist") +fmt.Println("error", err) +// error: open .env-is-not-exist: no such file or directory + +gotenv.Must(gotenv.Load, ".env-is-not-exist") +// it will throw a panic +// panic: open .env-is-not-exist: no such file or directory +``` + +### Another Scenario + +Just in case you want to parse environment variables from any `io.Reader`, gotenv keeps its `Parse` and `StrictParse` function as public API so you can use that. + +```go +// import "strings" + +pairs := gotenv.Parse(strings.NewReader("FOO=test\nBAR=$FOO")) +// gotenv.Env{"FOO": "test", "BAR": "test"} + +err, pairs = gotenv.StrictParse(strings.NewReader(`FOO="bar"`)) +// gotenv.Env{"FOO": "bar"} +``` + +`Parse` ignores invalid lines and returns `Env` of valid environment variables, while `StrictParse` returns an error for invalid lines. + +## Notes + +The gotenv package is a Go port of [`dotenv`](https://github.com/bkeepers/dotenv) project with some additions made for Go. For general features, it aims to be compatible as close as possible. diff --git a/vendor/github.com/subosito/gotenv/appveyor.yml b/vendor/github.com/subosito/gotenv/appveyor.yml new file mode 100644 index 00000000..33b4c404 --- /dev/null +++ b/vendor/github.com/subosito/gotenv/appveyor.yml @@ -0,0 +1,9 @@ +build: off +clone_folder: c:\gopath\src\github.com\subosito\gotenv +environment: + GOPATH: c:\gopath +stack: go 1.10 +before_test: + - go get -t +test_script: + - go test -v -cover -race diff --git a/vendor/github.com/subosito/gotenv/gotenv.go b/vendor/github.com/subosito/gotenv/gotenv.go new file mode 100644 index 00000000..745a3448 --- /dev/null +++ b/vendor/github.com/subosito/gotenv/gotenv.go @@ -0,0 +1,265 @@ +// Package gotenv provides functionality to dynamically load the environment variables +package gotenv + +import ( + "bufio" + "fmt" + "io" + "os" + "regexp" + "strings" +) + +const ( + // Pattern for detecting valid line format + linePattern = `\A\s*(?:export\s+)?([\w\.]+)(?:\s*=\s*|:\s+?)('(?:\'|[^'])*'|"(?:\"|[^"])*"|[^#\n]+)?\s*(?:\s*\#.*)?\z` + + // Pattern for detecting valid variable within a value + variablePattern = `(\\)?(\$)(\{?([A-Z0-9_]+)?\}?)` +) + +// Env holds key/value pair of valid environment variable +type Env map[string]string + +/* +Load is a function to load a file or multiple files and then export the valid variables into environment variables if they do not exist. +When it's called with no argument, it will load `.env` file on the current path and set the environment variables. +Otherwise, it will loop over the filenames parameter and set the proper environment variables. +*/ +func Load(filenames ...string) error { + return loadenv(false, filenames...) +} + +/* +OverLoad is a function to load a file or multiple files and then export and override the valid variables into environment variables. +*/ +func OverLoad(filenames ...string) error { + return loadenv(true, filenames...) +} + +/* +Must is wrapper function that will panic when supplied function returns an error. +*/ +func Must(fn func(filenames ...string) error, filenames ...string) { + if err := fn(filenames...); err != nil { + panic(err.Error()) + } +} + +/* +Apply is a function to load an io Reader then export the valid variables into environment variables if they do not exist. +*/ +func Apply(r io.Reader) error { + return parset(r, false) +} + +/* +OverApply is a function to load an io Reader then export and override the valid variables into environment variables. +*/ +func OverApply(r io.Reader) error { + return parset(r, true) +} + +func loadenv(override bool, filenames ...string) error { + if len(filenames) == 0 { + filenames = []string{".env"} + } + + for _, filename := range filenames { + f, err := os.Open(filename) + if err != nil { + return err + } + + err = parset(f, override) + if err != nil { + return err + } + + f.Close() + } + + return nil +} + +// parse and set :) +func parset(r io.Reader, override bool) error { + env, err := StrictParse(r) + if err != nil { + return err + } + + for key, val := range env { + setenv(key, val, override) + } + + return nil +} + +func setenv(key, val string, override bool) { + if override { + os.Setenv(key, val) + } else { + if _, present := os.LookupEnv(key); !present { + os.Setenv(key, val) + } + } +} + +// Parse is a function to parse line by line any io.Reader supplied and returns the valid Env key/value pair of valid variables. +// It expands the value of a variable from the environment variable but does not set the value to the environment itself. +// This function is skipping any invalid lines and only processing the valid one. +func Parse(r io.Reader) Env { + env, _ := StrictParse(r) + return env +} + +// StrictParse is a function to parse line by line any io.Reader supplied and returns the valid Env key/value pair of valid variables. +// It expands the value of a variable from the environment variable but does not set the value to the environment itself. +// This function is returning an error if there are any invalid lines. +func StrictParse(r io.Reader) (Env, error) { + env := make(Env) + scanner := bufio.NewScanner(r) + + i := 1 + bom := string([]byte{239, 187, 191}) + + for scanner.Scan() { + line := scanner.Text() + + if i == 1 { + line = strings.TrimPrefix(line, bom) + } + + i++ + + err := parseLine(line, env) + if err != nil { + return env, err + } + } + + return env, nil +} + +func parseLine(s string, env Env) error { + rl := regexp.MustCompile(linePattern) + rm := rl.FindStringSubmatch(s) + + if len(rm) == 0 { + return checkFormat(s, env) + } + + key := rm[1] + val := rm[2] + + // determine if string has quote prefix + hdq := strings.HasPrefix(val, `"`) + + // determine if string has single quote prefix + hsq := strings.HasPrefix(val, `'`) + + // trim whitespace + val = strings.Trim(val, " ") + + // remove quotes '' or "" + rq := regexp.MustCompile(`\A(['"])(.*)(['"])\z`) + val = rq.ReplaceAllString(val, "$2") + + if hdq { + val = strings.Replace(val, `\n`, "\n", -1) + val = strings.Replace(val, `\r`, "\r", -1) + + // Unescape all characters except $ so variables can be escaped properly + re := regexp.MustCompile(`\\([^$])`) + val = re.ReplaceAllString(val, "$1") + } + + rv := regexp.MustCompile(variablePattern) + fv := func(s string) string { + return varReplacement(s, hsq, env) + } + + val = rv.ReplaceAllStringFunc(val, fv) + val = parseVal(val, env) + + env[key] = val + return nil +} + +func parseExport(st string, env Env) error { + if strings.HasPrefix(st, "export") { + vs := strings.SplitN(st, " ", 2) + + if len(vs) > 1 { + if _, ok := env[vs[1]]; !ok { + return fmt.Errorf("line `%s` has an unset variable", st) + } + } + } + + return nil +} + +func varReplacement(s string, hsq bool, env Env) string { + if strings.HasPrefix(s, "\\") { + return strings.TrimPrefix(s, "\\") + } + + if hsq { + return s + } + + sn := `(\$)(\{?([A-Z0-9_]+)\}?)` + rn := regexp.MustCompile(sn) + mn := rn.FindStringSubmatch(s) + + if len(mn) == 0 { + return s + } + + v := mn[3] + + replace, ok := env[v] + if !ok { + replace = os.Getenv(v) + } + + return replace +} + +func checkFormat(s string, env Env) error { + st := strings.TrimSpace(s) + + if (st == "") || strings.HasPrefix(st, "#") { + return nil + } + + if err := parseExport(st, env); err != nil { + return err + } + + return fmt.Errorf("line `%s` doesn't match format", s) +} + +func parseVal(val string, env Env) string { + if strings.Contains(val, "=") { + if !(val == "\n" || val == "\r") { + kv := strings.Split(val, "\n") + + if len(kv) == 1 { + kv = strings.Split(val, "\r") + } + + if len(kv) > 1 { + val = kv[0] + + for i := 1; i < len(kv); i++ { + parseLine(kv[i], env) + } + } + } + } + + return val +} diff --git a/vendor/github.com/tdewolff/minify/v2/.goreleaser.yml b/vendor/github.com/tdewolff/minify/v2/.goreleaser.yml index 4edcdc20..32de82f4 100644 --- a/vendor/github.com/tdewolff/minify/v2/.goreleaser.yml +++ b/vendor/github.com/tdewolff/minify/v2/.goreleaser.yml @@ -1,7 +1,7 @@ builds: - binary: minify main: ./cmd/minify/ - ldflags: -s -w -X main.Version={{.Version}} -X main.Commit={{.Commit}} -X main.Date={{.Date}} + ldflags: -s -w -X main.Version={{.Version}} env: - CGO_ENABLED=0 - GO111MODULE=on diff --git a/vendor/github.com/tdewolff/minify/v2/.travis.yml b/vendor/github.com/tdewolff/minify/v2/.travis.yml index 9fd1c892..e08fe27a 100644 --- a/vendor/github.com/tdewolff/minify/v2/.travis.yml +++ b/vendor/github.com/tdewolff/minify/v2/.travis.yml @@ -1,6 +1,10 @@ language: go +go: + - 1.13.x +env: + - GO111MODULE=on before_install: - go get github.com/mattn/goveralls script: - - go test -v -covermode=count -coverprofile=profile.cov . ./css ./html ./js ./json ./svg ./xml - - goveralls -v -coverprofile=profile.cov -service travis-ci -repotoken $COVERALLS_TOKEN + - go test -covermode=count -coverprofile=profile.cov . ./css ./html ./js ./json ./svg ./xml + - goveralls -coverprofile=profile.cov -service travis-ci diff --git a/vendor/github.com/tdewolff/minify/v2/README.md b/vendor/github.com/tdewolff/minify/v2/README.md index d15c24b1..091e8d21 100644 --- a/vendor/github.com/tdewolff/minify/v2/README.md +++ b/vendor/github.com/tdewolff/minify/v2/README.md @@ -1,20 +1,14 @@ -# Minify [![Build Status](https://travis-ci.org/tdewolff/minify.svg?branch=master)](https://travis-ci.org/tdewolff/minify) [![GoDoc](http://godoc.org/github.com/tdewolff/minify?status.svg)](http://godoc.org/github.com/tdewolff/minify) [![Coverage Status](https://coveralls.io/repos/github/tdewolff/minify/badge.svg?branch=master)](https://coveralls.io/github/tdewolff/minify?branch=master) [![Join the chat at https://gitter.im/tdewolff/minify](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tdewolff/minify?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -***BE AWARE: YOU NEED GO 1.9.7+, 1.10.3+, 1.11 to run the latest release!!!*** - -If you cannot upgrade Go, please pin to **minify@v2.3.6** and **parse@v2.3.4** - ---- +# Minify [![Build Status](https://travis-ci.org/tdewolff/minify.svg?branch=master)](https://travis-ci.org/tdewolff/minify) [![GoDoc](http://godoc.org/github.com/tdewolff/minify?status.svg)](http://godoc.org/github.com/tdewolff/minify) [![Coverage Status](https://coveralls.io/repos/github/tdewolff/minify/badge.svg?branch=master)](https://coveralls.io/github/tdewolff/minify?branch=master) **[Online demo](https://go.tacodewolff.nl/minify) if you need to minify files *now*.** **[Command line tool](https://github.com/tdewolff/minify/tree/master/cmd/minify) that minifies concurrently and supports watching file changes.** -**[All releases](https://github.com/tdewolff/minify/releases) for various platforms.** +**[Releases](https://github.com/tdewolff/minify/releases) of CLI for various platforms.** See [CLI](https://github.com/tdewolff/minify/tree/master/cmd/minify) for more installation instructions. --- -*Did you know that the shortest valid piece of HTML5 is `x`? See for yourself at the [W3C Validator](http://validator.w3.org/)!* +*Did you know that the shortest valid piece of HTML5 is `x`? See for yourself at the [W3C Validator](http://validator.w3.org/)!* Minify is a minifier package written in [Go][1]. It provides HTML5, CSS3, JS, JSON, SVG and XML minifiers and an interface to implement any other minifier. Minification is the process of removing bytes from a file (such as whitespace) without changing its output and therefore shrinking its size and speeding up transmission over the internet and possibly parsing. The implemented minifiers are designed for high performance. @@ -47,6 +41,10 @@ The core functionality associates mimetypes with minification functions, allowin - [Mediatypes](#mediatypes) - [Examples](#examples) - [Common minifiers](#common-minifiers) + - [External minifiers](#external-minifiers) + - [Closure Compiler](#closure-compiler) + - [UglifyJS](#uglifyjs) + - [esbuild](#esbuild) - [Custom minifier](#custom-minifier-example) - [ResponseWriter](#responsewriter) - [Templates](#templates) @@ -76,11 +74,7 @@ Minifiers or bindings to minifiers exist in almost all programming languages. So This minifier proves to be that fast and extensive minifier that can handle HTML and any other filetype it may contain (CSS, JS, ...). It is usually orders of magnitude faster than existing minifiers. ## Installation -Run the following command - - go get -u github.com/tdewolff/minify/v2 - -or add the following imports and run the project with `go get` +With modules enabled (`GO111MODULES=auto` or `GO111MODULES=on`), add the following imports and run the project with `go get` ``` go import ( "github.com/tdewolff/minify/v2" @@ -93,6 +87,8 @@ import ( ) ``` +See [CLI tool](https://github.com/tdewolff/minify/tree/master/cmd/minify) for installation instructions of the binary. + ## API stability There is no guarantee for absolute stability, but I take issues and bugs seriously and don't take API changes lightly. The library will be maintained in a compatible way unless vital bugs prevent me from doing so. There has been one API change after v1 which added options support and I took the opportunity to push through some more API clean up as well. There are no plans whatsoever for future API changes. @@ -175,6 +171,7 @@ Options: - `KeepDefaultAttrVals` preserve default attribute values such as `