Updated dependencies and added avct/uasurfer (#8089)
* Updated dependencies and added avct/uasurfer * Added uasurfer to NOTICE.txt
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
f5c8a71698
Коммит
2fa7c464f0
204
NOTICE.txt
204
NOTICE.txt
@@ -2933,3 +2933,207 @@ 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.
|
||||
|
||||
---
|
||||
|
||||
## uasurfer
|
||||
|
||||
This product contains a modified portion of `uasurfer`, a Golang library for parsing User-Agent strings.
|
||||
|
||||
* HOMEPAGE
|
||||
* https://github.com/avct/uasurfer
|
||||
|
||||
* LICENSE
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright 2015 Avocet Systems Ltd.
|
||||
http://avocet.io/opensource
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
Copyright 2015 Avocet Systems Ltd.
|
||||
|
||||
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.
|
||||
|
||||
68
glide.lock
сгенерированный
68
glide.lock
сгенерированный
@@ -5,7 +5,9 @@ imports:
|
||||
version: 3fbce08846379ec7f4f6bc7fce6dd01ce28fae4c
|
||||
repo: https://github.com/mattermost/log4go.git
|
||||
- name: github.com/armon/go-metrics
|
||||
version: 9a4b6e10bed6220a1665955aa2b75afc91eb10b3
|
||||
version: 7aa49fde808223f8dadfdbfd3a20ff6c19e5f9ec
|
||||
- name: github.com/avct/uasurfer
|
||||
version: 7b6f7205267b5b81d20da3f09ec3dea3b3d90cf8
|
||||
- name: github.com/beorn7/perks
|
||||
version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
||||
subpackages:
|
||||
@@ -19,7 +21,7 @@ imports:
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/dgryski/dgoogauth
|
||||
version: 96977cbd42e27be71f9f731db6634123de7e861a
|
||||
version: fd153d5969d30bd701e10f3530228f001213047c
|
||||
- name: github.com/dimchansky/utfbom
|
||||
version: 6c6132ff69f0f6c088739067407b5d32c52e1d0f
|
||||
- name: github.com/disintegration/imaging
|
||||
@@ -31,7 +33,7 @@ imports:
|
||||
- name: github.com/fsnotify/fsnotify
|
||||
version: 629574ca2a5df945712d3079857300b5e4da0236
|
||||
- name: github.com/go-ini/ini
|
||||
version: f280b3ba517bf5fc98922624f21fb0e7a92adaec
|
||||
version: 32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a
|
||||
- name: github.com/go-ldap/ldap
|
||||
version: 8168ee085ee43257585e50c6441aadf54ecb2c9f
|
||||
- name: github.com/go-redis/redis
|
||||
@@ -50,7 +52,7 @@ imports:
|
||||
- raster
|
||||
- truetype
|
||||
- name: github.com/golang/protobuf
|
||||
version: 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9
|
||||
version: 1e59b77b52bf8e4b449a57e6f79f21226d571845
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/gorilla/context
|
||||
@@ -70,7 +72,7 @@ imports:
|
||||
subpackages:
|
||||
- codec
|
||||
- name: github.com/hashicorp/go-multierror
|
||||
version: 83588e72410abfbe4df460eeb6f30841ae47d4c4
|
||||
version: b7773ae218740a7be65057fc60b366a49b538a44
|
||||
- name: github.com/hashicorp/go-sockaddr
|
||||
version: 9b4c5fa5b10a683339a270d664474b9f4aee62fc
|
||||
- name: github.com/hashicorp/golang-lru
|
||||
@@ -82,6 +84,7 @@ imports:
|
||||
subpackages:
|
||||
- hcl/ast
|
||||
- hcl/parser
|
||||
- hcl/printer
|
||||
- hcl/scanner
|
||||
- hcl/strconv
|
||||
- hcl/token
|
||||
@@ -89,15 +92,19 @@ imports:
|
||||
- json/scanner
|
||||
- json/token
|
||||
- name: github.com/hashicorp/memberlist
|
||||
<<<<<<< HEAD
|
||||
version: caa5d20d6a642b7543b3745e54031a96008bee57
|
||||
- name: github.com/icrowley/fake
|
||||
version: e64cc2cf92049a299f359734c6ea76073f2a8b2c
|
||||
=======
|
||||
version: 3d8438da9589e7b608a83ffac1ef8211486bcb7c
|
||||
>>>>>>> Updated dependencies and added avct/uasurfer
|
||||
- name: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- name: github.com/jehiah/go-strftime
|
||||
version: 834e15c05a45371503440cc195bbd05c9a0968d9
|
||||
- name: github.com/lib/pq
|
||||
version: b609790bd85edf8e9ab7e0f8912750a786177bcf
|
||||
version: 83612a56d3dd153a94a629cd64925371c9adad78
|
||||
subpackages:
|
||||
- oid
|
||||
- name: github.com/magiconair/properties
|
||||
@@ -117,9 +124,7 @@ imports:
|
||||
subpackages:
|
||||
- pbutil
|
||||
- name: github.com/miekg/dns
|
||||
version: 9fc4eb252eedf0ef8adc05169ce35da5e31beaba
|
||||
subpackages:
|
||||
- internal/socket
|
||||
version: f5ac34d755eb6975958108413c7802a782ca16c8
|
||||
- name: github.com/minio/go-homedir
|
||||
version: 4d76aabb80b22bad8695d3904e943f1fb5e6199f
|
||||
- name: github.com/minio/minio-go
|
||||
@@ -134,7 +139,7 @@ imports:
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
version: 06020f85339e21b2478f756a78e295255ffa4d6a
|
||||
- name: github.com/mssola/user_agent
|
||||
version: a2f39d5a9b15ecc1fa1005b6aae73cd83da240ef
|
||||
version: 5243daae23628aeae9b6268541406bd5e95d5964
|
||||
- name: github.com/nicksnyder/go-i18n
|
||||
version: 0dc1626d56435e9d605a29875701721c54bc9bbd
|
||||
subpackages:
|
||||
@@ -143,13 +148,13 @@ imports:
|
||||
- i18n/language
|
||||
- i18n/translation
|
||||
- name: github.com/NYTimes/gziphandler
|
||||
version: 843330b8a375e6460183b08e030190b1a1eaea13
|
||||
version: 47ca22a0aeea4c9ceddfb935d818d636d934c312
|
||||
- name: github.com/pborman/uuid
|
||||
version: e790cca94e6cc75c7064b1332e63811d4aae1a53
|
||||
- name: github.com/pelletier/go-toml
|
||||
version: 4e9e0ee19b60b13eb79915933f44d8ed5f268bdd
|
||||
version: 0131db6d737cfbbfb678f8b7d92e55e27ce46224
|
||||
- name: github.com/pkg/errors
|
||||
version: f15c970de5b76fac0b59abb32d62c17cc7bed265
|
||||
version: e881fd58d78e04cf6d0de1217f8707c8cc2249bc
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
||||
subpackages:
|
||||
@@ -159,19 +164,19 @@ imports:
|
||||
subpackages:
|
||||
- prometheus
|
||||
- name: github.com/prometheus/client_model
|
||||
version: 6f3806018612930941127f2a7c6c453ba2c527d2
|
||||
version: 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
|
||||
subpackages:
|
||||
- go
|
||||
- name: github.com/prometheus/common
|
||||
version: e3fb1a1acd7605367a2b378bc2e2f893c05174b7
|
||||
version: 89604d197083d4781071d3c65855d24ecfb0a563
|
||||
subpackages:
|
||||
- expfmt
|
||||
- name: github.com/prometheus/procfs
|
||||
version: a6e9df898b1336106c743392c48ee0b71f5c4efa
|
||||
version: b15cd069a83443be3154b719d0cc9fe8117f09fb
|
||||
- name: github.com/rsc/letsencrypt
|
||||
version: 33926faef6d434b854ea994228f11d0185faa0c1
|
||||
- name: github.com/rwcarlsen/goexif
|
||||
version: 709fab3d192d7c62f86043caff1e7e3fb0f42bd8
|
||||
version: 17202558c8d9c3fd047859f1a5e73fd9ae709187
|
||||
subpackages:
|
||||
- exif
|
||||
- tiff
|
||||
@@ -182,19 +187,19 @@ imports:
|
||||
- name: github.com/segmentio/backo-go
|
||||
version: 204274ad699c0983a70203a566887f17a717fef4
|
||||
- name: github.com/spf13/afero
|
||||
version: 5660eeed305fe5f69c8fc6cf899132a459a97064
|
||||
version: 57afd63c68602b63ed976de00dd066ccb3c319db
|
||||
subpackages:
|
||||
- mem
|
||||
- name: github.com/spf13/cast
|
||||
version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
|
||||
- name: github.com/spf13/cobra
|
||||
version: 2da4a54c5ceefcee7ca5dd0eea1e18a3b6366489
|
||||
version: b95ab734e27d33e0d8fbabf71ca990568d4e2020
|
||||
- name: github.com/spf13/jwalterweatherman
|
||||
version: 12bd96e66386c1960ab0f74ced1362f66f552f7b
|
||||
version: 7c0cea34c8ece3fbeb2b27ab9b59511d360fb394
|
||||
- name: github.com/spf13/pflag
|
||||
version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
|
||||
- name: github.com/spf13/viper
|
||||
version: 4dddf7c62e16bce5807744018f5b753bfe21bbd2
|
||||
version: aafc9e6bc7b7bb53ddaa75a5ef49a17d6e654be5
|
||||
- name: github.com/stretchr/objx
|
||||
version: cbeaeb16a013161a98496fad62933b1d21786672
|
||||
- name: github.com/stretchr/testify
|
||||
@@ -207,19 +212,21 @@ imports:
|
||||
- name: github.com/tylerb/graceful
|
||||
version: 4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb
|
||||
- name: github.com/xenolf/lego
|
||||
version: aa94fb4696349fd6f35168dabe3ab030ddf3e484
|
||||
version: b929aa5aab5ad2e197bb3d74ef99fac61bfa47bc
|
||||
subpackages:
|
||||
- acme
|
||||
- name: github.com/xtgo/uuid
|
||||
version: a0b114877d4caeffbd7f87e3757c17fce570fea7
|
||||
- name: golang.org/x/crypto
|
||||
version: 6a293f2d4b14b8e6d3f0539e383f6d0d30fce3fd
|
||||
version: b3c9a1d25cfbbbab0ff4780b71c4f54e6e92a0de
|
||||
subpackages:
|
||||
- bcrypt
|
||||
- blowfish
|
||||
- ed25519
|
||||
- ed25519/internal/edwards25519
|
||||
- ocsp
|
||||
- name: golang.org/x/image
|
||||
version: f7e31b4ea2e3413ab91b4e7d2dc83e5f8d19a44c
|
||||
version: 12117c17ca67ffa1ce22e9409f3b0b0a93ac08c7
|
||||
subpackages:
|
||||
- bmp
|
||||
- font
|
||||
@@ -227,17 +234,22 @@ imports:
|
||||
- tiff
|
||||
- tiff/lzw
|
||||
- name: golang.org/x/net
|
||||
version: a337091b0525af65de94df2eb7e98bd9962dcbe2
|
||||
version: 434ec0c7fe3742c984919a691b2018a6e9694425
|
||||
subpackages:
|
||||
- bpf
|
||||
- context
|
||||
- html
|
||||
- html/atom
|
||||
- internal/iana
|
||||
- internal/socket
|
||||
- ipv4
|
||||
- ipv6
|
||||
- name: golang.org/x/sys
|
||||
version: 1e2299c37cc91a509f1b12369872d27be0ce98a6
|
||||
version: 810d7000345868fc619eb81f46307107118f4ae1
|
||||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/text
|
||||
version: 88f656faf3f37f690df1a32515b479415e1a6769
|
||||
version: e19ae1496984b1c655b8044a65c0300a3c878dd3
|
||||
subpackages:
|
||||
- transform
|
||||
- unicode/norm
|
||||
@@ -266,5 +278,5 @@ imports:
|
||||
subpackages:
|
||||
- store/memstore
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: eb3733d160e74a9c7e442f435eb3bea458e1d19f
|
||||
version: d670f9405373e636a5a2765eea47fac0c9bc91a4
|
||||
testImports: []
|
||||
|
||||
@@ -95,3 +95,4 @@ import:
|
||||
version: 2.0.0
|
||||
- package: github.com/mattermost/html2text
|
||||
- package: github.com/icrowley/fake
|
||||
- package: github.com/avct/uasurfer
|
||||
|
||||
35
vendor/github.com/NYTimes/gziphandler/gzip.go
сгенерированный
поставляемый
35
vendor/github.com/NYTimes/gziphandler/gzip.go
сгенерированный
поставляемый
@@ -82,7 +82,14 @@ type GzipResponseWriter struct {
|
||||
buf []byte // Holds the first part of the write before reaching the minSize or the end of the write.
|
||||
|
||||
contentTypes []string // Only compress if the response is one of these content-types. All are accepted if empty.
|
||||
flushed bool // Indicate if the stream was already flushed
|
||||
}
|
||||
|
||||
type GzipResponseWriterWithCloseNotify struct {
|
||||
*GzipResponseWriter
|
||||
}
|
||||
|
||||
func (w *GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
|
||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Write appends data to the gzip writer.
|
||||
@@ -170,8 +177,7 @@ func (w *GzipResponseWriter) init() {
|
||||
func (w *GzipResponseWriter) Close() error {
|
||||
if w.gw == nil {
|
||||
// Gzip not trigged yet, write out regular response.
|
||||
// WriteHeader only if it wasn't already wrote by a Flush
|
||||
if !w.flushed && w.code != 0 {
|
||||
if w.code != 0 {
|
||||
w.ResponseWriter.WriteHeader(w.code)
|
||||
}
|
||||
if w.buf != nil {
|
||||
@@ -194,16 +200,18 @@ func (w *GzipResponseWriter) Close() error {
|
||||
// http.ResponseWriter if it is an http.Flusher. This makes GzipResponseWriter
|
||||
// an http.Flusher.
|
||||
func (w *GzipResponseWriter) Flush() {
|
||||
if w.gw != nil {
|
||||
w.gw.Flush()
|
||||
if w.gw == nil {
|
||||
// Only flush once startGzip has been called.
|
||||
//
|
||||
// Flush is thus a no-op until the written body
|
||||
// exceeds minSize.
|
||||
return
|
||||
}
|
||||
|
||||
w.gw.Flush()
|
||||
|
||||
if fw, ok := w.ResponseWriter.(http.Flusher); ok {
|
||||
if !w.flushed && w.code != 0 {
|
||||
w.ResponseWriter.WriteHeader(w.code)
|
||||
}
|
||||
fw.Flush()
|
||||
w.flushed = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +272,6 @@ func GzipHandlerWithOpts(opts ...option) (func(http.Handler) http.Handler, error
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add(vary, acceptEncoding)
|
||||
|
||||
if acceptsGzip(r) {
|
||||
gw := &GzipResponseWriter{
|
||||
ResponseWriter: w,
|
||||
@@ -274,7 +281,13 @@ func GzipHandlerWithOpts(opts ...option) (func(http.Handler) http.Handler, error
|
||||
}
|
||||
defer gw.Close()
|
||||
|
||||
h.ServeHTTP(gw, r)
|
||||
if _, ok := w.(http.CloseNotifier); ok {
|
||||
gwcn := GzipResponseWriterWithCloseNotify{gw}
|
||||
h.ServeHTTP(gwcn, r)
|
||||
} else {
|
||||
h.ServeHTTP(gw, r)
|
||||
}
|
||||
|
||||
} else {
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
49
vendor/github.com/NYTimes/gziphandler/gzip_test.go
сгенерированный
поставляемый
49
vendor/github.com/NYTimes/gziphandler/gzip_test.go
сгенерированный
поставляемый
@@ -306,23 +306,58 @@ func TestStatusCodes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCodesFlushed(t *testing.T) {
|
||||
func TestFlushBeforeWrite(t *testing.T) {
|
||||
b := []byte(testBody)
|
||||
handler := GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
rw.(http.Flusher).Flush()
|
||||
rw.Write([]byte("Not found"))
|
||||
rw.Write(b)
|
||||
}))
|
||||
r := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
r.Header.Set(acceptEncoding, "gzip")
|
||||
r.Header.Set("Accept-Encoding", "gzip")
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
result := w.Result()
|
||||
if result.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("StatusCode should have been 404 but was %d", result.StatusCode)
|
||||
}
|
||||
res := w.Result()
|
||||
assert.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||
assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"))
|
||||
assert.NotEqual(t, b, w.Body.Bytes())
|
||||
}
|
||||
|
||||
func TestImplementCloseNotifier(t *testing.T) {
|
||||
GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request){
|
||||
_, ok := rw.(http.CloseNotifier)
|
||||
assert.True(t, ok, "response writer must implement http.CloseNotifier")
|
||||
})).ServeHTTP(&mockRWCloseNotify{}, &http.Request{})
|
||||
}
|
||||
|
||||
func TestNotImplementCloseNotifier(t *testing.T) {
|
||||
GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request){
|
||||
_, ok := rw.(http.CloseNotifier)
|
||||
assert.False(t, ok, "response writer must not implement http.CloseNotifier")
|
||||
})).ServeHTTP(httptest.NewRecorder(), &http.Request{})
|
||||
}
|
||||
|
||||
|
||||
type mockRWCloseNotify struct{}
|
||||
|
||||
func (m *mockRWCloseNotify) CloseNotify() <-chan bool {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockRWCloseNotify) Header() http.Header {
|
||||
return http.Header{}
|
||||
}
|
||||
|
||||
func (m *mockRWCloseNotify) Write([]byte) (int, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockRWCloseNotify) WriteHeader(int) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
|
||||
func TestIgnoreSubsequentWriteHeader(t *testing.T) {
|
||||
handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(500)
|
||||
|
||||
98
vendor/github.com/armon/go-metrics/prometheus/prometheus.go
сгенерированный
поставляемый
98
vendor/github.com/armon/go-metrics/prometheus/prometheus.go
сгенерированный
поставляемый
@@ -13,19 +13,91 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type PrometheusSink struct {
|
||||
mu sync.Mutex
|
||||
gauges map[string]prometheus.Gauge
|
||||
summaries map[string]prometheus.Summary
|
||||
counters map[string]prometheus.Counter
|
||||
var (
|
||||
// DefaultPrometheusOpts is the default set of options used when creating a
|
||||
// PrometheusSink.
|
||||
DefaultPrometheusOpts = PrometheusOpts{
|
||||
Expiration: 60 * time.Second,
|
||||
}
|
||||
)
|
||||
|
||||
// PrometheusOpts is used to configure the Prometheus Sink
|
||||
type PrometheusOpts struct {
|
||||
// Expiration is the duration a metric is valid for, after which it will be
|
||||
// untracked. If the value is zero, a metric is never expired.
|
||||
Expiration time.Duration
|
||||
}
|
||||
|
||||
type PrometheusSink struct {
|
||||
mu sync.Mutex
|
||||
gauges map[string]prometheus.Gauge
|
||||
summaries map[string]prometheus.Summary
|
||||
counters map[string]prometheus.Counter
|
||||
updates map[string]time.Time
|
||||
expiration time.Duration
|
||||
}
|
||||
|
||||
// NewPrometheusSink creates a new PrometheusSink using the default options.
|
||||
func NewPrometheusSink() (*PrometheusSink, error) {
|
||||
return &PrometheusSink{
|
||||
gauges: make(map[string]prometheus.Gauge),
|
||||
summaries: make(map[string]prometheus.Summary),
|
||||
counters: make(map[string]prometheus.Counter),
|
||||
}, nil
|
||||
return NewPrometheusSinkFrom(DefaultPrometheusOpts)
|
||||
}
|
||||
|
||||
// NewPrometheusSinkFrom creates a new PrometheusSink using the passed options.
|
||||
func NewPrometheusSinkFrom(opts PrometheusOpts) (*PrometheusSink, error) {
|
||||
sink := &PrometheusSink{
|
||||
gauges: make(map[string]prometheus.Gauge),
|
||||
summaries: make(map[string]prometheus.Summary),
|
||||
counters: make(map[string]prometheus.Counter),
|
||||
updates: make(map[string]time.Time),
|
||||
expiration: opts.Expiration,
|
||||
}
|
||||
|
||||
return sink, prometheus.Register(sink)
|
||||
}
|
||||
|
||||
// Describe is needed to meet the Collector interface.
|
||||
func (p *PrometheusSink) Describe(c chan<- *prometheus.Desc) {
|
||||
// We must emit some description otherwise an error is returned. This
|
||||
// description isn't shown to the user!
|
||||
prometheus.NewGauge(prometheus.GaugeOpts{Name: "Dummy", Help: "Dummy"}).Describe(c)
|
||||
}
|
||||
|
||||
// Collect meets the collection interface and allows us to enforce our expiration
|
||||
// logic to clean up ephemeral metrics if their value haven't been set for a
|
||||
// duration exceeding our allowed expiration time.
|
||||
func (p *PrometheusSink) Collect(c chan<- prometheus.Metric) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
expire := p.expiration != 0
|
||||
now := time.Now()
|
||||
for k, v := range p.gauges {
|
||||
last := p.updates[k]
|
||||
if expire && last.Add(p.expiration).Before(now) {
|
||||
delete(p.updates, k)
|
||||
delete(p.gauges, k)
|
||||
} else {
|
||||
v.Collect(c)
|
||||
}
|
||||
}
|
||||
for k, v := range p.summaries {
|
||||
last := p.updates[k]
|
||||
if expire && last.Add(p.expiration).Before(now) {
|
||||
delete(p.updates, k)
|
||||
delete(p.summaries, k)
|
||||
} else {
|
||||
v.Collect(c)
|
||||
}
|
||||
}
|
||||
for k, v := range p.counters {
|
||||
last := p.updates[k]
|
||||
if expire && last.Add(p.expiration).Before(now) {
|
||||
delete(p.updates, k)
|
||||
delete(p.counters, k)
|
||||
} else {
|
||||
v.Collect(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var forbiddenChars = regexp.MustCompile("[ .=\\-]")
|
||||
@@ -65,10 +137,10 @@ func (p *PrometheusSink) SetGaugeWithLabels(parts []string, val float32, labels
|
||||
Help: key,
|
||||
ConstLabels: prometheusLabels(labels),
|
||||
})
|
||||
prometheus.MustRegister(g)
|
||||
p.gauges[hash] = g
|
||||
}
|
||||
g.Set(float64(val))
|
||||
p.updates[hash] = time.Now()
|
||||
}
|
||||
|
||||
func (p *PrometheusSink) AddSample(parts []string, val float32) {
|
||||
@@ -87,10 +159,10 @@ func (p *PrometheusSink) AddSampleWithLabels(parts []string, val float32, labels
|
||||
MaxAge: 10 * time.Second,
|
||||
ConstLabels: prometheusLabels(labels),
|
||||
})
|
||||
prometheus.MustRegister(g)
|
||||
p.summaries[hash] = g
|
||||
}
|
||||
g.Observe(float64(val))
|
||||
p.updates[hash] = time.Now()
|
||||
}
|
||||
|
||||
// EmitKey is not implemented. Prometheus doesn’t offer a type for which an
|
||||
@@ -114,8 +186,8 @@ func (p *PrometheusSink) IncrCounterWithLabels(parts []string, val float32, labe
|
||||
Help: key,
|
||||
ConstLabels: prometheusLabels(labels),
|
||||
})
|
||||
prometheus.MustRegister(g)
|
||||
p.counters[hash] = g
|
||||
}
|
||||
g.Add(float64(val))
|
||||
p.updates[hash] = time.Now()
|
||||
}
|
||||
|
||||
2
vendor/github.com/armon/go-metrics/start.go
сгенерированный
поставляемый
2
vendor/github.com/armon/go-metrics/start.go
сгенерированный
поставляемый
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// Config is used to configure metrics settings
|
||||
type Config struct {
|
||||
ServiceName string // Prefixed with keys to seperate services
|
||||
ServiceName string // Prefixed with keys to separate services
|
||||
HostName string // Hostname to use. If not provided and EnableHostname, it will be os.Hostname
|
||||
EnableHostname bool // Enable prefixing gauge values with hostname
|
||||
EnableHostnameLabel bool // Enable adding hostname to labels
|
||||
|
||||
56
vendor/github.com/avct/uasurfer/.gitignore
сгенерированный
поставляемый
Обычный файл
56
vendor/github.com/avct/uasurfer/.gitignore
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,56 @@
|
||||
# Compiled bin #
|
||||
###################
|
||||
|
||||
|
||||
# Compiled source #
|
||||
###################
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Configuration Files #
|
||||
#######################
|
||||
*.cfg
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
logs
|
||||
coverage.html
|
||||
coverage.out
|
||||
|
||||
# Test Files #
|
||||
#######################
|
||||
*.test
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# go.rice generated files
|
||||
*.rice-box.go
|
||||
|
||||
# Dev Tools #
|
||||
######################
|
||||
.vagrant
|
||||
11
vendor/github.com/avct/uasurfer/.travis.yml
сгенерированный
поставляемый
Обычный файл
11
vendor/github.com/avct/uasurfer/.travis.yml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,11 @@
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.8.x
|
||||
- 1.7.x
|
||||
|
||||
script:
|
||||
- go test
|
||||
192
vendor/github.com/avct/uasurfer/LICENSE
сгенерированный
поставляемый
Обычный файл
192
vendor/github.com/avct/uasurfer/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,192 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright 2015 Avocet Systems Ltd.
|
||||
http://avocet.io/opensource
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
Copyright 2015 Avocet Systems Ltd.
|
||||
|
||||
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.
|
||||
169
vendor/github.com/avct/uasurfer/README.md
сгенерированный
поставляемый
Обычный файл
169
vendor/github.com/avct/uasurfer/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,169 @@
|
||||
[](https://travis-ci.org/avct/uasurfer) [](https://godoc.org/github.com/avct/uasurfer) [](https://goreportcard.com/report/github.com/avct/uasurfer)
|
||||
|
||||
# uasurfer
|
||||
|
||||

|
||||
|
||||
**User Agent Surfer** (uasurfer) is a lightweight Golang package that parses and abstracts [HTTP User-Agent strings](https://en.wikipedia.org/wiki/User_agent) with particular attention to device type.
|
||||
|
||||
The following information is returned by uasurfer from a raw HTTP User-Agent string:
|
||||
|
||||
| Name | Example | Coverage in 192,792 parses |
|
||||
|----------------|---------|--------------------------------|
|
||||
| Browser name | `chrome` | 99.85% |
|
||||
| Browser version | `53` | 99.17% |
|
||||
| Platform | `ipad` | 99.97% |
|
||||
| OS name | `ios` | 99.96% |
|
||||
| OS version | `10` | 98.81% |
|
||||
| Device type | `tablet` | 99.98% |
|
||||
|
||||
Layout engine, browser language, and other esoteric attributes are not parsed.
|
||||
|
||||
Coverage is estimated from a random sample of real UA strings collected across thousands of sources in US and EU mid-2016.
|
||||
|
||||
## Usage
|
||||
|
||||
### Parse(ua string) Function
|
||||
|
||||
The `Parse()` function accepts a user agent `string` and returns UserAgent struct with named constants and integers for versions (minor, major and patch separately), and the full UA string that was parsed (lowercase). A string can be retrieved by adding `.String()` to a variable, such as `uasurfer.BrowserName.String()`.
|
||||
|
||||
```
|
||||
// Define a user agent string
|
||||
myUA := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36"
|
||||
|
||||
// Parse() returns all attributes, including returning the full UA string last
|
||||
ua, uaString := uasurfer.Parse(myUA)
|
||||
```
|
||||
|
||||
where example UserAgent is:
|
||||
```
|
||||
{
|
||||
Browser {
|
||||
BrowserName: BrowserChrome,
|
||||
Version: {
|
||||
Major: 45,
|
||||
Minor: 0,
|
||||
Patch: 2454,
|
||||
},
|
||||
},
|
||||
OS {
|
||||
Platform: PlatformMac,
|
||||
Name: OSMacOSX,
|
||||
Version: {
|
||||
Major: 10,
|
||||
Minor: 10,
|
||||
Patch: 5,
|
||||
},
|
||||
},
|
||||
DeviceType: DeviceComputer,
|
||||
}
|
||||
```
|
||||
|
||||
**Usage note:** There are some OSes that do not return a version, see docs below. Linux is typically not reported with a specific Linux distro name or version.
|
||||
|
||||
#### Browser Name
|
||||
* `BrowserChrome` - Google [Chrome](https://en.wikipedia.org/wiki/Google_Chrome), [Chromium](https://en.wikipedia.org/wiki/Chromium_(web_browser))
|
||||
* `BrowserSafari` - Apple [Safari](https://en.wikipedia.org/wiki/Safari_(web_browser)), Google Search ([GSA](https://itunes.apple.com/us/app/google/id284815942))
|
||||
* `BrowserIE` - Microsoft [Internet Explorer](https://en.wikipedia.org/wiki/Internet_Explorer), [Edge](https://en.wikipedia.org/wiki/Microsoft_Edge)
|
||||
* `BrowserFirefox` - Mozilla [Firefox](https://en.wikipedia.org/wiki/Firefox), GNU [IceCat](https://en.wikipedia.org/wiki/GNU_IceCat), [Iceweasel](https://en.wikipedia.org/wiki/Mozilla_Corporation_software_rebranded_by_the_Debian_project#Iceweasel), [Seamonkey](https://en.wikipedia.org/wiki/SeaMonkey)
|
||||
* `BrowserAndroid` - Android [WebView](https://developer.chrome.com/multidevice/webview/overview) (Android OS <4.4 only)
|
||||
* `BrowserOpera` - [Opera](https://en.wikipedia.org/wiki/Opera_(web_browser))
|
||||
* `BrowserUCBrowser` - [UC Browser](https://en.wikipedia.org/wiki/UC_Browser)
|
||||
* `BrowserSilk` - Amazon [Silk](https://en.wikipedia.org/wiki/Amazon_Silk)
|
||||
* `BrowserSpotify` - [Spotify](https://en.wikipedia.org/wiki/Spotify#Clients) desktop client
|
||||
* `BrowserBlackberry` - RIM [BlackBerry](https://en.wikipedia.org/wiki/BlackBerry)
|
||||
* `BrowserUnknown` - Unknown
|
||||
|
||||
#### Browser Version
|
||||
|
||||
Browser version returns an `unint8` of the major version attribute of the User-Agent String. For example Chrome 45.0.23423 would return `45`. The intention is to support math operators with versions, such as "do XYZ for Chrome version >23".
|
||||
|
||||
Unknown version is returned as `0`.
|
||||
|
||||
#### Platform
|
||||
* `PlatformWindows` - Microsoft Windows
|
||||
* `PlatformMac` - Apple Macintosh
|
||||
* `PlatformLinux` - Linux, including Android and other OSes
|
||||
* `PlatformiPad` - Apple iPad
|
||||
* `PlatformiPhone` - Apple iPhone
|
||||
* `PlatformBlackberry` - RIM Blackberry
|
||||
* `PlatformWindowsPhone` Microsoft Windows Phone & Mobile
|
||||
* `PlatformKindle` - Amazon Kindle & Kindle Fire
|
||||
* `PlatformPlaystation` - Sony Playstation, Vita, PSP
|
||||
* `PlatformXbox` - Microsoft Xbox - `PlatformXbox`
|
||||
* `PlatformNintendo` - Nintendo DS, Wii, etc.
|
||||
* `PlatformUnknown` - Unknown
|
||||
|
||||
#### OS Name
|
||||
* `OSWindows`
|
||||
* `OSMacOSX` - includes "macOS Sierra"
|
||||
* `OSiOS`
|
||||
* `OSAndroid`
|
||||
* `OSChromeOS`
|
||||
* `OSWebOS`
|
||||
* `OSLinux`
|
||||
* `OSPlaystation`
|
||||
* `OSXbox`
|
||||
* `OSNintendo`
|
||||
* `OSUnknown`
|
||||
|
||||
#### OS Version
|
||||
|
||||
OS X major version is alway 10 with consecutive minor versions indicating release releases (10 - Yosemite, 11 - El Capitain, 12 Sierra, etc). Windows version is NT version. `Version{0, 0, 0}` indicated version is unknown or not evaluated.
|
||||
Versions can be compared using `Less` function: `if ver1.Less(ver2) {}`
|
||||
|
||||
Here are some examples across the platform, os.name, and os.version:
|
||||
|
||||
* For Windows XP (Windows NT 5.1), "`PlatformWindows`" is the platform, "`OSWindows`" is the name, and `{5, 1, 0}` the version.
|
||||
* For OS X 10.5.1, "`PlatformMac`" is the platform, "`OSMacOSX`" the name, and `{10, 5, 1}` the version.
|
||||
* For Android 5.1, "`PlatformLinux`" is the platform, "`OSAndroid`" is the name, and `{5, 1, 0}` the version.
|
||||
* For iOS 5.1, "`PlatformiPhone`" or "`PlatformiPad`" is the platform, "`OSiOS`" is the name, and `{5, 1, 0}` the version.
|
||||
|
||||
###### Windows Version Guide
|
||||
|
||||
* Windows 10 - `{10, 0, 0}`
|
||||
* Windows 8.1 - `{6, 3, 0}`
|
||||
* Windows 8 - `{6, 2, 0}`
|
||||
* Windows 7 - `{6, 1, 0}`
|
||||
* Windows Vista - `{6, 0, 0}`
|
||||
* Windows XP - `{5, 1, 0}` or `{5, 2, 0}`
|
||||
* Windows 2000 - `{5, 0, 0}`
|
||||
|
||||
Windows 95, 98, and ME represent 0.01% of traffic worldwide and are not available through this package at this time.
|
||||
|
||||
#### DeviceType
|
||||
DeviceType is typically quite accurate, though determining between phones and tablets on Android is not always possible due to how some vendors design their UA strings. A mobile Android device without tablet indicator defaults to being classified as a phone. DeviceTV supports major brands such as Philips, Sharp, Vizio and steaming boxes such as Apple, Google, Roku, Amazon.
|
||||
|
||||
* `DeviceComputer`
|
||||
* `DevicePhone`
|
||||
* `DeviceTablet`
|
||||
* `DeviceTV`
|
||||
* `DeviceConsole`
|
||||
* `DeviceWearable`
|
||||
* `DeviceUnknown`
|
||||
|
||||
## Example Combinations of Attributes
|
||||
* Surface RT -> `OSWindows8`, `DeviceTablet`, OSVersion >= `6`
|
||||
* Android Tablet -> `OSAndroid`, `DeviceTablet`
|
||||
* Microsoft Edge -> `BrowserIE`, BrowserVersion >= `12.0.0`
|
||||
|
||||
## To do
|
||||
|
||||
* Remove compiled regexp in favor of string.Contains wherever possible (lowers mem/alloc)
|
||||
* Better version support on Firefox derivatives (e.g. SeaMonkey)
|
||||
* Potential additional browser support:
|
||||
* "NetFront" (1% share in India)
|
||||
* "QQ Browser" (6.5% share in China)
|
||||
* "Sogou Explorer" (5% share in China)
|
||||
* "Maxthon" (1.5% share in China)
|
||||
* "Nokia"
|
||||
* Potential additional OS support:
|
||||
* "Nokia" (5% share in India)
|
||||
* "Series 40" (5.5% share in India)
|
||||
* Windows 2003 Server
|
||||
* iOS safari browser identification based on iOS version
|
||||
* Add android version to browser identification
|
||||
* old Macs
|
||||
* "opera/9.64 (macintosh; ppc mac os x; u; en) presto/2.1.1"
|
||||
* old Windows
|
||||
* "mozilla/5.0 (windows nt 4.0; wow64) applewebkit/537.36 (khtml, like gecko) chrome/37.0.2049.0 safari/537.36"
|
||||
192
vendor/github.com/avct/uasurfer/browser.go
сгенерированный
поставляемый
Обычный файл
192
vendor/github.com/avct/uasurfer/browser.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,192 @@
|
||||
package uasurfer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Browser struct contains the lowercase name of the browser, along
|
||||
// with its browser version number. Browser are grouped together without
|
||||
// consideration for device. For example, Chrome (Chrome/43.0) and Chrome for iOS
|
||||
// (CriOS/43.0) would both return as "chrome" (name) and 43.0 (version). Similarly
|
||||
// Internet Explorer 11 and Edge 12 would return as "ie" and "11" or "12", respectively.
|
||||
// type Browser struct {
|
||||
// Name BrowserName
|
||||
// Version struct {
|
||||
// Major int
|
||||
// Minor int
|
||||
// Patch int
|
||||
// }
|
||||
// }
|
||||
|
||||
// Retrieve browser name from UA strings
|
||||
func (u *UserAgent) evalBrowserName(ua string) bool {
|
||||
// Blackberry goes first because it reads as MSIE & Safari
|
||||
if strings.Contains(ua, "blackberry") || strings.Contains(ua, "playbook") || strings.Contains(ua, "bb10") || strings.Contains(ua, "rim ") {
|
||||
u.Browser.Name = BrowserBlackberry
|
||||
return u.isBot()
|
||||
}
|
||||
|
||||
if strings.Contains(ua, "applewebkit") {
|
||||
switch {
|
||||
case strings.Contains(ua, "opr/") || strings.Contains(ua, "opios/"):
|
||||
u.Browser.Name = BrowserOpera
|
||||
|
||||
case strings.Contains(ua, "silk/"):
|
||||
u.Browser.Name = BrowserSilk
|
||||
|
||||
case strings.Contains(ua, "edge/") || strings.Contains(ua, "iemobile/") || strings.Contains(ua, "msie "):
|
||||
u.Browser.Name = BrowserIE
|
||||
|
||||
case strings.Contains(ua, "ucbrowser/") || strings.Contains(ua, "ucweb/"):
|
||||
u.Browser.Name = BrowserUCBrowser
|
||||
|
||||
// Edge, Silk and other chrome-identifying browsers must evaluate before chrome, unless we want to add more overhead
|
||||
case strings.Contains(ua, "chrome/") || strings.Contains(ua, "crios/") || strings.Contains(ua, "chromium/") || strings.Contains(ua, "crmo/"):
|
||||
u.Browser.Name = BrowserChrome
|
||||
|
||||
case strings.Contains(ua, "android") && !strings.Contains(ua, "chrome/") && strings.Contains(ua, "version/") && !strings.Contains(ua, "like android"):
|
||||
// Android WebView on Android >= 4.4 is purposefully being identified as Chrome above -- https://developer.chrome.com/multidevice/webview/overview
|
||||
u.Browser.Name = BrowserAndroid
|
||||
|
||||
case strings.Contains(ua, "fxios"):
|
||||
u.Browser.Name = BrowserFirefox
|
||||
|
||||
case strings.Contains(ua, " spotify/"):
|
||||
u.Browser.Name = BrowserSpotify
|
||||
|
||||
// AppleBot uses webkit signature as well
|
||||
case strings.Contains(ua, "applebot"):
|
||||
u.Browser.Name = BrowserAppleBot
|
||||
|
||||
// presume it's safari unless an esoteric browser is being specified (webOSBrowser, SamsungBrowser, etc.)
|
||||
case strings.Contains(ua, "like gecko") && strings.Contains(ua, "mozilla/") && strings.Contains(ua, "safari/") && !strings.Contains(ua, "linux") && !strings.Contains(ua, "android") && !strings.Contains(ua, "browser/") && !strings.Contains(ua, "os/"):
|
||||
u.Browser.Name = BrowserSafari
|
||||
|
||||
// if we got this far and the device is iPhone or iPad, assume safari. Some agents don't actually contain the word "safari"
|
||||
case strings.Contains(ua, "iphone") || strings.Contains(ua, "ipad"):
|
||||
u.Browser.Name = BrowserSafari
|
||||
|
||||
// Google's search app on iPhone, leverages native Safari rather than Chrome
|
||||
case strings.Contains(ua, " gsa/"):
|
||||
u.Browser.Name = BrowserSafari
|
||||
|
||||
default:
|
||||
goto notwebkit
|
||||
|
||||
}
|
||||
return u.isBot()
|
||||
}
|
||||
|
||||
notwebkit:
|
||||
switch {
|
||||
case strings.Contains(ua, "msie") || strings.Contains(ua, "trident"):
|
||||
u.Browser.Name = BrowserIE
|
||||
|
||||
case strings.Contains(ua, "gecko") && (strings.Contains(ua, "firefox") || strings.Contains(ua, "iceweasel") || strings.Contains(ua, "seamonkey") || strings.Contains(ua, "icecat")):
|
||||
u.Browser.Name = BrowserFirefox
|
||||
|
||||
case strings.Contains(ua, "presto") || strings.Contains(ua, "opera"):
|
||||
u.Browser.Name = BrowserOpera
|
||||
|
||||
case strings.Contains(ua, "ucbrowser"):
|
||||
u.Browser.Name = BrowserUCBrowser
|
||||
|
||||
case strings.Contains(ua, "applebot"):
|
||||
u.Browser.Name = BrowserAppleBot
|
||||
|
||||
case strings.Contains(ua, "baiduspider"):
|
||||
u.Browser.Name = BrowserBaiduBot
|
||||
|
||||
case strings.Contains(ua, "adidxbot") || strings.Contains(ua, "bingbot") || strings.Contains(ua, "bingpreview"):
|
||||
u.Browser.Name = BrowserBingBot
|
||||
|
||||
case strings.Contains(ua, "duckduckbot"):
|
||||
u.Browser.Name = BrowserDuckDuckGoBot
|
||||
|
||||
case strings.Contains(ua, "facebot") || strings.Contains(ua, "facebookexternalhit"):
|
||||
u.Browser.Name = BrowserFacebookBot
|
||||
|
||||
case strings.Contains(ua, "googlebot"):
|
||||
u.Browser.Name = BrowserGoogleBot
|
||||
|
||||
case strings.Contains(ua, "linkedinbot"):
|
||||
u.Browser.Name = BrowserLinkedInBot
|
||||
|
||||
case strings.Contains(ua, "msnbot"):
|
||||
u.Browser.Name = BrowserMsnBot
|
||||
|
||||
case strings.Contains(ua, "pingdom.com_bot"):
|
||||
u.Browser.Name = BrowserPingdomBot
|
||||
|
||||
case strings.Contains(ua, "twitterbot"):
|
||||
u.Browser.Name = BrowserTwitterBot
|
||||
|
||||
case strings.Contains(ua, "yandex") || strings.Contains(ua, "yadirectfetcher"):
|
||||
u.Browser.Name = BrowserYandexBot
|
||||
|
||||
case strings.Contains(ua, "yahoo"):
|
||||
u.Browser.Name = BrowserYahooBot
|
||||
|
||||
case strings.Contains(ua, "phantomjs"):
|
||||
u.Browser.Name = BrowserBot
|
||||
|
||||
default:
|
||||
u.Browser.Name = BrowserUnknown
|
||||
|
||||
}
|
||||
|
||||
return u.isBot()
|
||||
}
|
||||
|
||||
// Retrieve browser version
|
||||
// Methods used in order:
|
||||
// 1st: look for generic version/#
|
||||
// 2nd: look for browser-specific instructions (e.g. chrome/34)
|
||||
// 3rd: infer from OS (iOS only)
|
||||
func (u *UserAgent) evalBrowserVersion(ua string) {
|
||||
// if there is a 'version/#' attribute with numeric version, use it -- except for Chrome since Android vendors sometimes hijack version/#
|
||||
if u.Browser.Name != BrowserChrome && u.Browser.Version.findVersionNumber(ua, "version/") {
|
||||
return
|
||||
}
|
||||
|
||||
switch u.Browser.Name {
|
||||
case BrowserChrome:
|
||||
// match both chrome and crios
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "chrome/") || u.Browser.Version.findVersionNumber(ua, "crios/") || u.Browser.Version.findVersionNumber(ua, "crmo/")
|
||||
|
||||
case BrowserIE:
|
||||
if u.Browser.Version.findVersionNumber(ua, "msie ") || u.Browser.Version.findVersionNumber(ua, "edge/") {
|
||||
return
|
||||
}
|
||||
|
||||
// get MSIE version from trident version https://en.wikipedia.org/wiki/Trident_(layout_engine)
|
||||
if u.Browser.Version.findVersionNumber(ua, "trident/") {
|
||||
// convert trident versions 3-7 to MSIE version
|
||||
if (u.Browser.Version.Major >= 3) && (u.Browser.Version.Major <= 7) {
|
||||
u.Browser.Version.Major += 4
|
||||
}
|
||||
}
|
||||
|
||||
case BrowserFirefox:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "firefox/") || u.Browser.Version.findVersionNumber(ua, "fxios/")
|
||||
|
||||
case BrowserSafari: // executes typically if we're on iOS and not using a familiar browser
|
||||
u.Browser.Version = u.OS.Version
|
||||
// early Safari used a version number +1 to OS version
|
||||
if (u.Browser.Version.Major <= 3) && (u.Browser.Version.Major >= 1) {
|
||||
u.Browser.Version.Major++
|
||||
}
|
||||
|
||||
case BrowserUCBrowser:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "ucbrowser/")
|
||||
|
||||
case BrowserOpera:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "opr/") || u.Browser.Version.findVersionNumber(ua, "opios/") || u.Browser.Version.findVersionNumber(ua, "opera/")
|
||||
|
||||
case BrowserSilk:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "silk/")
|
||||
|
||||
case BrowserSpotify:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "spotify/")
|
||||
}
|
||||
}
|
||||
49
vendor/github.com/avct/uasurfer/const_string.go
сгенерированный
поставляемый
Обычный файл
49
vendor/github.com/avct/uasurfer/const_string.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,49 @@
|
||||
// Code generated by "stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go"; DO NOT EDIT.
|
||||
|
||||
package uasurfer
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _DeviceType_name = "DeviceUnknownDeviceComputerDeviceTabletDevicePhoneDeviceConsoleDeviceWearableDeviceTV"
|
||||
|
||||
var _DeviceType_index = [...]uint8{0, 13, 27, 39, 50, 63, 77, 85}
|
||||
|
||||
func (i DeviceType) String() string {
|
||||
if i < 0 || i >= DeviceType(len(_DeviceType_index)-1) {
|
||||
return fmt.Sprintf("DeviceType(%d)", i)
|
||||
}
|
||||
return _DeviceType_name[_DeviceType_index[i]:_DeviceType_index[i+1]]
|
||||
}
|
||||
|
||||
const _BrowserName_name = "BrowserUnknownBrowserChromeBrowserIEBrowserSafariBrowserFirefoxBrowserAndroidBrowserOperaBrowserBlackberryBrowserUCBrowserBrowserSilkBrowserNokiaBrowserNetFrontBrowserQQBrowserMaxthonBrowserSogouExplorerBrowserSpotifyBrowserBotBrowserAppleBotBrowserBaiduBotBrowserBingBotBrowserDuckDuckGoBotBrowserFacebookBotBrowserGoogleBotBrowserLinkedInBotBrowserMsnBotBrowserPingdomBotBrowserTwitterBotBrowserYandexBotBrowserYahooBot"
|
||||
|
||||
var _BrowserName_index = [...]uint16{0, 14, 27, 36, 49, 63, 77, 89, 106, 122, 133, 145, 160, 169, 183, 203, 217, 227, 242, 257, 271, 291, 309, 325, 343, 356, 373, 390, 406, 421}
|
||||
|
||||
func (i BrowserName) String() string {
|
||||
if i < 0 || i >= BrowserName(len(_BrowserName_index)-1) {
|
||||
return fmt.Sprintf("BrowserName(%d)", i)
|
||||
}
|
||||
return _BrowserName_name[_BrowserName_index[i]:_BrowserName_index[i+1]]
|
||||
}
|
||||
|
||||
const _OSName_name = "OSUnknownOSWindowsPhoneOSWindowsOSMacOSXOSiOSOSAndroidOSBlackberryOSChromeOSOSKindleOSWebOSOSLinuxOSPlaystationOSXboxOSNintendoOSBot"
|
||||
|
||||
var _OSName_index = [...]uint8{0, 9, 23, 32, 40, 45, 54, 66, 76, 84, 91, 98, 111, 117, 127, 132}
|
||||
|
||||
func (i OSName) String() string {
|
||||
if i < 0 || i >= OSName(len(_OSName_index)-1) {
|
||||
return fmt.Sprintf("OSName(%d)", i)
|
||||
}
|
||||
return _OSName_name[_OSName_index[i]:_OSName_index[i+1]]
|
||||
}
|
||||
|
||||
const _Platform_name = "PlatformUnknownPlatformWindowsPlatformMacPlatformLinuxPlatformiPadPlatformiPhonePlatformiPodPlatformBlackberryPlatformWindowsPhonePlatformPlaystationPlatformXboxPlatformNintendoPlatformBot"
|
||||
|
||||
var _Platform_index = [...]uint8{0, 15, 30, 41, 54, 66, 80, 92, 110, 130, 149, 161, 177, 188}
|
||||
|
||||
func (i Platform) String() string {
|
||||
if i < 0 || i >= Platform(len(_Platform_index)-1) {
|
||||
return fmt.Sprintf("Platform(%d)", i)
|
||||
}
|
||||
return _Platform_name[_Platform_index[i]:_Platform_index[i+1]]
|
||||
}
|
||||
60
vendor/github.com/avct/uasurfer/device.go
сгенерированный
поставляемый
Обычный файл
60
vendor/github.com/avct/uasurfer/device.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,60 @@
|
||||
package uasurfer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (u *UserAgent) evalDevice(ua string) {
|
||||
switch {
|
||||
|
||||
case u.OS.Platform == PlatformWindows || u.OS.Platform == PlatformMac || u.OS.Name == OSChromeOS:
|
||||
if strings.Contains(ua, "mobile") || strings.Contains(ua, "touch") {
|
||||
u.DeviceType = DeviceTablet // windows rt, linux haxor tablets
|
||||
return
|
||||
}
|
||||
u.DeviceType = DeviceComputer
|
||||
|
||||
case u.OS.Platform == PlatformiPad || u.OS.Platform == PlatformiPod || strings.Contains(ua, "tablet") || strings.Contains(ua, "kindle/") || strings.Contains(ua, "playbook"):
|
||||
u.DeviceType = DeviceTablet
|
||||
|
||||
case u.OS.Platform == PlatformiPhone || u.OS.Platform == PlatformBlackberry || strings.Contains(ua, "phone"):
|
||||
u.DeviceType = DevicePhone
|
||||
|
||||
// long list of smarttv and tv dongle identifiers
|
||||
case strings.Contains(ua, "tv") || strings.Contains(ua, "crkey") || strings.Contains(ua, "googletv") || strings.Contains(ua, "aftb") || strings.Contains(ua, "adt-") || strings.Contains(ua, "roku") || strings.Contains(ua, "viera") || strings.Contains(ua, "aquos") || strings.Contains(ua, "dtv") || strings.Contains(ua, "appletv") || strings.Contains(ua, "smarttv") || strings.Contains(ua, "tuner") || strings.Contains(ua, "smart-tv") || strings.Contains(ua, "hbbtv") || strings.Contains(ua, "netcast") || strings.Contains(ua, "vizio"):
|
||||
u.DeviceType = DeviceTV
|
||||
|
||||
case u.OS.Name == OSAndroid:
|
||||
// android phones report as "mobile", android tablets should not but often do -- http://android-developers.blogspot.com/2010/12/android-browser-user-agent-issues.html
|
||||
if strings.Contains(ua, "mobile") {
|
||||
u.DeviceType = DevicePhone
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(ua, "tablet") || strings.Contains(ua, "nexus 7") || strings.Contains(ua, "nexus 9") || strings.Contains(ua, "nexus 10") || strings.Contains(ua, "xoom") {
|
||||
u.DeviceType = DeviceTablet
|
||||
return
|
||||
}
|
||||
|
||||
u.DeviceType = DevicePhone // default to phone
|
||||
|
||||
case u.OS.Platform == PlatformPlaystation || u.OS.Platform == PlatformXbox || u.OS.Platform == PlatformNintendo:
|
||||
u.DeviceType = DeviceConsole
|
||||
|
||||
case strings.Contains(ua, "glass") || strings.Contains(ua, "watch") || strings.Contains(ua, "sm-v"):
|
||||
u.DeviceType = DeviceWearable
|
||||
|
||||
// specifically above "mobile" string check as Kindle Fire tablets report as "mobile"
|
||||
case u.Browser.Name == BrowserSilk || u.OS.Name == OSKindle && !strings.Contains(ua, "sd4930ur"):
|
||||
u.DeviceType = DeviceTablet
|
||||
|
||||
case strings.Contains(ua, "mobile") || strings.Contains(ua, "touch") || strings.Contains(ua, " mobi") || strings.Contains(ua, "webos"): //anything "mobile"/"touch" that didn't get captured as tablet, console or wearable is presumed a phone
|
||||
u.DeviceType = DevicePhone
|
||||
|
||||
case u.OS.Name == OSLinux: // linux goes last since it's in so many other device types (tvs, wearables, android-based stuff)
|
||||
u.DeviceType = DeviceComputer
|
||||
|
||||
default:
|
||||
u.DeviceType = DeviceUnknown
|
||||
}
|
||||
}
|
||||
332
vendor/github.com/avct/uasurfer/system.go
сгенерированный
поставляемый
Обычный файл
332
vendor/github.com/avct/uasurfer/system.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,332 @@
|
||||
package uasurfer
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
amazonFireFingerprint = regexp.MustCompile("\\s(k[a-z]{3,5}|sd\\d{4}ur)\\s") //tablet or phone
|
||||
)
|
||||
|
||||
func (u *UserAgent) evalOS(ua string) bool {
|
||||
|
||||
s := strings.IndexRune(ua, '(')
|
||||
e := strings.IndexRune(ua, ')')
|
||||
if s > e {
|
||||
s = 0
|
||||
e = len(ua)
|
||||
}
|
||||
if e == -1 {
|
||||
e = len(ua)
|
||||
}
|
||||
|
||||
agentPlatform := ua[s+1 : e]
|
||||
specsEnd := strings.Index(agentPlatform, ";")
|
||||
var specs string
|
||||
if specsEnd != -1 {
|
||||
specs = agentPlatform[:specsEnd]
|
||||
} else {
|
||||
specs = agentPlatform
|
||||
}
|
||||
|
||||
//strict OS & version identification
|
||||
switch specs {
|
||||
case "android":
|
||||
u.evalLinux(ua, agentPlatform)
|
||||
|
||||
case "bb10", "playbook":
|
||||
u.OS.Platform = PlatformBlackberry
|
||||
u.OS.Name = OSBlackberry
|
||||
|
||||
case "x11", "linux":
|
||||
u.evalLinux(ua, agentPlatform)
|
||||
|
||||
case "ipad", "iphone", "ipod touch", "ipod":
|
||||
u.evaliOS(specs, agentPlatform)
|
||||
|
||||
case "macintosh":
|
||||
u.evalMacintosh(ua)
|
||||
|
||||
default:
|
||||
switch {
|
||||
// Blackberry
|
||||
case strings.Contains(ua, "blackberry") || strings.Contains(ua, "playbook"):
|
||||
u.OS.Platform = PlatformBlackberry
|
||||
u.OS.Name = OSBlackberry
|
||||
|
||||
// Windows Phone
|
||||
case strings.Contains(agentPlatform, "windows phone "):
|
||||
u.evalWindowsPhone(agentPlatform)
|
||||
|
||||
// Windows, Xbox
|
||||
case strings.Contains(ua, "windows "):
|
||||
u.evalWindows(ua)
|
||||
|
||||
// Kindle
|
||||
case strings.Contains(ua, "kindle/") || amazonFireFingerprint.MatchString(agentPlatform):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSKindle
|
||||
|
||||
// Linux (broader attempt)
|
||||
case strings.Contains(ua, "linux"):
|
||||
u.evalLinux(ua, agentPlatform)
|
||||
|
||||
// WebOS (non-linux flagged)
|
||||
case strings.Contains(ua, "webos") || strings.Contains(ua, "hpwos"):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSWebOS
|
||||
|
||||
// Nintendo
|
||||
case strings.Contains(ua, "nintendo"):
|
||||
u.OS.Platform = PlatformNintendo
|
||||
u.OS.Name = OSNintendo
|
||||
|
||||
// Playstation
|
||||
case strings.Contains(ua, "playstation") || strings.Contains(ua, "vita") || strings.Contains(ua, "psp"):
|
||||
u.OS.Platform = PlatformPlaystation
|
||||
u.OS.Name = OSPlaystation
|
||||
|
||||
// Android
|
||||
case strings.Contains(ua, "android"):
|
||||
u.evalLinux(ua, agentPlatform)
|
||||
|
||||
default:
|
||||
u.OS.Platform = PlatformUnknown
|
||||
u.OS.Name = OSUnknown
|
||||
}
|
||||
}
|
||||
|
||||
return u.isBot()
|
||||
}
|
||||
|
||||
func (u *UserAgent) isBot() bool {
|
||||
|
||||
if u.OS.Platform == PlatformBot || u.OS.Name == OSBot {
|
||||
u.DeviceType = DeviceComputer
|
||||
return true
|
||||
}
|
||||
|
||||
if u.Browser.Name >= BrowserBot && u.Browser.Name <= BrowserYahooBot {
|
||||
u.OS.Platform = PlatformBot
|
||||
u.OS.Name = OSBot
|
||||
u.DeviceType = DeviceComputer
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// evalLinux returns the `Platform`, `OSName` and Version of UAs with
|
||||
// 'linux' listed as their platform.
|
||||
func (u *UserAgent) evalLinux(ua string, agentPlatform string) {
|
||||
|
||||
switch {
|
||||
// Kindle Fire
|
||||
case strings.Contains(ua, "kindle") || amazonFireFingerprint.MatchString(agentPlatform):
|
||||
// get the version of Android if available, though we don't call this OSAndroid
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSKindle
|
||||
u.OS.Version.findVersionNumber(agentPlatform, "android ")
|
||||
|
||||
// Android, Kindle Fire
|
||||
case strings.Contains(ua, "android") || strings.Contains(ua, "googletv"):
|
||||
// Android
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSAndroid
|
||||
u.OS.Version.findVersionNumber(agentPlatform, "android ")
|
||||
|
||||
// ChromeOS
|
||||
case strings.Contains(ua, "cros"):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSChromeOS
|
||||
|
||||
// WebOS
|
||||
case strings.Contains(ua, "webos") || strings.Contains(ua, "hpwos"):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSWebOS
|
||||
|
||||
// Linux, "Linux-like"
|
||||
case strings.Contains(ua, "x11") || strings.Contains(ua, "bsd") || strings.Contains(ua, "suse") || strings.Contains(ua, "debian") || strings.Contains(ua, "ubuntu"):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSLinux
|
||||
|
||||
default:
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSLinux
|
||||
}
|
||||
}
|
||||
|
||||
// evaliOS returns the `Platform`, `OSName` and Version of UAs with
|
||||
// 'ipad' or 'iphone' listed as their platform.
|
||||
func (u *UserAgent) evaliOS(uaPlatform string, agentPlatform string) {
|
||||
|
||||
switch uaPlatform {
|
||||
// iPhone
|
||||
case "iphone":
|
||||
u.OS.Platform = PlatformiPhone
|
||||
u.OS.Name = OSiOS
|
||||
u.OS.getiOSVersion(agentPlatform)
|
||||
|
||||
// iPad
|
||||
case "ipad":
|
||||
u.OS.Platform = PlatformiPad
|
||||
u.OS.Name = OSiOS
|
||||
u.OS.getiOSVersion(agentPlatform)
|
||||
|
||||
// iPod
|
||||
case "ipod touch", "ipod":
|
||||
u.OS.Platform = PlatformiPod
|
||||
u.OS.Name = OSiOS
|
||||
u.OS.getiOSVersion(agentPlatform)
|
||||
|
||||
default:
|
||||
u.OS.Platform = PlatformiPad
|
||||
u.OS.Name = OSUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserAgent) evalWindowsPhone(agentPlatform string) {
|
||||
u.OS.Platform = PlatformWindowsPhone
|
||||
|
||||
if u.OS.Version.findVersionNumber(agentPlatform, "windows phone os ") || u.OS.Version.findVersionNumber(agentPlatform, "windows phone ") {
|
||||
u.OS.Name = OSWindowsPhone
|
||||
} else {
|
||||
u.OS.Name = OSUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserAgent) evalWindows(ua string) {
|
||||
|
||||
switch {
|
||||
//Xbox -- it reads just like Windows
|
||||
case strings.Contains(ua, "xbox"):
|
||||
u.OS.Platform = PlatformXbox
|
||||
u.OS.Name = OSXbox
|
||||
if !u.OS.Version.findVersionNumber(ua, "windows nt ") {
|
||||
u.OS.Version.Major = 6
|
||||
u.OS.Version.Minor = 0
|
||||
u.OS.Version.Patch = 0
|
||||
}
|
||||
|
||||
// No windows version
|
||||
case !strings.Contains(ua, "windows "):
|
||||
u.OS.Platform = PlatformWindows
|
||||
u.OS.Name = OSUnknown
|
||||
|
||||
case strings.Contains(ua, "windows nt ") && u.OS.Version.findVersionNumber(ua, "windows nt "):
|
||||
u.OS.Platform = PlatformWindows
|
||||
u.OS.Name = OSWindows
|
||||
|
||||
case strings.Contains(ua, "windows xp"):
|
||||
u.OS.Platform = PlatformWindows
|
||||
u.OS.Name = OSWindows
|
||||
u.OS.Version.Major = 5
|
||||
u.OS.Version.Minor = 1
|
||||
u.OS.Version.Patch = 0
|
||||
|
||||
default:
|
||||
u.OS.Platform = PlatformWindows
|
||||
u.OS.Name = OSUnknown
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserAgent) evalMacintosh(uaPlatformGroup string) {
|
||||
u.OS.Platform = PlatformMac
|
||||
if i := strings.Index(uaPlatformGroup, "os x 10"); i != -1 {
|
||||
u.OS.Name = OSMacOSX
|
||||
u.OS.Version.parse(uaPlatformGroup[i+5:])
|
||||
|
||||
return
|
||||
}
|
||||
u.OS.Name = OSUnknown
|
||||
}
|
||||
|
||||
func (v *Version) findVersionNumber(s string, m string) bool {
|
||||
if ind := strings.Index(s, m); ind != -1 {
|
||||
return v.parse(s[ind+len(m):])
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getiOSVersion accepts the platform portion of a UA string and returns
|
||||
// a Version.
|
||||
func (o *OS) getiOSVersion(uaPlatformGroup string) {
|
||||
if i := strings.Index(uaPlatformGroup, "cpu iphone os "); i != -1 {
|
||||
o.Version.parse(uaPlatformGroup[i+14:])
|
||||
return
|
||||
}
|
||||
|
||||
if i := strings.Index(uaPlatformGroup, "cpu os "); i != -1 {
|
||||
o.Version.parse(uaPlatformGroup[i+7:])
|
||||
return
|
||||
}
|
||||
|
||||
o.Version.parse(uaPlatformGroup)
|
||||
}
|
||||
|
||||
// strToInt simply accepts a string and returns a `int`,
|
||||
// with '0' being default.
|
||||
func strToInt(str string) int {
|
||||
i, _ := strconv.Atoi(str)
|
||||
return i
|
||||
}
|
||||
|
||||
// strToVer accepts a string and returns a Version,
|
||||
// with {0, 0, 0} being default.
|
||||
func (v *Version) parse(str string) bool {
|
||||
if len(str) == 0 || str[0] < '0' || str[0] > '9' {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
empty := true
|
||||
val := 0
|
||||
l := len(str) - 1
|
||||
|
||||
for k, c := range str {
|
||||
if c >= '0' && c <= '9' {
|
||||
if empty {
|
||||
val = int(c) - 48
|
||||
empty = false
|
||||
if k == l {
|
||||
str = str[:0]
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if val == 0 {
|
||||
if c == '0' {
|
||||
if k == l {
|
||||
str = str[:0]
|
||||
}
|
||||
continue
|
||||
}
|
||||
str = str[k:]
|
||||
break
|
||||
}
|
||||
|
||||
val = 10*val + int(c) - 48
|
||||
if k == l {
|
||||
str = str[:0]
|
||||
}
|
||||
continue
|
||||
}
|
||||
str = str[k+1:]
|
||||
break
|
||||
}
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
v.Major = val
|
||||
|
||||
case 1:
|
||||
v.Minor = val
|
||||
|
||||
case 2:
|
||||
v.Patch = val
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
227
vendor/github.com/avct/uasurfer/uasurfer.go
сгенерированный
поставляемый
Обычный файл
227
vendor/github.com/avct/uasurfer/uasurfer.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,227 @@
|
||||
// Package uasurfer provides fast and reliable abstraction
|
||||
// of HTTP User-Agent strings. The philosophy is to identify
|
||||
// technologies that holds >1% market share, and to avoid
|
||||
// expending resources and accuracy on guessing at esoteric UA
|
||||
// strings.
|
||||
package uasurfer
|
||||
|
||||
import "strings"
|
||||
|
||||
//go:generate stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go
|
||||
|
||||
// DeviceType (int) returns a constant.
|
||||
type DeviceType int
|
||||
|
||||
// A complete list of supported devices in the
|
||||
// form of constants.
|
||||
const (
|
||||
DeviceUnknown DeviceType = iota
|
||||
DeviceComputer
|
||||
DeviceTablet
|
||||
DevicePhone
|
||||
DeviceConsole
|
||||
DeviceWearable
|
||||
DeviceTV
|
||||
)
|
||||
|
||||
// BrowserName (int) returns a constant.
|
||||
type BrowserName int
|
||||
|
||||
// A complete list of supported web browsers in the
|
||||
// form of constants.
|
||||
const (
|
||||
BrowserUnknown BrowserName = iota
|
||||
BrowserChrome
|
||||
BrowserIE
|
||||
BrowserSafari
|
||||
BrowserFirefox
|
||||
BrowserAndroid
|
||||
BrowserOpera
|
||||
BrowserBlackberry
|
||||
BrowserUCBrowser
|
||||
BrowserSilk
|
||||
BrowserNokia
|
||||
BrowserNetFront
|
||||
BrowserQQ
|
||||
BrowserMaxthon
|
||||
BrowserSogouExplorer
|
||||
BrowserSpotify
|
||||
BrowserBot // Bot list begins here
|
||||
BrowserAppleBot
|
||||
BrowserBaiduBot
|
||||
BrowserBingBot
|
||||
BrowserDuckDuckGoBot
|
||||
BrowserFacebookBot
|
||||
BrowserGoogleBot
|
||||
BrowserLinkedInBot
|
||||
BrowserMsnBot
|
||||
BrowserPingdomBot
|
||||
BrowserTwitterBot
|
||||
BrowserYandexBot
|
||||
BrowserYahooBot // Bot list ends here
|
||||
)
|
||||
|
||||
// OSName (int) returns a constant.
|
||||
type OSName int
|
||||
|
||||
// A complete list of supported OSes in the
|
||||
// form of constants. For handling particular versions
|
||||
// of operating systems (e.g. Windows 2000), see
|
||||
// the README.md file.
|
||||
const (
|
||||
OSUnknown OSName = iota
|
||||
OSWindowsPhone
|
||||
OSWindows
|
||||
OSMacOSX
|
||||
OSiOS
|
||||
OSAndroid
|
||||
OSBlackberry
|
||||
OSChromeOS
|
||||
OSKindle
|
||||
OSWebOS
|
||||
OSLinux
|
||||
OSPlaystation
|
||||
OSXbox
|
||||
OSNintendo
|
||||
OSBot
|
||||
)
|
||||
|
||||
// Platform (int) returns a constant.
|
||||
type Platform int
|
||||
|
||||
// A complete list of supported platforms in the
|
||||
// form of constants. Many OSes report their
|
||||
// true platform, such as Android OS being Linux
|
||||
// platform.
|
||||
const (
|
||||
PlatformUnknown Platform = iota
|
||||
PlatformWindows
|
||||
PlatformMac
|
||||
PlatformLinux
|
||||
PlatformiPad
|
||||
PlatformiPhone
|
||||
PlatformiPod
|
||||
PlatformBlackberry
|
||||
PlatformWindowsPhone
|
||||
PlatformPlaystation
|
||||
PlatformXbox
|
||||
PlatformNintendo
|
||||
PlatformBot
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
}
|
||||
|
||||
func (v Version) Less(c Version) bool {
|
||||
if v.Major < c.Major {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Major > c.Major {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.Minor < c.Minor {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Minor > c.Minor {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.Patch < c.Patch
|
||||
}
|
||||
|
||||
type UserAgent struct {
|
||||
Browser Browser
|
||||
OS OS
|
||||
DeviceType DeviceType
|
||||
}
|
||||
|
||||
type Browser struct {
|
||||
Name BrowserName
|
||||
Version Version
|
||||
}
|
||||
|
||||
type OS struct {
|
||||
Platform Platform
|
||||
Name OSName
|
||||
Version Version
|
||||
}
|
||||
|
||||
// Reset resets the UserAgent to it's zero value
|
||||
func (ua *UserAgent) Reset() {
|
||||
ua.Browser = Browser{}
|
||||
ua.OS = OS{}
|
||||
ua.DeviceType = DeviceUnknown
|
||||
}
|
||||
|
||||
// Parse accepts a raw user agent (string) and returns the UserAgent.
|
||||
func Parse(ua string) *UserAgent {
|
||||
dest := new(UserAgent)
|
||||
parse(ua, dest)
|
||||
return dest
|
||||
}
|
||||
|
||||
// ParseUserAgent is the same as Parse, but populates the supplied UserAgent.
|
||||
// It is the caller's responsibility to call Reset() on the UserAgent before
|
||||
// passing it to this function.
|
||||
func ParseUserAgent(ua string, dest *UserAgent) {
|
||||
parse(ua, dest)
|
||||
}
|
||||
|
||||
func parse(ua string, dest *UserAgent) {
|
||||
ua = normalise(ua)
|
||||
switch {
|
||||
case len(ua) == 0:
|
||||
dest.OS.Platform = PlatformUnknown
|
||||
dest.OS.Name = OSUnknown
|
||||
dest.Browser.Name = BrowserUnknown
|
||||
dest.DeviceType = DeviceUnknown
|
||||
|
||||
// stop on on first case returning true
|
||||
case dest.evalOS(ua):
|
||||
case dest.evalBrowserName(ua):
|
||||
default:
|
||||
dest.evalBrowserVersion(ua)
|
||||
dest.evalDevice(ua)
|
||||
}
|
||||
}
|
||||
|
||||
// normalise normalises the user supplied agent string so that
|
||||
// we can more easily parse it.
|
||||
func normalise(ua string) string {
|
||||
if len(ua) <= 1024 {
|
||||
var buf [1024]byte
|
||||
ascii := copyLower(buf[:len(ua)], ua)
|
||||
if !ascii {
|
||||
// Fall back for non ascii characters
|
||||
return strings.ToLower(ua)
|
||||
}
|
||||
return string(buf[:len(ua)])
|
||||
}
|
||||
// Fallback for unusually long strings
|
||||
return strings.ToLower(ua)
|
||||
}
|
||||
|
||||
// copyLower copies a lowercase version of s to b. It assumes s contains only single byte characters
|
||||
// and will panic if b is nil or is not long enough to contain all the bytes from s.
|
||||
// It returns early with false if any characters were non ascii.
|
||||
func copyLower(b []byte, s string) bool {
|
||||
for j := 0; j < len(s); j++ {
|
||||
c := s[j]
|
||||
if c > 127 {
|
||||
return false
|
||||
}
|
||||
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
c += 'a' - 'A'
|
||||
}
|
||||
|
||||
b[j] = c
|
||||
}
|
||||
return true
|
||||
}
|
||||
1064
vendor/github.com/avct/uasurfer/uasurfer_test.go
сгенерированный
поставляемый
Обычный файл
1064
vendor/github.com/avct/uasurfer/uasurfer_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
8
vendor/github.com/dgryski/dgoogauth/googauth_test.go
сгенерированный
поставляемый
8
vendor/github.com/dgryski/dgoogauth/googauth_test.go
сгенерированный
поставляемый
@@ -1,7 +1,7 @@
|
||||
package dgoogauth
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -199,11 +199,11 @@ func TestAuthenticate(t *testing.T) {
|
||||
t0 = int64(time.Now().Unix() / 30)
|
||||
}
|
||||
c := ComputeCode(otpconf.Secret, t0)
|
||||
code := fmt.Sprintf("%06d", c)
|
||||
|
||||
invalid := c + 1
|
||||
attempts = []attempt{
|
||||
{strconv.Itoa(invalid), false},
|
||||
{strconv.Itoa(c), true},
|
||||
{code + "1", false},
|
||||
{code, true},
|
||||
}
|
||||
|
||||
for _, a := range attempts {
|
||||
|
||||
6
vendor/github.com/go-ini/ini/file.go
сгенерированный
поставляемый
6
vendor/github.com/go-ini/ini/file.go
сгенерированный
поставляемый
@@ -345,6 +345,12 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, val := range key.nestedValues {
|
||||
if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PrettySection {
|
||||
|
||||
12
vendor/github.com/go-ini/ini/ini.go
сгенерированный
поставляемый
12
vendor/github.com/go-ini/ini/ini.go
сгенерированный
поставляемый
@@ -32,7 +32,7 @@ const (
|
||||
|
||||
// Maximum allowed depth when recursively substituing variable names.
|
||||
_DEPTH_VALUES = 99
|
||||
_VERSION = "1.30.3"
|
||||
_VERSION = "1.32.0"
|
||||
)
|
||||
|
||||
// Version returns current package version literal.
|
||||
@@ -134,8 +134,16 @@ type LoadOptions struct {
|
||||
AllowBooleanKeys bool
|
||||
// AllowShadows indicates whether to keep track of keys with same name under same section.
|
||||
AllowShadows bool
|
||||
// UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
|
||||
// AllowNestedValues indicates whether to allow AWS-like nested values.
|
||||
// Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values
|
||||
AllowNestedValues bool
|
||||
// UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format
|
||||
// when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
|
||||
UnescapeValueDoubleQuotes bool
|
||||
// UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format
|
||||
// when value is NOT surrounded by any quotes.
|
||||
// Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all.
|
||||
UnescapeValueCommentSymbols bool
|
||||
// Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
|
||||
// conform to key/value pairs. Specify the names of those blocks here.
|
||||
UnparseableSections []string
|
||||
|
||||
13
vendor/github.com/go-ini/ini/ini_test.go
сгенерированный
поставляемый
13
vendor/github.com/go-ini/ini/ini_test.go
сгенерированный
поставляемый
@@ -268,6 +268,19 @@ create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Unescape comment symbols inside value", func() {
|
||||
f, err := ini.LoadSources(ini.LoadOptions{
|
||||
IgnoreInlineComment: true,
|
||||
UnescapeValueCommentSymbols: true,
|
||||
}, []byte(`
|
||||
key = test value <span style="color: %s\; background: %s">more text</span>
|
||||
`))
|
||||
So(err, ShouldBeNil)
|
||||
So(f, ShouldNotBeNil)
|
||||
|
||||
So(f.Section("").Key("key").String(), ShouldEqual, `test value <span style="color: %s; background: %s">more text</span>`)
|
||||
})
|
||||
|
||||
Convey("Allow unparseable sections", func() {
|
||||
f, err := ini.LoadSources(ini.LoadOptions{
|
||||
Insensitive: true,
|
||||
|
||||
26
vendor/github.com/go-ini/ini/key.go
сгенерированный
поставляемый
26
vendor/github.com/go-ini/ini/key.go
сгенерированный
поставляемый
@@ -34,6 +34,8 @@ type Key struct {
|
||||
|
||||
isShadow bool
|
||||
shadows []*Key
|
||||
|
||||
nestedValues []string
|
||||
}
|
||||
|
||||
// newKey simply return a key object with given values.
|
||||
@@ -66,6 +68,22 @@ func (k *Key) AddShadow(val string) error {
|
||||
return k.addShadow(val)
|
||||
}
|
||||
|
||||
func (k *Key) addNestedValue(val string) error {
|
||||
if k.isAutoIncrement || k.isBooleanType {
|
||||
return errors.New("cannot add nested value to auto-increment or boolean key")
|
||||
}
|
||||
|
||||
k.nestedValues = append(k.nestedValues, val)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Key) AddNestedValue(val string) error {
|
||||
if !k.s.f.options.AllowNestedValues {
|
||||
return errors.New("nested value is not allowed")
|
||||
}
|
||||
return k.addNestedValue(val)
|
||||
}
|
||||
|
||||
// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv
|
||||
type ValueMapper func(string) string
|
||||
|
||||
@@ -92,6 +110,12 @@ func (k *Key) ValueWithShadows() []string {
|
||||
return vals
|
||||
}
|
||||
|
||||
// NestedValues returns nested values stored in the key.
|
||||
// It is possible returned value is nil if no nested values stored in the key.
|
||||
func (k *Key) NestedValues() []string {
|
||||
return k.nestedValues
|
||||
}
|
||||
|
||||
// transformValue takes a raw value and transforms to its final string.
|
||||
func (k *Key) transformValue(val string) string {
|
||||
if k.s.f.ValueMapper != nil {
|
||||
@@ -114,7 +138,7 @@ func (k *Key) transformValue(val string) string {
|
||||
|
||||
// Search in the same section.
|
||||
nk, err := k.s.GetKey(noption)
|
||||
if err != nil {
|
||||
if err != nil || k == nk {
|
||||
// Search again in default section.
|
||||
nk, _ = k.s.f.Section("").GetKey(noption)
|
||||
}
|
||||
|
||||
43
vendor/github.com/go-ini/ini/key_test.go
сгенерированный
поставляемый
43
vendor/github.com/go-ini/ini/key_test.go
сгенерированный
поставляемый
@@ -15,6 +15,7 @@
|
||||
package ini_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -478,3 +479,45 @@ func TestKey_SetValue(t *testing.T) {
|
||||
So(k.Value(), ShouldEqual, "ini.v1")
|
||||
})
|
||||
}
|
||||
|
||||
func TestKey_NestedValues(t *testing.T) {
|
||||
Convey("Read and write nested values", t, func() {
|
||||
f, err := ini.LoadSources(ini.LoadOptions{
|
||||
AllowNestedValues: true,
|
||||
}, []byte(`
|
||||
aws_access_key_id = foo
|
||||
aws_secret_access_key = bar
|
||||
region = us-west-2
|
||||
s3 =
|
||||
max_concurrent_requests=10
|
||||
max_queue_size=1000`))
|
||||
So(err, ShouldBeNil)
|
||||
So(f, ShouldNotBeNil)
|
||||
|
||||
So(f.Section("").Key("s3").NestedValues(), ShouldResemble, []string{"max_concurrent_requests=10", "max_queue_size=1000"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = f.WriteTo(&buf)
|
||||
So(err, ShouldBeNil)
|
||||
So(buf.String(), ShouldEqual, `aws_access_key_id = foo
|
||||
aws_secret_access_key = bar
|
||||
region = us-west-2
|
||||
s3 =
|
||||
max_concurrent_requests=10
|
||||
max_queue_size=1000
|
||||
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecursiveValues(t *testing.T) {
|
||||
Convey("Recursive values should not reflect on same key", t, func() {
|
||||
f, err := ini.Load([]byte(`
|
||||
NAME = ini
|
||||
[package]
|
||||
NAME = %(NAME)s`))
|
||||
So(err, ShouldBeNil)
|
||||
So(f, ShouldNotBeNil)
|
||||
So(f.Section("package").Key("NAME").String(), ShouldEqual, "ini")
|
||||
})
|
||||
}
|
||||
|
||||
31
vendor/github.com/go-ini/ini/parser.go
сгенерированный
поставляемый
31
vendor/github.com/go-ini/ini/parser.go
сгенерированный
поставляемый
@@ -193,7 +193,9 @@ func hasSurroundedQuote(in string, quote byte) bool {
|
||||
strings.IndexByte(in[1:], quote) == len(in)-2
|
||||
}
|
||||
|
||||
func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes bool) (string, error) {
|
||||
func (p *parser) readValue(in []byte,
|
||||
ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols bool) (string, error) {
|
||||
|
||||
line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
|
||||
if len(line) == 0 {
|
||||
return "", nil
|
||||
@@ -243,6 +245,13 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment, u
|
||||
if hasSurroundedQuote(line, '\'') ||
|
||||
hasSurroundedQuote(line, '"') {
|
||||
line = line[1 : len(line)-1]
|
||||
} else if len(valQuote) == 0 && unescapeValueCommentSymbols {
|
||||
if strings.Contains(line, `\;`) {
|
||||
line = strings.Replace(line, `\;`, ";", -1)
|
||||
}
|
||||
if strings.Contains(line, `\#`) {
|
||||
line = strings.Replace(line, `\#`, "#", -1)
|
||||
}
|
||||
}
|
||||
return line, nil
|
||||
}
|
||||
@@ -261,6 +270,10 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
}
|
||||
section, _ := f.NewSection(name)
|
||||
|
||||
// This "last" is not strictly equivalent to "previous one" if current key is not the first nested key
|
||||
var isLastValueEmpty bool
|
||||
var lastRegularKey *Key
|
||||
|
||||
var line []byte
|
||||
var inUnparseableSection bool
|
||||
for !p.isEOF {
|
||||
@@ -269,6 +282,14 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.options.AllowNestedValues &&
|
||||
isLastValueEmpty && len(line) > 0 {
|
||||
if line[0] == ' ' || line[0] == '\t' {
|
||||
lastRegularKey.addNestedValue(string(bytes.TrimSpace(line)))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
@@ -333,7 +354,8 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
kname, err := p.readValue(line,
|
||||
f.options.IgnoreContinuation,
|
||||
f.options.IgnoreInlineComment,
|
||||
f.options.UnescapeValueDoubleQuotes)
|
||||
f.options.UnescapeValueDoubleQuotes,
|
||||
f.options.UnescapeValueCommentSymbols)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -359,10 +381,12 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
value, err := p.readValue(line[offset:],
|
||||
f.options.IgnoreContinuation,
|
||||
f.options.IgnoreInlineComment,
|
||||
f.options.UnescapeValueDoubleQuotes)
|
||||
f.options.UnescapeValueDoubleQuotes,
|
||||
f.options.UnescapeValueCommentSymbols)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isLastValueEmpty = len(value) == 0
|
||||
|
||||
key, err := section.NewKey(kname, value)
|
||||
if err != nil {
|
||||
@@ -371,6 +395,7 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
key.isAutoIncrement = isAutoIncr
|
||||
key.Comment = strings.TrimSpace(p.comment.String())
|
||||
p.comment.Reset()
|
||||
lastRegularKey = key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
3
vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
сгенерированный
поставляемый
3
vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
сгенерированный
поставляемый
@@ -193,8 +193,7 @@ func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeU
|
||||
// "Generated output always contains 3, 6, or 9 fractional digits,
|
||||
// depending on required precision."
|
||||
s, ns := s.Field(0).Int(), s.Field(1).Int()
|
||||
d := time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond
|
||||
x := fmt.Sprintf("%.9f", d.Seconds())
|
||||
x := fmt.Sprintf("%d.%09d", s, ns)
|
||||
x = strings.TrimSuffix(x, "000")
|
||||
x = strings.TrimSuffix(x, "000")
|
||||
out.write(`"`)
|
||||
|
||||
1
vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
сгенерированный
поставляемый
1
vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
сгенерированный
поставляемый
@@ -407,6 +407,7 @@ var marshalingTests = []struct {
|
||||
{"Any with WKT", marshaler, anyWellKnown, anyWellKnownJSON},
|
||||
{"Any with WKT and indent", marshalerAllOptions, anyWellKnown, anyWellKnownPrettyJSON},
|
||||
{"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}, `{"dur":"3.000s"}`},
|
||||
{"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 100000000, Nanos: 1}}, `{"dur":"100000000.000000001s"}`},
|
||||
{"Struct", marshaler, &pb.KnownTypes{St: &stpb.Struct{
|
||||
Fields: map[string]*stpb.Value{
|
||||
"one": {Kind: &stpb.Value_StringValue{"loneliest number"}},
|
||||
|
||||
2
vendor/github.com/hashicorp/go-multierror/.travis.yml
сгенерированный
поставляемый
2
vendor/github.com/hashicorp/go-multierror/.travis.yml
сгенерированный
поставляемый
@@ -3,7 +3,7 @@ sudo: false
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
- 1.x
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
||||
4
vendor/github.com/hashicorp/memberlist/memberlist.go
сгенерированный
поставляемый
4
vendor/github.com/hashicorp/memberlist/memberlist.go
сгенерированный
поставляемый
@@ -639,7 +639,9 @@ func (m *Memberlist) Shutdown() error {
|
||||
// Shut down the transport first, which should block until it's
|
||||
// completely torn down. If we kill the memberlist-side handlers
|
||||
// those I/O handlers might get stuck.
|
||||
m.transport.Shutdown()
|
||||
if err := m.transport.Shutdown(); err != nil {
|
||||
m.logger.Printf("[ERR] Failed to shutdown transport: %v", err)
|
||||
}
|
||||
|
||||
// Now tear down everything else.
|
||||
atomic.StoreInt32(&m.shutdown, 1)
|
||||
|
||||
2
vendor/github.com/hashicorp/memberlist/net.go
сгенерированный
поставляемый
2
vendor/github.com/hashicorp/memberlist/net.go
сгенерированный
поставляемый
@@ -1059,7 +1059,7 @@ func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error {
|
||||
// operations, given the deadline. The bool return parameter is true if we
|
||||
// we able to round trip a ping to the other node.
|
||||
func (m *Memberlist) sendPingAndWaitForAck(addr string, ping ping, deadline time.Time) (bool, error) {
|
||||
conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout)
|
||||
conn, err := m.transport.DialTimeout(addr, deadline.Sub(time.Now()))
|
||||
if err != nil {
|
||||
// If the node is actually dead we expect this to fail, so we
|
||||
// shouldn't spam the logs with it. After this point, errors
|
||||
|
||||
14
vendor/github.com/hashicorp/memberlist/state.go
сгенерированный
поставляемый
14
vendor/github.com/hashicorp/memberlist/state.go
сгенерированный
поставляемый
@@ -251,10 +251,17 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
||||
nackCh := make(chan struct{}, m.config.IndirectChecks+1)
|
||||
m.setProbeChannels(ping.SeqNo, ackCh, nackCh, probeInterval)
|
||||
|
||||
// Mark the sent time here, which should be after any pre-processing but
|
||||
// before system calls to do the actual send. This probably over-reports
|
||||
// a bit, but it's the best we can do. We had originally put this right
|
||||
// after the I/O, but that would sometimes give negative RTT measurements
|
||||
// which was not desirable.
|
||||
sent := time.Now()
|
||||
|
||||
// Send a ping to the node. If this node looks like it's suspect or dead,
|
||||
// also tack on a suspect message so that it has a chance to refute as
|
||||
// soon as possible.
|
||||
deadline := time.Now().Add(probeInterval)
|
||||
deadline := sent.Add(probeInterval)
|
||||
addr := node.Address()
|
||||
if node.State == stateAlive {
|
||||
if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil {
|
||||
@@ -284,11 +291,6 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the sent time here, which should be after any pre-processing and
|
||||
// system calls to do the actual send. This probably under-reports a bit,
|
||||
// but it's the best we can do.
|
||||
sent := time.Now()
|
||||
|
||||
// Arrange for our self-awareness to get updated. At this point we've
|
||||
// sent the ping, so any return statement means the probe succeeded
|
||||
// which will improve our health until we get to the failure scenarios
|
||||
|
||||
2
vendor/github.com/hashicorp/memberlist/transport.go
сгенерированный
поставляемый
2
vendor/github.com/hashicorp/memberlist/transport.go
сгенерированный
поставляемый
@@ -17,7 +17,7 @@ type Packet struct {
|
||||
|
||||
// Timestamp is the time when the packet was received. This should be
|
||||
// taken as close as possible to the actual receipt time to help make an
|
||||
// accurate RTT measurements during probes.
|
||||
// accurate RTT measurement during probes.
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
|
||||
14
vendor/github.com/lib/pq/.travis.yml
сгенерированный
поставляемый
14
vendor/github.com/lib/pq/.travis.yml
сгенерированный
поставляемый
@@ -16,7 +16,7 @@ env:
|
||||
- PQGOSSLTESTS=1
|
||||
- PQSSLCERTTEST_PATH=$PWD/certs
|
||||
- PGHOST=127.0.0.1
|
||||
- MEGACHECK_VERSION=2017.1
|
||||
- MEGACHECK_VERSION=2017.2.1
|
||||
matrix:
|
||||
- PGVERSION=10
|
||||
- PGVERSION=9.6
|
||||
@@ -46,15 +46,13 @@ script:
|
||||
- >
|
||||
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
|
||||
- go vet ./...
|
||||
# For compatibility with Go 1.5, launch only if megacheck is present,
|
||||
# ignore SA1019 (deprecation warnings) in conn_test.go (we have to use the
|
||||
# deprecated driver.Execer and driver.Queryer interfaces) and S1024
|
||||
# (time.Until) everywhere.
|
||||
# For compatibility with Go 1.5, launch only if megacheck is present.
|
||||
- >
|
||||
which megacheck > /dev/null
|
||||
&& megacheck -ignore 'github.com/lib/pq/conn_test.go:SA1019 github.com/lib/pq/*.go:S1024' ./...
|
||||
which megacheck > /dev/null && megacheck -go 1.5 ./...
|
||||
|| echo 'megacheck is not supported, skipping check'
|
||||
# For compatibility with Go 1.5, launch only if golint is present.
|
||||
- which golint > /dev/null && golint ./... || echo 'golint is not supported, skipping check'
|
||||
- >
|
||||
which golint > /dev/null && golint ./...
|
||||
|| echo 'golint is not supported, skipping check'
|
||||
- PQTEST_BINARY_PARAMETERS=no go test -race -v ./...
|
||||
- PQTEST_BINARY_PARAMETERS=yes go test -race -v ./...
|
||||
|
||||
2
vendor/github.com/lib/pq/doc.go
сгенерированный
поставляемый
2
vendor/github.com/lib/pq/doc.go
сгенерированный
поставляемый
@@ -239,7 +239,7 @@ for more information). Note that the channel name will be truncated to 63
|
||||
bytes by the PostgreSQL server.
|
||||
|
||||
You can find a complete, working example of Listener usage at
|
||||
http://godoc.org/github.com/lib/pq/examples/listen.
|
||||
http://godoc.org/github.com/lib/pq/example/listen.
|
||||
|
||||
*/
|
||||
package pq
|
||||
|
||||
8
vendor/github.com/miekg/dns/.codecov.yml
сгенерированный
поставляемый
Обычный файл
8
vendor/github.com/miekg/dns/.codecov.yml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,8 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 40%
|
||||
threshold: null
|
||||
patch: false
|
||||
changes: false
|
||||
21
vendor/github.com/miekg/dns/Gopkg.lock
сгенерированный
поставляемый
Обычный файл
21
vendor/github.com/miekg/dns/Gopkg.lock
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,21 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ed25519","ed25519/internal/edwards25519"]
|
||||
revision = "b080dc9a8c480b08e698fb1219160d598526310f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["bpf","internal/iana","internal/socket","ipv4","ipv6"]
|
||||
revision = "894f8ed5849b15b810ae41e9590a0d05395bba27"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "c4abc38abaeeeeb9be92455c9c02cae32841122b8982aaa067ef25bb8e86ff9d"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
26
vendor/github.com/miekg/dns/Gopkg.toml
сгенерированный
поставляемый
Обычный файл
26
vendor/github.com/miekg/dns/Gopkg.toml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,26 @@
|
||||
|
||||
# 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"
|
||||
33
vendor/github.com/miekg/dns/Makefile.fuzz
сгенерированный
поставляемый
Обычный файл
33
vendor/github.com/miekg/dns/Makefile.fuzz
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,33 @@
|
||||
# Makefile for fuzzing
|
||||
#
|
||||
# Use go-fuzz and needs the tools installed.
|
||||
# See https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/
|
||||
#
|
||||
# Installing go-fuzz:
|
||||
# $ make -f Makefile.fuzz get
|
||||
# Installs:
|
||||
# * github.com/dvyukov/go-fuzz/go-fuzz
|
||||
# * get github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
||||
all: build
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go-fuzz-build -tags fuzz github.com/miekg/dns
|
||||
|
||||
.PHONY: build-newrr
|
||||
build-newrr:
|
||||
go-fuzz-build -func FuzzNewRR -tags fuzz github.com/miekg/dns
|
||||
|
||||
.PHONY: fuzz
|
||||
fuzz:
|
||||
go-fuzz -bin=dns-fuzz.zip -workdir=fuzz
|
||||
|
||||
.PHONY: get
|
||||
get:
|
||||
go get github.com/dvyukov/go-fuzz/go-fuzz
|
||||
go get github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm *-fuzz.zip
|
||||
52
vendor/github.com/miekg/dns/Makefile.release
сгенерированный
поставляемый
Обычный файл
52
vendor/github.com/miekg/dns/Makefile.release
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,52 @@
|
||||
# Makefile for releasing.
|
||||
#
|
||||
# The release is controlled from version.go. The version found there is
|
||||
# used to tag the git repo, we're not building any artifects so there is nothing
|
||||
# to upload to github.
|
||||
#
|
||||
# * Up the version in version.go
|
||||
# * Run: make -f Makefile.release release
|
||||
# * will *commit* your change with 'Release $VERSION'
|
||||
# * push to github
|
||||
#
|
||||
|
||||
define GO
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(dns.Version.String())
|
||||
}
|
||||
endef
|
||||
|
||||
$(file > version_release.go,$(GO))
|
||||
VERSION:=$(shell go run version_release.go)
|
||||
TAG="v$(VERSION)"
|
||||
|
||||
all:
|
||||
@echo Use the \'release\' target to start a release $(VERSION)
|
||||
rm -f version_release.go
|
||||
|
||||
.PHONY: release
|
||||
release: commit push
|
||||
@echo Released $(VERSION)
|
||||
rm -f version_release.go
|
||||
|
||||
.PHONY: commit
|
||||
commit:
|
||||
@echo Committing release $(VERSION)
|
||||
git commit -am"Release $(VERSION)"
|
||||
git tag $(TAG)
|
||||
|
||||
.PHONY: push
|
||||
push:
|
||||
@echo Pushing release $(VERSION) to master
|
||||
git push --tags
|
||||
git push
|
||||
5
vendor/github.com/miekg/dns/README.md
сгенерированный
поставляемый
5
vendor/github.com/miekg/dns/README.md
сгенерированный
поставляемый
@@ -74,7 +74,7 @@ Send pull request if you want to be listed here.
|
||||
* Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds;
|
||||
* Server side programming (mimicking the net/http package);
|
||||
* Client side programming;
|
||||
* DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA;
|
||||
* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519;
|
||||
* EDNS0, NSID, Cookies;
|
||||
* AXFR/IXFR;
|
||||
* TSIG, SIG(0);
|
||||
@@ -150,10 +150,13 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
|
||||
* 6975 - Algorithm Understanding in DNSSEC
|
||||
* 7043 - EUI48/EUI64 records
|
||||
* 7314 - DNS (EDNS) EXPIRE Option
|
||||
* 7477 - CSYNC RR
|
||||
* 7828 - edns-tcp-keepalive EDNS0 Option
|
||||
* 7553 - URI record
|
||||
* 7858 - DNS over TLS: Initiation and Performance Considerations
|
||||
* 7871 - EDNS0 Client Subnet
|
||||
* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
|
||||
* 8080 - EdDSA for DNSSEC
|
||||
|
||||
## Loosely based upon
|
||||
|
||||
|
||||
19
vendor/github.com/miekg/dns/client.go
сгенерированный
поставляемый
19
vendor/github.com/miekg/dns/client.go
сгенерированный
поставляемый
@@ -20,7 +20,7 @@ const tcpIdleTimeout time.Duration = 8 * time.Second
|
||||
type Conn struct {
|
||||
net.Conn // a net.Conn holding the connection
|
||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
|
||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
||||
rtt time.Duration
|
||||
t time.Time
|
||||
tsigRequestMAC string
|
||||
@@ -39,7 +39,7 @@ type Client struct {
|
||||
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
|
||||
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
|
||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
||||
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
|
||||
group singleflight
|
||||
}
|
||||
@@ -192,8 +192,10 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
||||
}
|
||||
|
||||
// ReadMsg reads a message from the connection co.
|
||||
// If the received message contains a TSIG record the transaction
|
||||
// signature is verified.
|
||||
// If the received message contains a TSIG record the transaction signature
|
||||
// is verified. This method always tries to return the message, however if an
|
||||
// error is returned there are no guarantees that the returned message is a
|
||||
// valid representation of the packet read.
|
||||
func (co *Conn) ReadMsg() (*Msg, error) {
|
||||
p, err := co.ReadMsgHeader(nil)
|
||||
if err != nil {
|
||||
@@ -202,13 +204,10 @@ func (co *Conn) ReadMsg() (*Msg, error) {
|
||||
|
||||
m := new(Msg)
|
||||
if err := m.Unpack(p); err != nil {
|
||||
// If ErrTruncated was returned, we still want to allow the user to use
|
||||
// If an error was returned, we still want to allow the user to use
|
||||
// the message, but naively they can just check err if they don't want
|
||||
// to use a truncated message
|
||||
if err == ErrTruncated {
|
||||
return m, err
|
||||
}
|
||||
return nil, err
|
||||
// to use an erroneous message
|
||||
return m, err
|
||||
}
|
||||
if t := m.IsTsig(); t != nil {
|
||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
||||
|
||||
10
vendor/github.com/miekg/dns/client_test.go
сгенерированный
поставляемый
10
vendor/github.com/miekg/dns/client_test.go
сгенерированный
поставляемый
@@ -458,12 +458,12 @@ func TestTruncatedMsg(t *testing.T) {
|
||||
t.Errorf("error should not be ErrTruncated from question cutoff unpack: %v", err)
|
||||
}
|
||||
|
||||
// Finally, if we only have the header, we should still return an error
|
||||
// Finally, if we only have the header, we don't return an error.
|
||||
buf1 = buf[:12]
|
||||
|
||||
r = new(Msg)
|
||||
if err = r.Unpack(buf1); err == nil || err != ErrTruncated {
|
||||
t.Errorf("error not ErrTruncated from header-only unpack: %v", err)
|
||||
if err = r.Unpack(buf1); err != nil {
|
||||
t.Errorf("from header-only unpack should not return an error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,11 +568,11 @@ func TestConcurrentExchanges(t *testing.T) {
|
||||
wg.Add(len(r))
|
||||
for i := 0; i < len(r); i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
r[i], _, _ = c.Exchange(m.Copy(), addrstr)
|
||||
if r[i] == nil {
|
||||
t.Fatalf("response %d is nil", i)
|
||||
t.Errorf("response %d is nil", i)
|
||||
}
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
select {
|
||||
|
||||
6
vendor/github.com/miekg/dns/clientconfig.go
сгенерированный
поставляемый
6
vendor/github.com/miekg/dns/clientconfig.go
сгенерированный
поставляемый
@@ -79,8 +79,10 @@ func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
|
||||
switch {
|
||||
case len(s) >= 6 && s[:6] == "ndots:":
|
||||
n, _ := strconv.Atoi(s[6:])
|
||||
if n < 1 {
|
||||
n = 1
|
||||
if n < 0 {
|
||||
n = 0
|
||||
} else if n > 15 {
|
||||
n = 15
|
||||
}
|
||||
c.Ndots = n
|
||||
case len(s) >= 8 && s[:8] == "timeout:":
|
||||
|
||||
54
vendor/github.com/miekg/dns/clientconfig_test.go
сгенерированный
поставляемый
54
vendor/github.com/miekg/dns/clientconfig_test.go
сгенерированный
поставляемый
@@ -40,6 +40,28 @@ func testConfig(t *testing.T, data string) {
|
||||
func TestNameserver(t *testing.T) { testConfig(t, normal) }
|
||||
func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) }
|
||||
|
||||
func TestNdots(t *testing.T) {
|
||||
ndotsVariants := map[string]int{
|
||||
"options ndots:0": 0,
|
||||
"options ndots:1": 1,
|
||||
"options ndots:15": 15,
|
||||
"options ndots:16": 15,
|
||||
"options ndots:-1": 0,
|
||||
"": 1,
|
||||
}
|
||||
|
||||
for data := range ndotsVariants {
|
||||
cc, err := ClientConfigFromReader(strings.NewReader(data))
|
||||
if err != nil {
|
||||
t.Errorf("error parsing resolv.conf: %v", err)
|
||||
}
|
||||
if cc.Ndots != ndotsVariants[data] {
|
||||
t.Errorf("Ndots not properly parsed: (Expected: %d / Was: %d)", ndotsVariants[data], cc.Ndots)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestReadFromFile(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
@@ -67,7 +89,7 @@ func TestReadFromFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameList(t *testing.T) {
|
||||
func TestNameListNdots1(t *testing.T) {
|
||||
cfg := ClientConfig{
|
||||
Ndots: 1,
|
||||
}
|
||||
@@ -91,10 +113,18 @@ func TestNameList(t *testing.T) {
|
||||
} else if names[1] != "miek.nl.test." {
|
||||
t.Errorf("NameList didn't return search last: %v", names[1])
|
||||
}
|
||||
}
|
||||
func TestNameListNdots2(t *testing.T) {
|
||||
cfg := ClientConfig{
|
||||
Ndots: 2,
|
||||
}
|
||||
|
||||
cfg.Ndots = 2
|
||||
// Sent domain has less than NDots and search
|
||||
names = cfg.NameList("miek.nl")
|
||||
cfg.Search = []string{
|
||||
"test",
|
||||
}
|
||||
names := cfg.NameList("miek.nl")
|
||||
|
||||
if len(names) != 2 {
|
||||
t.Errorf("NameList returned != 2 names: %v", names)
|
||||
} else if names[0] != "miek.nl.test." {
|
||||
@@ -103,3 +133,21 @@ func TestNameList(t *testing.T) {
|
||||
t.Errorf("NameList didn't return sent domain last: %v", names[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameListNdots0(t *testing.T) {
|
||||
cfg := ClientConfig{
|
||||
Ndots: 0,
|
||||
}
|
||||
cfg.Search = []string{
|
||||
"test",
|
||||
}
|
||||
// Sent domain has less than NDots and search
|
||||
names := cfg.NameList("miek")
|
||||
if len(names) != 2 {
|
||||
t.Errorf("NameList returned != 2 names: %v", names)
|
||||
} else if names[0] != "miek." {
|
||||
t.Errorf("NameList didn't return search first: %v", names[0])
|
||||
} else if names[1] != "miek.test." {
|
||||
t.Errorf("NameList didn't return sent domain last: %v", names[1])
|
||||
}
|
||||
}
|
||||
|
||||
3
vendor/github.com/miekg/dns/compress_generate.go
сгенерированный
поставляемый
3
vendor/github.com/miekg/dns/compress_generate.go
сгенерированный
поставляемый
@@ -18,8 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var packageHdr = `
|
||||
// *** DO NOT MODIFY ***
|
||||
// AUTOGENERATED BY go generate from compress_generate.go
|
||||
// Code generated by "go run compress_generate.go"; DO NOT EDIT.
|
||||
|
||||
package dns
|
||||
|
||||
|
||||
7
vendor/github.com/miekg/dns/defaults.go
сгенерированный
поставляемый
7
vendor/github.com/miekg/dns/defaults.go
сгенерированный
поставляемый
@@ -273,8 +273,11 @@ func (t Type) String() string {
|
||||
|
||||
// String returns the string representation for the class c.
|
||||
func (c Class) String() string {
|
||||
if c1, ok := ClassToString[uint16(c)]; ok {
|
||||
return c1
|
||||
if s, ok := ClassToString[uint16(c)]; ok {
|
||||
// Only emit mnemonics when they are unambiguous, specically ANY is in both.
|
||||
if _, ok := StringToType[s]; !ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return "CLASS" + strconv.Itoa(int(c))
|
||||
}
|
||||
|
||||
61
vendor/github.com/miekg/dns/dns_test.go
сгенерированный
поставляемый
61
vendor/github.com/miekg/dns/dns_test.go
сгенерированный
поставляемый
@@ -1,6 +1,7 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"testing"
|
||||
@@ -317,8 +318,9 @@ func TestNoRdataPack(t *testing.T) {
|
||||
func TestNoRdataUnpack(t *testing.T) {
|
||||
data := make([]byte, 1024)
|
||||
for typ, fn := range TypeToRR {
|
||||
if typ == TypeSOA || typ == TypeTSIG {
|
||||
if typ == TypeSOA || typ == TypeTSIG || typ == TypeTKEY {
|
||||
// SOA, TSIG will not be seen (like this) in dyn. updates?
|
||||
// TKEY requires length fields to be present for the Key and OtherData fields
|
||||
continue
|
||||
}
|
||||
r := fn()
|
||||
@@ -405,3 +407,60 @@ func TestMsgPackBuffer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we can decode a TKEY packet from the string, modify the RR, and then pack it again.
|
||||
func TestTKEY(t *testing.T) {
|
||||
// An example TKEY RR captured. There is no known accepted standard text format for a TKEY
|
||||
// record so we do this from a hex string instead of from a text readable string.
|
||||
tkeyStr := "0737362d6d732d370932322d3332633233332463303439663961662d633065612d313165372d363839362d6463333937396666656666640000f900ff0000000000d2086773732d747369670059fd01f359fe53730003000000b8a181b53081b2a0030a0100a10b06092a864882f712010202a2819d04819a60819706092a864886f71201020202006f8187308184a003020105a10302010fa2783076a003020112a26f046db29b1b1d2625da3b20b49dafef930dd1e9aad335e1c5f45dcd95e0005d67a1100f3e573d70506659dbed064553f1ab890f68f65ae10def0dad5b423b39f240ebe666f2886c5fe03819692d29182bbed87b83e1f9d16b7334ec16a3c4fc5ad4a990088e0be43f0c6957916f5fe60000"
|
||||
tkeyBytes, err := hex.DecodeString(tkeyStr)
|
||||
if err != nil {
|
||||
t.Fatal("unable to decode TKEY string ", err)
|
||||
}
|
||||
// Decode the RR
|
||||
rr, tkeyLen, unPackErr := UnpackRR(tkeyBytes, 0)
|
||||
if unPackErr != nil {
|
||||
t.Fatal("unable to decode TKEY RR", unPackErr)
|
||||
}
|
||||
// Make sure it's a TKEY record
|
||||
if rr.Header().Rrtype != TypeTKEY {
|
||||
t.Fatal("Unable to decode TKEY")
|
||||
}
|
||||
// Make sure we get back the same length
|
||||
if rr.len() != len(tkeyBytes) {
|
||||
t.Fatalf("Lengths don't match %d != %d", rr.len(), len(tkeyBytes))
|
||||
}
|
||||
// make space for it with some fudge room
|
||||
msg := make([]byte, tkeyLen+1000)
|
||||
offset, packErr := PackRR(rr, msg, 0, nil, false)
|
||||
if packErr != nil {
|
||||
t.Fatal("unable to pack TKEY RR", packErr)
|
||||
}
|
||||
if offset != len(tkeyBytes) {
|
||||
t.Fatalf("mismatched TKEY RR size %d != %d", len(tkeyBytes), offset)
|
||||
}
|
||||
if bytes.Compare(tkeyBytes, msg[0:offset]) != 0 {
|
||||
t.Fatal("mismatched TKEY data after rewriting bytes")
|
||||
}
|
||||
t.Logf("got TKEY of: " + rr.String())
|
||||
// Now add some bytes to this and make sure we can encode OtherData properly
|
||||
tkey := rr.(*TKEY)
|
||||
tkey.OtherData = "abcd"
|
||||
tkey.OtherLen = 2
|
||||
offset, packErr = PackRR(tkey, msg, 0, nil, false)
|
||||
if packErr != nil {
|
||||
t.Fatal("unable to pack TKEY RR after modification", packErr)
|
||||
}
|
||||
if offset != (len(tkeyBytes) + 2) {
|
||||
t.Fatalf("mismatched TKEY RR size %d != %d", offset, len(tkeyBytes)+2)
|
||||
}
|
||||
t.Logf("modified to TKEY of: " + rr.String())
|
||||
|
||||
// Make sure we can parse our string output
|
||||
tkey.Hdr.Class = ClassINET // https://github.com/miekg/dns/issues/577
|
||||
newRR, newError := NewRR(tkey.String())
|
||||
if newError != nil {
|
||||
t.Fatalf("unable to parse TKEY string: %s", newError)
|
||||
}
|
||||
t.Log("got reparsed TKEY of newRR: " + newRR.String())
|
||||
}
|
||||
|
||||
80
vendor/github.com/miekg/dns/dnssec.go
сгенерированный
поставляемый
80
vendor/github.com/miekg/dns/dnssec.go
сгенерированный
поставляемый
@@ -19,6 +19,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// DNSSEC encryption algorithm codes.
|
||||
@@ -38,6 +40,8 @@ const (
|
||||
ECCGOST
|
||||
ECDSAP256SHA256
|
||||
ECDSAP384SHA384
|
||||
ED25519
|
||||
ED448
|
||||
INDIRECT uint8 = 252
|
||||
PRIVATEDNS uint8 = 253 // Private (experimental keys)
|
||||
PRIVATEOID uint8 = 254
|
||||
@@ -56,6 +60,8 @@ var AlgorithmToString = map[uint8]string{
|
||||
ECCGOST: "ECC-GOST",
|
||||
ECDSAP256SHA256: "ECDSAP256SHA256",
|
||||
ECDSAP384SHA384: "ECDSAP384SHA384",
|
||||
ED25519: "ED25519",
|
||||
ED448: "ED448",
|
||||
INDIRECT: "INDIRECT",
|
||||
PRIVATEDNS: "PRIVATEDNS",
|
||||
PRIVATEOID: "PRIVATEOID",
|
||||
@@ -73,6 +79,7 @@ var AlgorithmToHash = map[uint8]crypto.Hash{
|
||||
ECDSAP256SHA256: crypto.SHA256,
|
||||
ECDSAP384SHA384: crypto.SHA384,
|
||||
RSASHA512: crypto.SHA512,
|
||||
ED25519: crypto.Hash(0),
|
||||
}
|
||||
|
||||
// DNSSEC hashing algorithm codes.
|
||||
@@ -301,17 +308,33 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
|
||||
return ErrAlg
|
||||
}
|
||||
|
||||
h := hash.New()
|
||||
h.Write(signdata)
|
||||
h.Write(wire)
|
||||
switch rr.Algorithm {
|
||||
case ED25519:
|
||||
// ed25519 signs the raw message and performs hashing internally.
|
||||
// All other supported signature schemes operate over the pre-hashed
|
||||
// message, and thus ed25519 must be handled separately here.
|
||||
//
|
||||
// The raw message is passed directly into sign and crypto.Hash(0) is
|
||||
// used to signal to the crypto.Signer that the data has not been hashed.
|
||||
signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
rr.Signature = toBase64(signature)
|
||||
default:
|
||||
h := hash.New()
|
||||
h.Write(signdata)
|
||||
h.Write(wire)
|
||||
|
||||
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rr.Signature = toBase64(signature)
|
||||
}
|
||||
|
||||
rr.Signature = toBase64(signature)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -352,6 +375,9 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
|
||||
// signature = append(signature, intToBytes(r1, 20)...)
|
||||
// signature = append(signature, intToBytes(s1, 20)...)
|
||||
// rr.Signature = signature
|
||||
|
||||
case ED25519:
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
return nil, ErrAlg
|
||||
@@ -456,6 +482,17 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
||||
}
|
||||
return ErrSig
|
||||
|
||||
case ED25519:
|
||||
pubkey := k.publicKeyED25519()
|
||||
if pubkey == nil {
|
||||
return ErrKey
|
||||
}
|
||||
|
||||
if ed25519.Verify(pubkey, append(signeddata, wire...), sigbuf) {
|
||||
return nil
|
||||
}
|
||||
return ErrSig
|
||||
|
||||
default:
|
||||
return ErrAlg
|
||||
}
|
||||
@@ -578,6 +615,17 @@ func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey {
|
||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(keybuf) != ed25519.PublicKeySize {
|
||||
return nil
|
||||
}
|
||||
return keybuf
|
||||
}
|
||||
|
||||
type wireSlice [][]byte
|
||||
|
||||
func (p wireSlice) Len() int { return len(p) }
|
||||
@@ -615,6 +663,10 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
|
||||
switch x := r1.(type) {
|
||||
case *NS:
|
||||
x.Ns = strings.ToLower(x.Ns)
|
||||
case *MD:
|
||||
x.Md = strings.ToLower(x.Md)
|
||||
case *MF:
|
||||
x.Mf = strings.ToLower(x.Mf)
|
||||
case *CNAME:
|
||||
x.Target = strings.ToLower(x.Target)
|
||||
case *SOA:
|
||||
@@ -633,6 +685,18 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
|
||||
x.Email = strings.ToLower(x.Email)
|
||||
case *MX:
|
||||
x.Mx = strings.ToLower(x.Mx)
|
||||
case *RP:
|
||||
x.Mbox = strings.ToLower(x.Mbox)
|
||||
x.Txt = strings.ToLower(x.Txt)
|
||||
case *AFSDB:
|
||||
x.Hostname = strings.ToLower(x.Hostname)
|
||||
case *RT:
|
||||
x.Host = strings.ToLower(x.Host)
|
||||
case *SIG:
|
||||
x.SignerName = strings.ToLower(x.SignerName)
|
||||
case *PX:
|
||||
x.Map822 = strings.ToLower(x.Map822)
|
||||
x.Mapx400 = strings.ToLower(x.Mapx400)
|
||||
case *NAPTR:
|
||||
x.Replacement = strings.ToLower(x.Replacement)
|
||||
case *KX:
|
||||
|
||||
22
vendor/github.com/miekg/dns/dnssec_keygen.go
сгенерированный
поставляемый
22
vendor/github.com/miekg/dns/dnssec_keygen.go
сгенерированный
поставляемый
@@ -8,6 +8,8 @@ import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// Generate generates a DNSKEY of the given bit size.
|
||||
@@ -38,6 +40,10 @@ func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
|
||||
if bits != 384 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
case ED25519:
|
||||
if bits != 256 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
}
|
||||
|
||||
switch k.Algorithm {
|
||||
@@ -75,6 +81,13 @@ func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
|
||||
}
|
||||
k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)
|
||||
return priv, nil
|
||||
case ED25519:
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k.setPublicKeyED25519(pub)
|
||||
return priv, nil
|
||||
default:
|
||||
return nil, ErrAlg
|
||||
}
|
||||
@@ -117,6 +130,15 @@ func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Set the public key for Ed25519
|
||||
func (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool {
|
||||
if _K == nil {
|
||||
return false
|
||||
}
|
||||
k.PublicKey = toBase64(_K)
|
||||
return true
|
||||
}
|
||||
|
||||
// Set the public key (the values E and N) for RSA
|
||||
// RFC 3110: Section 2. RSA Public KEY Resource Records
|
||||
func exponentToBuf(_E int) []byte {
|
||||
|
||||
50
vendor/github.com/miekg/dns/dnssec_keyscan.go
сгенерированный
поставляемый
50
vendor/github.com/miekg/dns/dnssec_keyscan.go
сгенерированный
поставляемый
@@ -1,6 +1,7 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
@@ -9,6 +10,8 @@ import (
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// NewPrivateKey returns a PrivateKey by parsing the string s.
|
||||
@@ -86,6 +89,8 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, er
|
||||
}
|
||||
priv.PublicKey = *pub
|
||||
return priv, nil
|
||||
case ED25519:
|
||||
return readPrivateKeyED25519(m)
|
||||
default:
|
||||
return nil, ErrPrivKey
|
||||
}
|
||||
@@ -166,13 +171,56 @@ func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
|
||||
var p ed25519.PrivateKey
|
||||
// TODO: validate that the required flags are present
|
||||
for k, v := range m {
|
||||
switch k {
|
||||
case "privatekey":
|
||||
p1, err := fromBase64([]byte(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(p1) != 32 {
|
||||
return nil, ErrPrivKey
|
||||
}
|
||||
// RFC 8080 and Golang's x/crypto/ed25519 differ as to how the
|
||||
// private keys are represented. RFC 8080 specifies that private
|
||||
// keys be stored solely as the seed value (p1 above) while the
|
||||
// ed25519 package represents them as the seed value concatenated
|
||||
// to the public key, which is derived from the seed value.
|
||||
//
|
||||
// ed25519.GenerateKey reads exactly 32 bytes from the passed in
|
||||
// io.Reader and uses them as the seed. It also derives the
|
||||
// public key and produces a compatible private key.
|
||||
_, p, err = ed25519.GenerateKey(bytes.NewReader(p1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "created", "publish", "activate":
|
||||
/* not used in Go (yet) */
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// parseKey reads a private key from r. It returns a map[string]string,
|
||||
// with the key-value pairs, or an error when the file is not correct.
|
||||
func parseKey(r io.Reader, file string) (map[string]string, error) {
|
||||
s := scanInit(r)
|
||||
s, cancel := scanInit(r)
|
||||
m := make(map[string]string)
|
||||
c := make(chan lex)
|
||||
k := ""
|
||||
defer func() {
|
||||
cancel()
|
||||
// zlexer can send up to two tokens, the next one and possibly 1 remainders.
|
||||
// Do a non-blocking read.
|
||||
_, ok := <-c
|
||||
_, ok = <-c
|
||||
if !ok {
|
||||
// too bad
|
||||
}
|
||||
}()
|
||||
// Start the lexer
|
||||
go klexer(s, c)
|
||||
for l := range c {
|
||||
|
||||
8
vendor/github.com/miekg/dns/dnssec_privkey.go
сгенерированный
поставляемый
8
vendor/github.com/miekg/dns/dnssec_privkey.go
сгенерированный
поставляемый
@@ -7,6 +7,8 @@ import (
|
||||
"crypto/rsa"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
const format = "Private-key-format: v1.3\n"
|
||||
@@ -79,6 +81,12 @@ func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
|
||||
"Private_value(x): " + priv + "\n" +
|
||||
"Public_value(y): " + pub + "\n"
|
||||
|
||||
case ed25519.PrivateKey:
|
||||
private := toBase64(p[:32])
|
||||
return format +
|
||||
"Algorithm: " + algorithm + "\n" +
|
||||
"PrivateKey: " + private + "\n"
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
189
vendor/github.com/miekg/dns/dnssec_test.go
сгенерированный
поставляемый
189
vendor/github.com/miekg/dns/dnssec_test.go
сгенерированный
поставляемый
@@ -8,6 +8,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
func getKey() *DNSKEY {
|
||||
@@ -461,6 +463,55 @@ func TestSignVerifyECDSA2(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignVerifyEd25519(t *testing.T) {
|
||||
srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
srv := srv1.(*SRV)
|
||||
|
||||
// With this key
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Rrtype = TypeDNSKEY
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 14400
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = ED25519
|
||||
privkey, err := key.Generate(256)
|
||||
if err != nil {
|
||||
t.Fatal("failure to generate key")
|
||||
}
|
||||
|
||||
// Fill in the values of the Sig, before signing
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.TypeCovered = srv.Hdr.Rrtype
|
||||
sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3
|
||||
sig.OrigTtl = srv.Hdr.Ttl
|
||||
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
|
||||
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
|
||||
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
|
||||
sig.SignerName = key.Hdr.Name
|
||||
sig.Algorithm = ED25519
|
||||
|
||||
if sig.Sign(privkey.(ed25519.PrivateKey), []RR{srv}) != nil {
|
||||
t.Fatal("failure to sign the record")
|
||||
}
|
||||
|
||||
err = sig.Verify(key, []RR{srv})
|
||||
if err != nil {
|
||||
t.Logf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
|
||||
key.String(),
|
||||
srv.String(),
|
||||
sig.String(),
|
||||
key.PrivateKeyString(privkey),
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Here the test vectors from the relevant RFCs are checked.
|
||||
// rfc6605 6.1
|
||||
func TestRFC6605P256(t *testing.T) {
|
||||
@@ -590,6 +641,144 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
|
||||
}
|
||||
}
|
||||
|
||||
// rfc8080 6.1
|
||||
func TestRFC8080Ed25519Example1(t *testing.T) {
|
||||
exDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 (
|
||||
l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= )`
|
||||
exPriv := `Private-key-format: v1.2
|
||||
Algorithm: 15 (ED25519)
|
||||
PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=`
|
||||
rrDNSKEY, err := NewRR(exDNSKEY)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exDS := `example.com. 3600 IN DS 3613 15 2 (
|
||||
3aa5ab37efce57f737fc1627013fee07bdf241bd10f3b1964ab55c78e79
|
||||
a304b )`
|
||||
rrDS, err := NewRR(exDS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
|
||||
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
|
||||
t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
|
||||
}
|
||||
|
||||
exMX := `example.com. 3600 IN MX 10 mail.example.com.`
|
||||
exRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 (
|
||||
1440021600 1438207200 3613 example.com. (
|
||||
oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f
|
||||
x8A4M3e23mRZ9VrbpMngwcrqNAg== ) )`
|
||||
rrMX, err := NewRR(exMX)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rrRRSIG, err := NewRR(exRRSIG)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
|
||||
t.Errorf("failure to validate the spec RRSIG: %v", err)
|
||||
}
|
||||
|
||||
ourRRSIG := &RRSIG{
|
||||
Hdr: RR_Header{
|
||||
Ttl: rrMX.Header().Ttl,
|
||||
},
|
||||
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
|
||||
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
|
||||
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
|
||||
}
|
||||
ourRRSIG.Expiration, _ = StringToTime("20150819220000")
|
||||
ourRRSIG.Inception, _ = StringToTime("20150729220000")
|
||||
err = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
|
||||
t.Errorf("failure to validate our RRSIG: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
|
||||
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
|
||||
}
|
||||
}
|
||||
|
||||
// rfc8080 6.1
|
||||
func TestRFC8080Ed25519Example2(t *testing.T) {
|
||||
exDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 (
|
||||
zPnZ/QwEe7S8C5SPz2OfS5RR40ATk2/rYnE9xHIEijs= )`
|
||||
exPriv := `Private-key-format: v1.2
|
||||
Algorithm: 15 (ED25519)
|
||||
PrivateKey: DSSF3o0s0f+ElWzj9E/Osxw8hLpk55chkmx0LYN5WiY=`
|
||||
rrDNSKEY, err := NewRR(exDNSKEY)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exDS := `example.com. 3600 IN DS 35217 15 2 (
|
||||
401781b934e392de492ec77ae2e15d70f6575a1c0bc59c5275c04ebe80c
|
||||
6614c )`
|
||||
rrDS, err := NewRR(exDS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
|
||||
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
|
||||
t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
|
||||
}
|
||||
|
||||
exMX := `example.com. 3600 IN MX 10 mail.example.com.`
|
||||
exRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 (
|
||||
1440021600 1438207200 35217 example.com. (
|
||||
zXQ0bkYgQTEFyfLyi9QoiY6D8ZdYo4wyUhVioYZXFdT410QPRITQSqJSnzQ
|
||||
oSm5poJ7gD7AQR0O7KuI5k2pcBg== ) )`
|
||||
rrMX, err := NewRR(exMX)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rrRRSIG, err := NewRR(exRRSIG)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
|
||||
t.Errorf("failure to validate the spec RRSIG: %v", err)
|
||||
}
|
||||
|
||||
ourRRSIG := &RRSIG{
|
||||
Hdr: RR_Header{
|
||||
Ttl: rrMX.Header().Ttl,
|
||||
},
|
||||
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
|
||||
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
|
||||
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
|
||||
}
|
||||
ourRRSIG.Expiration, _ = StringToTime("20150819220000")
|
||||
ourRRSIG.Inception, _ = StringToTime("20150729220000")
|
||||
err = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
|
||||
t.Errorf("failure to validate our RRSIG: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
|
||||
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidRRSet(t *testing.T) {
|
||||
goodRecords := make([]RR, 2)
|
||||
goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
|
||||
2
vendor/github.com/miekg/dns/doc.go
сгенерированный
поставляемый
2
vendor/github.com/miekg/dns/doc.go
сгенерированный
поставляемый
@@ -63,7 +63,7 @@ class) is as easy as setting:
|
||||
|
||||
c.SingleInflight = true
|
||||
|
||||
More advanced options are availabe using a net.Dialer and the corresponding API.
|
||||
More advanced options are available using a net.Dialer and the corresponding API.
|
||||
For example it is possible to set a timeout, or to specify a source IP address
|
||||
and port to use for the connection:
|
||||
|
||||
|
||||
39
vendor/github.com/miekg/dns/edns.go
сгенерированный
поставляемый
39
vendor/github.com/miekg/dns/edns.go
сгенерированный
поставляемый
@@ -13,19 +13,18 @@ import (
|
||||
const (
|
||||
EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
||||
EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
|
||||
EDNS0NSID = 0x3 // nsid (RFC5001)
|
||||
EDNS0NSID = 0x3 // nsid (See RFC 5001)
|
||||
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
|
||||
EDNS0DHU = 0x6 // DS Hash Understood
|
||||
EDNS0N3U = 0x7 // NSEC3 Hash Understood
|
||||
EDNS0SUBNET = 0x8 // client-subnet (RFC6891)
|
||||
EDNS0SUBNET = 0x8 // client-subnet (See RFC 7871)
|
||||
EDNS0EXPIRE = 0x9 // EDNS0 expire
|
||||
EDNS0COOKIE = 0xa // EDNS0 Cookie
|
||||
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (RFC7828)
|
||||
EDNS0PADDING = 0xc // EDNS0 padding (RFC7830)
|
||||
EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
|
||||
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (RFC6891)
|
||||
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (RFC6891)
|
||||
_DO = 1 << 15 // dnssec ok
|
||||
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828)
|
||||
EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830)
|
||||
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891)
|
||||
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891)
|
||||
_DO = 1 << 15 // DNSSEC OK
|
||||
)
|
||||
|
||||
// OPT is the EDNS0 RR appended to messages to convey extra (meta) information.
|
||||
@@ -58,9 +57,6 @@ func (rr *OPT) String() string {
|
||||
}
|
||||
case *EDNS0_SUBNET:
|
||||
s += "\n; SUBNET: " + o.String()
|
||||
if o.(*EDNS0_SUBNET).DraftOption {
|
||||
s += " (draft)"
|
||||
}
|
||||
case *EDNS0_COOKIE:
|
||||
s += "\n; COOKIE: " + o.String()
|
||||
case *EDNS0_UL:
|
||||
@@ -188,7 +184,7 @@ func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); re
|
||||
func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
|
||||
|
||||
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
|
||||
// an idea of where the client lives. It can then give back a different
|
||||
// an idea of where the client lives. See RFC 7871. It can then give back a different
|
||||
// answer depending on the location or network topology.
|
||||
// Basic use pattern for creating an subnet option:
|
||||
//
|
||||
@@ -204,26 +200,19 @@ func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
|
||||
// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6
|
||||
// o.Option = append(o.Option, e)
|
||||
//
|
||||
// Note: the spec (draft-ietf-dnsop-edns-client-subnet-00) has some insane logic
|
||||
// for which netmask applies to the address. This code will parse all the
|
||||
// available bits when unpacking (up to optlen). When packing it will apply
|
||||
// SourceNetmask. If you need more advanced logic, patches welcome and good luck.
|
||||
// This code will parse all the available bits when unpacking (up to optlen).
|
||||
// When packing it will apply SourceNetmask. If you need more advanced logic,
|
||||
// patches welcome and good luck.
|
||||
type EDNS0_SUBNET struct {
|
||||
Code uint16 // Always EDNS0SUBNET
|
||||
Family uint16 // 1 for IP, 2 for IP6
|
||||
SourceNetmask uint8
|
||||
SourceScope uint8
|
||||
Address net.IP
|
||||
DraftOption bool // Set to true if using the old (0x50fa) option code
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_SUBNET) Option() uint16 {
|
||||
if e.DraftOption {
|
||||
return EDNS0SUBNETDRAFT
|
||||
}
|
||||
return EDNS0SUBNET
|
||||
}
|
||||
func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET }
|
||||
|
||||
func (e *EDNS0_SUBNET) pack() ([]byte, error) {
|
||||
b := make([]byte, 4)
|
||||
@@ -431,6 +420,7 @@ func (e *EDNS0_LLQ) String() string {
|
||||
return s
|
||||
}
|
||||
|
||||
// EDNS0_DUA implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975.
|
||||
type EDNS0_DAU struct {
|
||||
Code uint16 // Always EDNS0DAU
|
||||
AlgCode []uint8
|
||||
@@ -453,6 +443,7 @@ func (e *EDNS0_DAU) String() string {
|
||||
return s
|
||||
}
|
||||
|
||||
// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975.
|
||||
type EDNS0_DHU struct {
|
||||
Code uint16 // Always EDNS0DHU
|
||||
AlgCode []uint8
|
||||
@@ -475,6 +466,7 @@ func (e *EDNS0_DHU) String() string {
|
||||
return s
|
||||
}
|
||||
|
||||
// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975.
|
||||
type EDNS0_N3U struct {
|
||||
Code uint16 // Always EDNS0N3U
|
||||
AlgCode []uint8
|
||||
@@ -498,6 +490,7 @@ func (e *EDNS0_N3U) String() string {
|
||||
return s
|
||||
}
|
||||
|
||||
// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314.
|
||||
type EDNS0_EXPIRE struct {
|
||||
Code uint16 // Always EDNS0EXPIRE
|
||||
Expire uint32
|
||||
|
||||
23
vendor/github.com/miekg/dns/fuzz.go
сгенерированный
поставляемый
Обычный файл
23
vendor/github.com/miekg/dns/fuzz.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,23 @@
|
||||
// +build fuzz
|
||||
|
||||
package dns
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
msg := new(Msg)
|
||||
|
||||
if err := msg.Unpack(data); err != nil {
|
||||
return 0
|
||||
}
|
||||
if _, err := msg.Pack(); err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func FuzzNewRR(data []byte) int {
|
||||
if _, err := NewRR(string(data)); err != nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
25
vendor/github.com/miekg/dns/fuzz_test.go
сгенерированный
поставляемый
25
vendor/github.com/miekg/dns/fuzz_test.go
сгенерированный
поставляемый
@@ -1,25 +0,0 @@
|
||||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFuzzString(t *testing.T) {
|
||||
testcases := []string{"", " MINFO ", " RP ", " NSEC 0 0", " \" NSEC 0 0\"", " \" MINFO \"",
|
||||
";a ", ";a<><61><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
|
||||
" NSAP O ", " NSAP N ",
|
||||
" TYPE4 TYPE6a789a3bc0045c8a5fb42c7d1bd998f5444 IN 9579b47d46817afbd17273e6",
|
||||
" TYPE45 3 3 4147994 TYPE\\(\\)\\)\\(\\)\\(\\(\\)\\(\\)\\)\\)\\(\\)\\(\\)\\(\\(\\R 948\"\")\\(\\)\\)\\)\\(\\ ",
|
||||
"$GENERATE 0-3 ${441189,5039418474430,o}",
|
||||
"$INCLUDE 00 TYPE00000000000n ",
|
||||
"$INCLUDE PE4 TYPE061463623/727071511 \\(\\)\\$GENERATE 6-462/0",
|
||||
}
|
||||
for i, tc := range testcases {
|
||||
rr, err := NewRR(tc)
|
||||
if err == nil {
|
||||
// rr can be nil because we can (for instance) just parse a comment
|
||||
if rr == nil {
|
||||
continue
|
||||
}
|
||||
t.Fatalf("parsed mailformed RR %d: %s", i, rr.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
2346
vendor/github.com/miekg/dns/idn/code_points.go
сгенерированный
поставляемый
2346
vendor/github.com/miekg/dns/idn/code_points.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
18
vendor/github.com/miekg/dns/idn/example_test.go
сгенерированный
поставляемый
18
vendor/github.com/miekg/dns/idn/example_test.go
сгенерированный
поставляемый
@@ -1,18 +0,0 @@
|
||||
package idn_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/miekg/dns/idn"
|
||||
)
|
||||
|
||||
func ExampleToPunycode() {
|
||||
name := "インターネット.テスト"
|
||||
fmt.Printf("%s -> %s", name, idn.ToPunycode(name))
|
||||
// Output: インターネット.テスト -> xn--eckucmux0ukc.xn--zckzah
|
||||
}
|
||||
|
||||
func ExampleFromPunycode() {
|
||||
name := "xn--mgbaja8a1hpac.xn--mgbachtv"
|
||||
fmt.Printf("%s -> %s", name, idn.FromPunycode(name))
|
||||
// Output: xn--mgbaja8a1hpac.xn--mgbachtv -> الانترنت.اختبار
|
||||
}
|
||||
370
vendor/github.com/miekg/dns/idn/punycode.go
сгенерированный
поставляемый
370
vendor/github.com/miekg/dns/idn/punycode.go
сгенерированный
поставляемый
@@ -1,370 +0,0 @@
|
||||
// Package idn implements encoding from and to punycode as speficied by RFC 3492.
|
||||
package idn
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Implementation idea from RFC itself and from from IDNA::Punycode created by
|
||||
// Tatsuhiko Miyagawa <miyagawa@bulknews.net> and released under Perl Artistic
|
||||
// License in 2002.
|
||||
|
||||
const (
|
||||
_MIN rune = 1
|
||||
_MAX rune = 26
|
||||
_SKEW rune = 38
|
||||
_BASE rune = 36
|
||||
_BIAS rune = 72
|
||||
_N rune = 128
|
||||
_DAMP rune = 700
|
||||
|
||||
_DELIMITER = '-'
|
||||
_PREFIX = "xn--"
|
||||
)
|
||||
|
||||
// ToPunycode converts unicode domain names to DNS-appropriate punycode names.
|
||||
// This function will return an empty string result for domain names with
|
||||
// invalid unicode strings. This function expects domain names in lowercase.
|
||||
func ToPunycode(s string) string {
|
||||
// Early check to see if encoding is needed.
|
||||
// This will prevent making heap allocations when not needed.
|
||||
if !needToPunycode(s) {
|
||||
return s
|
||||
}
|
||||
|
||||
tokens := dns.SplitDomainName(s)
|
||||
switch {
|
||||
case s == "":
|
||||
return ""
|
||||
case tokens == nil: // s == .
|
||||
return "."
|
||||
case s[len(s)-1] == '.':
|
||||
tokens = append(tokens, "")
|
||||
}
|
||||
|
||||
for i := range tokens {
|
||||
t := encode([]byte(tokens[i]))
|
||||
if t == nil {
|
||||
return ""
|
||||
}
|
||||
tokens[i] = string(t)
|
||||
}
|
||||
return strings.Join(tokens, ".")
|
||||
}
|
||||
|
||||
// FromPunycode returns unicode domain name from provided punycode string.
|
||||
// This function expects punycode strings in lowercase.
|
||||
func FromPunycode(s string) string {
|
||||
// Early check to see if decoding is needed.
|
||||
// This will prevent making heap allocations when not needed.
|
||||
if !needFromPunycode(s) {
|
||||
return s
|
||||
}
|
||||
|
||||
tokens := dns.SplitDomainName(s)
|
||||
switch {
|
||||
case s == "":
|
||||
return ""
|
||||
case tokens == nil: // s == .
|
||||
return "."
|
||||
case s[len(s)-1] == '.':
|
||||
tokens = append(tokens, "")
|
||||
}
|
||||
for i := range tokens {
|
||||
tokens[i] = string(decode([]byte(tokens[i])))
|
||||
}
|
||||
return strings.Join(tokens, ".")
|
||||
}
|
||||
|
||||
// digitval converts single byte into meaningful value that's used to calculate decoded unicode character.
|
||||
const errdigit = 0xffff
|
||||
|
||||
func digitval(code rune) rune {
|
||||
switch {
|
||||
case code >= 'A' && code <= 'Z':
|
||||
return code - 'A'
|
||||
case code >= 'a' && code <= 'z':
|
||||
return code - 'a'
|
||||
case code >= '0' && code <= '9':
|
||||
return code - '0' + 26
|
||||
}
|
||||
return errdigit
|
||||
}
|
||||
|
||||
// lettercode finds BASE36 byte (a-z0-9) based on calculated number.
|
||||
func lettercode(digit rune) rune {
|
||||
switch {
|
||||
case digit >= 0 && digit <= 25:
|
||||
return digit + 'a'
|
||||
case digit >= 26 && digit <= 36:
|
||||
return digit - 26 + '0'
|
||||
}
|
||||
panic("dns: not reached")
|
||||
}
|
||||
|
||||
// adapt calculates next bias to be used for next iteration delta.
|
||||
func adapt(delta rune, numpoints int, firsttime bool) rune {
|
||||
if firsttime {
|
||||
delta /= _DAMP
|
||||
} else {
|
||||
delta /= 2
|
||||
}
|
||||
|
||||
var k rune
|
||||
for delta = delta + delta/rune(numpoints); delta > (_BASE-_MIN)*_MAX/2; k += _BASE {
|
||||
delta /= _BASE - _MIN
|
||||
}
|
||||
|
||||
return k + ((_BASE-_MIN+1)*delta)/(delta+_SKEW)
|
||||
}
|
||||
|
||||
// next finds minimal rune (one with lowest codepoint value) that should be equal or above boundary.
|
||||
func next(b []rune, boundary rune) rune {
|
||||
if len(b) == 0 {
|
||||
panic("dns: invalid set of runes to determine next one")
|
||||
}
|
||||
m := b[0]
|
||||
for _, x := range b[1:] {
|
||||
if x >= boundary && (m < boundary || x < m) {
|
||||
m = x
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// preprune converts unicode rune to lower case. At this time it's not
|
||||
// supporting all things described in RFCs.
|
||||
func preprune(r rune) rune {
|
||||
if unicode.IsUpper(r) {
|
||||
r = unicode.ToLower(r)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// tfunc is a function that helps calculate each character weight.
|
||||
func tfunc(k, bias rune) rune {
|
||||
switch {
|
||||
case k <= bias:
|
||||
return _MIN
|
||||
case k >= bias+_MAX:
|
||||
return _MAX
|
||||
}
|
||||
return k - bias
|
||||
}
|
||||
|
||||
// needToPunycode returns true for strings that require punycode encoding
|
||||
// (contain unicode characters).
|
||||
func needToPunycode(s string) bool {
|
||||
// This function is very similar to bytes.Runes. We don't use bytes.Runes
|
||||
// because it makes a heap allocation that's not needed here.
|
||||
for i := 0; len(s) > 0; i++ {
|
||||
r, l := utf8.DecodeRuneInString(s)
|
||||
if r > 0x7f {
|
||||
return true
|
||||
}
|
||||
s = s[l:]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// needFromPunycode returns true for strings that require punycode decoding.
|
||||
func needFromPunycode(s string) bool {
|
||||
if s == "." {
|
||||
return false
|
||||
}
|
||||
|
||||
off := 0
|
||||
end := false
|
||||
pl := len(_PREFIX)
|
||||
sl := len(s)
|
||||
|
||||
// If s starts with _PREFIX.
|
||||
if sl > pl && s[off:off+pl] == _PREFIX {
|
||||
return true
|
||||
}
|
||||
|
||||
for {
|
||||
// Find the part after the next ".".
|
||||
off, end = dns.NextLabel(s, off)
|
||||
if end {
|
||||
return false
|
||||
}
|
||||
// If this parts starts with _PREFIX.
|
||||
if sl-off > pl && s[off:off+pl] == _PREFIX {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// encode transforms Unicode input bytes (that represent DNS label) into
|
||||
// punycode bytestream. This function would return nil if there's an invalid
|
||||
// character in the label.
|
||||
func encode(input []byte) []byte {
|
||||
n, bias := _N, _BIAS
|
||||
|
||||
b := bytes.Runes(input)
|
||||
for i := range b {
|
||||
if !isValidRune(b[i]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b[i] = preprune(b[i])
|
||||
}
|
||||
|
||||
basic := make([]byte, 0, len(b))
|
||||
for _, ltr := range b {
|
||||
if ltr <= 0x7f {
|
||||
basic = append(basic, byte(ltr))
|
||||
}
|
||||
}
|
||||
basiclen := len(basic)
|
||||
fulllen := len(b)
|
||||
if basiclen == fulllen {
|
||||
return basic
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
out.WriteString(_PREFIX)
|
||||
if basiclen > 0 {
|
||||
out.Write(basic)
|
||||
out.WriteByte(_DELIMITER)
|
||||
}
|
||||
|
||||
var (
|
||||
ltr, nextltr rune
|
||||
delta, q rune // delta calculation (see rfc)
|
||||
t, k, cp rune // weight and codepoint calculation
|
||||
)
|
||||
|
||||
for h := basiclen; h < fulllen; n, delta = n+1, delta+1 {
|
||||
nextltr = next(b, n)
|
||||
delta, n = delta+(nextltr-n)*rune(h+1), nextltr
|
||||
|
||||
for _, ltr = range b {
|
||||
if ltr < n {
|
||||
delta++
|
||||
}
|
||||
if ltr == n {
|
||||
q = delta
|
||||
for k = _BASE; ; k += _BASE {
|
||||
t = tfunc(k, bias)
|
||||
if q < t {
|
||||
break
|
||||
}
|
||||
cp = t + ((q - t) % (_BASE - t))
|
||||
out.WriteRune(lettercode(cp))
|
||||
q = (q - t) / (_BASE - t)
|
||||
}
|
||||
|
||||
out.WriteRune(lettercode(q))
|
||||
|
||||
bias = adapt(delta, h+1, h == basiclen)
|
||||
h, delta = h+1, 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
// decode transforms punycode input bytes (that represent DNS label) into Unicode bytestream.
|
||||
func decode(b []byte) []byte {
|
||||
src := b // b would move and we need to keep it
|
||||
|
||||
n, bias := _N, _BIAS
|
||||
if !bytes.HasPrefix(b, []byte(_PREFIX)) {
|
||||
return b
|
||||
}
|
||||
out := make([]rune, 0, len(b))
|
||||
b = b[len(_PREFIX):]
|
||||
for pos := len(b) - 1; pos >= 0; pos-- {
|
||||
// only last delimiter is our interest
|
||||
if b[pos] == _DELIMITER {
|
||||
out = append(out, bytes.Runes(b[:pos])...)
|
||||
b = b[pos+1:] // trim source string
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return src
|
||||
}
|
||||
var (
|
||||
i, oldi, w rune
|
||||
ch byte
|
||||
t, digit rune
|
||||
ln int
|
||||
)
|
||||
|
||||
for i = 0; len(b) > 0; i++ {
|
||||
oldi, w = i, 1
|
||||
for k := _BASE; len(b) > 0; k += _BASE {
|
||||
ch, b = b[0], b[1:]
|
||||
digit = digitval(rune(ch))
|
||||
if digit == errdigit {
|
||||
return src
|
||||
}
|
||||
i += digit * w
|
||||
if i < 0 {
|
||||
// safety check for rune overflow
|
||||
return src
|
||||
}
|
||||
|
||||
t = tfunc(k, bias)
|
||||
if digit < t {
|
||||
break
|
||||
}
|
||||
|
||||
w *= _BASE - t
|
||||
}
|
||||
ln = len(out) + 1
|
||||
bias = adapt(i-oldi, ln, oldi == 0)
|
||||
n += i / rune(ln)
|
||||
i = i % rune(ln)
|
||||
// insert
|
||||
out = append(out, 0)
|
||||
copy(out[i+1:], out[i:])
|
||||
out[i] = n
|
||||
}
|
||||
|
||||
var ret bytes.Buffer
|
||||
for _, r := range out {
|
||||
ret.WriteRune(r)
|
||||
}
|
||||
return ret.Bytes()
|
||||
}
|
||||
|
||||
// isValidRune checks if the character is valid. We will look for the
|
||||
// character property in the code points list. For now we aren't checking special
|
||||
// rules in case of contextual property
|
||||
func isValidRune(r rune) bool {
|
||||
return findProperty(r) == propertyPVALID
|
||||
}
|
||||
|
||||
// findProperty will try to check the code point property of the given
|
||||
// character. It will use a binary search algorithm as we have a slice of
|
||||
// ordered ranges (average case performance O(log n))
|
||||
func findProperty(r rune) property {
|
||||
imin, imax := 0, len(codePoints)
|
||||
|
||||
for imax >= imin {
|
||||
imid := (imin + imax) / 2
|
||||
|
||||
codePoint := codePoints[imid]
|
||||
if (codePoint.start == r && codePoint.end == 0) || (codePoint.start <= r && codePoint.end >= r) {
|
||||
return codePoint.state
|
||||
}
|
||||
|
||||
if (codePoint.end > 0 && codePoint.end < r) || (codePoint.end == 0 && codePoint.start < r) {
|
||||
imin = imid + 1
|
||||
} else {
|
||||
imax = imid - 1
|
||||
}
|
||||
}
|
||||
|
||||
return propertyUnknown
|
||||
}
|
||||
116
vendor/github.com/miekg/dns/idn/punycode_test.go
сгенерированный
поставляемый
116
vendor/github.com/miekg/dns/idn/punycode_test.go
сгенерированный
поставляемый
@@ -1,116 +0,0 @@
|
||||
package idn
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testcases = [][2]string{
|
||||
{"", ""},
|
||||
{"a", "a"},
|
||||
{"a-b", "a-b"},
|
||||
{"a-b-c", "a-b-c"},
|
||||
{"abc", "abc"},
|
||||
{"я", "xn--41a"},
|
||||
{"zя", "xn--z-0ub"},
|
||||
{"яZ", "xn--z-zub"},
|
||||
{"а-я", "xn----7sb8g"},
|
||||
{"إختبار", "xn--kgbechtv"},
|
||||
{"آزمایشی", "xn--hgbk6aj7f53bba"},
|
||||
{"测试", "xn--0zwm56d"},
|
||||
{"測試", "xn--g6w251d"},
|
||||
{"испытание", "xn--80akhbyknj4f"},
|
||||
{"परीक्षा", "xn--11b5bs3a9aj6g"},
|
||||
{"δοκιμή", "xn--jxalpdlp"},
|
||||
{"테스트", "xn--9t4b11yi5a"},
|
||||
{"טעסט", "xn--deba0ad"},
|
||||
{"テスト", "xn--zckzah"},
|
||||
{"பரிட்சை", "xn--hlcj6aya9esc7a"},
|
||||
{"mamão-com-açúcar", "xn--mamo-com-acar-yeb1e6q"},
|
||||
{"σ", "xn--4xa"},
|
||||
}
|
||||
|
||||
func TestEncodeDecodePunycode(t *testing.T) {
|
||||
for _, tst := range testcases {
|
||||
enc := encode([]byte(tst[0]))
|
||||
if string(enc) != tst[1] {
|
||||
t.Errorf("%s encodeded as %s but should be %s", tst[0], enc, tst[1])
|
||||
}
|
||||
dec := decode([]byte(tst[1]))
|
||||
if string(dec) != strings.ToLower(tst[0]) {
|
||||
t.Errorf("%s decoded as %s but should be %s", tst[1], dec, strings.ToLower(tst[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToFromPunycode(t *testing.T) {
|
||||
for _, tst := range testcases {
|
||||
// assert unicode.com == punycode.com
|
||||
full := ToPunycode(tst[0] + ".com")
|
||||
if full != tst[1]+".com" {
|
||||
t.Errorf("invalid result from string conversion to punycode, %s and should be %s.com", full, tst[1])
|
||||
}
|
||||
// assert punycode.punycode == unicode.unicode
|
||||
decoded := FromPunycode(tst[1] + "." + tst[1])
|
||||
if decoded != strings.ToLower(tst[0]+"."+tst[0]) {
|
||||
t.Errorf("invalid result from string conversion to punycode, %s and should be %s.%s", decoded, tst[0], tst[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeFinalPeriod(t *testing.T) {
|
||||
for _, tst := range testcases {
|
||||
// assert unicode.com. == punycode.com.
|
||||
full := ToPunycode(tst[0] + ".")
|
||||
if full != tst[1]+"." {
|
||||
t.Errorf("invalid result from string conversion to punycode when period added at the end, %#v and should be %#v", full, tst[1]+".")
|
||||
}
|
||||
// assert punycode.com. == unicode.com.
|
||||
decoded := FromPunycode(tst[1] + ".")
|
||||
if decoded != strings.ToLower(tst[0]+".") {
|
||||
t.Errorf("invalid result from string conversion to punycode when period added, %#v and should be %#v", decoded, tst[0]+".")
|
||||
}
|
||||
full = ToPunycode(tst[0])
|
||||
if full != tst[1] {
|
||||
t.Errorf("invalid result from string conversion to punycode when no period added at the end, %#v and should be %#v", full, tst[1]+".")
|
||||
}
|
||||
// assert punycode.com. == unicode.com.
|
||||
decoded = FromPunycode(tst[1])
|
||||
if decoded != strings.ToLower(tst[0]) {
|
||||
t.Errorf("invalid result from string conversion to punycode when no period added, %#v and should be %#v", decoded, tst[0]+".")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var invalidACEs = []string{
|
||||
"xn--*",
|
||||
"xn--",
|
||||
"xn---",
|
||||
"xn--a000000000",
|
||||
}
|
||||
|
||||
func TestInvalidPunycode(t *testing.T) {
|
||||
for _, d := range invalidACEs {
|
||||
s := FromPunycode(d)
|
||||
if s != d {
|
||||
t.Errorf("Changed invalid name %s to %#v", d, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// You can verify the labels that are valid or not comparing to the Verisign
|
||||
// website: http://mct.verisign-grs.com/
|
||||
var invalidUnicodes = []string{
|
||||
"Σ",
|
||||
"ЯZ",
|
||||
"Испытание",
|
||||
}
|
||||
|
||||
func TestInvalidUnicodes(t *testing.T) {
|
||||
for _, d := range invalidUnicodes {
|
||||
s := ToPunycode(d)
|
||||
if s != "" {
|
||||
t.Errorf("Changed invalid name %s to %#v", d, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
7
vendor/github.com/miekg/dns/internal/socket/cmsghdr.go
сгенерированный
поставляемый
7
vendor/github.com/miekg/dns/internal/socket/cmsghdr.go
сгенерированный
поставляемый
@@ -1,7 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
func (h *cmsghdr) len() int { return int(h.Len) }
|
||||
func (h *cmsghdr) lvl() int { return int(h.Level) }
|
||||
func (h *cmsghdr) typ() int { return int(h.Type) }
|
||||
118
vendor/github.com/miekg/dns/internal/socket/controlmessage.go
сгенерированный
поставляемый
118
vendor/github.com/miekg/dns/internal/socket/controlmessage.go
сгенерированный
поставляемый
@@ -1,118 +0,0 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func controlHeaderLen() int {
|
||||
return roundup(sizeofCmsghdr)
|
||||
}
|
||||
|
||||
func controlMessageLen(dataLen int) int {
|
||||
return roundup(sizeofCmsghdr) + dataLen
|
||||
}
|
||||
|
||||
// returns the whole length of control message.
|
||||
func ControlMessageSpace(dataLen int) int {
|
||||
return roundup(sizeofCmsghdr) + roundup(dataLen)
|
||||
}
|
||||
|
||||
// A ControlMessage represents the head message in a stream of control
|
||||
// messages.
|
||||
//
|
||||
// A control message comprises of a header, data and a few padding
|
||||
// fields to conform to the interface to the kernel.
|
||||
//
|
||||
// See RFC 3542 for further information.
|
||||
type ControlMessage []byte
|
||||
|
||||
// Data returns the data field of the control message at the head.
|
||||
func (m ControlMessage) Data(dataLen int) []byte {
|
||||
l := controlHeaderLen()
|
||||
if len(m) < l || len(m) < l+dataLen {
|
||||
return nil
|
||||
}
|
||||
return m[l : l+dataLen]
|
||||
}
|
||||
|
||||
// ParseHeader parses and returns the header fields of the control
|
||||
// message at the head.
|
||||
func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
|
||||
l := controlHeaderLen()
|
||||
if len(m) < l {
|
||||
return 0, 0, 0, errors.New("short message")
|
||||
}
|
||||
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
|
||||
return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil
|
||||
}
|
||||
|
||||
// Next returns the control message at the next.
|
||||
func (m ControlMessage) Next(dataLen int) ControlMessage {
|
||||
l := ControlMessageSpace(dataLen)
|
||||
if len(m) < l {
|
||||
return nil
|
||||
}
|
||||
return m[l:]
|
||||
}
|
||||
|
||||
// MarshalHeader marshals the header fields of the control message at
|
||||
// the head.
|
||||
func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
|
||||
if len(m) < controlHeaderLen() {
|
||||
return errors.New("short message")
|
||||
}
|
||||
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
|
||||
h.set(controlMessageLen(dataLen), lvl, typ)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal marshals the control message at the head, and returns the next
|
||||
// control message.
|
||||
func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) {
|
||||
l := len(data)
|
||||
if len(m) < ControlMessageSpace(l) {
|
||||
return nil, errors.New("short message")
|
||||
}
|
||||
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
|
||||
h.set(controlMessageLen(l), lvl, typ)
|
||||
if l > 0 {
|
||||
copy(m.Data(l), data)
|
||||
}
|
||||
return m.Next(l), nil
|
||||
}
|
||||
|
||||
// Parse parses as a single or multiple control messages.
|
||||
func (m ControlMessage) Parse() ([]ControlMessage, error) {
|
||||
var ms []ControlMessage
|
||||
for len(m) >= controlHeaderLen() {
|
||||
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
|
||||
l := h.len()
|
||||
if l <= 0 {
|
||||
return nil, errors.New("invalid header length")
|
||||
}
|
||||
if uint64(l) < uint64(controlHeaderLen()) {
|
||||
return nil, errors.New("invalid message length")
|
||||
}
|
||||
if uint64(l) > uint64(len(m)) {
|
||||
return nil, errors.New("short buffer")
|
||||
}
|
||||
ms = append(ms, ControlMessage(m[:l]))
|
||||
ll := l - controlHeaderLen()
|
||||
if len(m) >= ControlMessageSpace(ll) {
|
||||
m = m[ControlMessageSpace(ll):]
|
||||
} else {
|
||||
m = m[controlMessageLen(ll):]
|
||||
}
|
||||
}
|
||||
return ms, nil
|
||||
}
|
||||
|
||||
// NewControlMessage returns a new stream of control messages.
|
||||
func NewControlMessage(dataLen []int) ControlMessage {
|
||||
var l int
|
||||
for i := range dataLen {
|
||||
l += ControlMessageSpace(dataLen[i])
|
||||
}
|
||||
return make([]byte, l)
|
||||
}
|
||||
103
vendor/github.com/miekg/dns/internal/socket/controlmessage_test.go
сгенерированный
поставляемый
103
vendor/github.com/miekg/dns/internal/socket/controlmessage_test.go
сгенерированный
поставляемый
@@ -1,103 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type mockControl struct {
|
||||
Level int
|
||||
Type int
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func TestControlMessage(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
cs []mockControl
|
||||
}{
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 1, Type: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 2, Type: 2, Data: []byte{0xfe}},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 3, Type: 3, Data: []byte{0xfe, 0xff, 0xff, 0xfe}},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
|
||||
{Level: 2, Type: 2, Data: []byte{0xfe}},
|
||||
},
|
||||
},
|
||||
} {
|
||||
var w []byte
|
||||
var tailPadLen int
|
||||
mm := NewControlMessage([]int{0})
|
||||
for i, c := range tt.cs {
|
||||
m := NewControlMessage([]int{len(c.Data)})
|
||||
l := len(m) - len(mm)
|
||||
if i == len(tt.cs)-1 && l > len(c.Data) {
|
||||
tailPadLen = l - len(c.Data)
|
||||
}
|
||||
w = append(w, m...)
|
||||
}
|
||||
|
||||
var err error
|
||||
ww := make([]byte, len(w))
|
||||
copy(ww, w)
|
||||
m := ControlMessage(ww)
|
||||
for _, c := range tt.cs {
|
||||
if err = m.MarshalHeader(c.Level, c.Type, len(c.Data)); err != nil {
|
||||
t.Fatalf("(%v).MarshalHeader() = %v", tt.cs, err)
|
||||
}
|
||||
copy(m.Data(len(c.Data)), c.Data)
|
||||
m = m.Next(len(c.Data))
|
||||
}
|
||||
m = ControlMessage(w)
|
||||
for _, c := range tt.cs {
|
||||
m, err = m.Marshal(c.Level, c.Type, c.Data)
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).Marshal() = %v", tt.cs, err)
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(ww, w) {
|
||||
t.Fatalf("got %#v; want %#v", ww, w)
|
||||
}
|
||||
|
||||
ws := [][]byte{w}
|
||||
if tailPadLen > 0 {
|
||||
// Test a message with no tail padding.
|
||||
nopad := w[:len(w)-tailPadLen]
|
||||
ws = append(ws, [][]byte{nopad}...)
|
||||
}
|
||||
for _, w := range ws {
|
||||
ms, err := ControlMessage(w).Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).Parse() = %v", tt.cs, err)
|
||||
}
|
||||
for i, m := range ms {
|
||||
lvl, typ, dataLen, err := m.ParseHeader()
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).ParseHeader() = %v", tt.cs, err)
|
||||
}
|
||||
if lvl != tt.cs[i].Level || typ != tt.cs[i].Type || dataLen != len(tt.cs[i].Data) {
|
||||
t.Fatalf("%v: got %d, %d, %d; want %d, %d, %d", tt.cs[i], lvl, typ, dataLen, tt.cs[i].Level, tt.cs[i].Type, len(tt.cs[i].Data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
vendor/github.com/miekg/dns/internal/socket/socket.go
сгенерированный
поставляемый
4
vendor/github.com/miekg/dns/internal/socket/socket.go
сгенерированный
поставляемый
@@ -1,4 +0,0 @@
|
||||
// Package socket contains ControlMessage parsing code from
|
||||
// golang.org/x/net/internal/socket. Instead of supporting all possible
|
||||
// architectures, we're only supporting linux 32/64 bit.
|
||||
package socket
|
||||
14
vendor/github.com/miekg/dns/internal/socket/sys.go
сгенерированный
поставляемый
14
vendor/github.com/miekg/dns/internal/socket/sys.go
сгенерированный
поставляемый
@@ -1,14 +0,0 @@
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var (
|
||||
kernelAlign = func() int {
|
||||
var p uintptr
|
||||
return int(unsafe.Sizeof(p))
|
||||
}()
|
||||
)
|
||||
|
||||
func roundup(l int) int {
|
||||
return (l + kernelAlign - 1) & ^(kernelAlign - 1)
|
||||
}
|
||||
71
vendor/github.com/miekg/dns/leak_test.go
сгенерированный
поставляемый
Обычный файл
71
vendor/github.com/miekg/dns/leak_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,71 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// copied from net/http/main_test.go
|
||||
|
||||
func interestingGoroutines() (gs []string) {
|
||||
buf := make([]byte, 2<<20)
|
||||
buf = buf[:runtime.Stack(buf, true)]
|
||||
for _, g := range strings.Split(string(buf), "\n\n") {
|
||||
sl := strings.SplitN(g, "\n", 2)
|
||||
if len(sl) != 2 {
|
||||
continue
|
||||
}
|
||||
stack := strings.TrimSpace(sl[1])
|
||||
if stack == "" ||
|
||||
strings.Contains(stack, "testing.(*M).before.func1") ||
|
||||
strings.Contains(stack, "os/signal.signal_recv") ||
|
||||
strings.Contains(stack, "created by net.startServer") ||
|
||||
strings.Contains(stack, "created by testing.RunTests") ||
|
||||
strings.Contains(stack, "closeWriteAndWait") ||
|
||||
strings.Contains(stack, "testing.Main(") ||
|
||||
strings.Contains(stack, "testing.(*T).Run(") ||
|
||||
// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
|
||||
strings.Contains(stack, "runtime.goexit") ||
|
||||
strings.Contains(stack, "created by runtime.gc") ||
|
||||
strings.Contains(stack, "dns.interestingGoroutines") ||
|
||||
strings.Contains(stack, "runtime.MHeap_Scavenger") {
|
||||
continue
|
||||
}
|
||||
gs = append(gs, stack)
|
||||
}
|
||||
sort.Strings(gs)
|
||||
return
|
||||
}
|
||||
|
||||
func goroutineLeaked() error {
|
||||
if testing.Short() {
|
||||
// Don't worry about goroutine leaks in -short mode or in
|
||||
// benchmark mode. Too distracting when there are false positives.
|
||||
return nil
|
||||
}
|
||||
|
||||
var stackCount map[string]int
|
||||
for i := 0; i < 5; i++ {
|
||||
n := 0
|
||||
stackCount = make(map[string]int)
|
||||
gs := interestingGoroutines()
|
||||
for _, g := range gs {
|
||||
stackCount[g]++
|
||||
n++
|
||||
}
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
// Wait for goroutines to schedule and die off:
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
for stack, count := range stackCount {
|
||||
fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
|
||||
}
|
||||
return fmt.Errorf("too many goroutines running after dns test(s)")
|
||||
}
|
||||
18
vendor/github.com/miekg/dns/msg.go
сгенерированный
поставляемый
18
vendor/github.com/miekg/dns/msg.go
сгенерированный
поставляемый
@@ -612,8 +612,8 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
|
||||
// If we cannot unpack the whole array, then it will return nil
|
||||
func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) {
|
||||
var r RR
|
||||
// Optimistically make dst be the length that was sent
|
||||
dst := make([]RR, 0, l)
|
||||
// Don't pre-allocate, l may be under attacker control
|
||||
var dst []RR
|
||||
for i := 0; i < l; i++ {
|
||||
off1 := off
|
||||
r, off, err = UnpackRR(msg, off)
|
||||
@@ -811,13 +811,19 @@ func (dns *Msg) Unpack(msg []byte) (err error) {
|
||||
dns.CheckingDisabled = (dh.Bits & _CD) != 0
|
||||
dns.Rcode = int(dh.Bits & 0xF)
|
||||
|
||||
// If we are at the end of the message we should return *just* the
|
||||
// header. This can still be useful to the caller. 9.9.9.9 sends these
|
||||
// when responding with REFUSED for instance.
|
||||
if off == len(msg) {
|
||||
return ErrTruncated
|
||||
// reset sections before returning
|
||||
dns.Question, dns.Answer, dns.Ns, dns.Extra = nil, nil, nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Optimistically use the count given to us in the header
|
||||
dns.Question = make([]Question, 0, int(dh.Qdcount))
|
||||
|
||||
// Qdcount, Ancount, Nscount, Arcount can't be trusted, as they are
|
||||
// attacker controlled. This means we can't use them to pre-allocate
|
||||
// slices.
|
||||
dns.Question = nil
|
||||
for i := 0; i < int(dh.Qdcount); i++ {
|
||||
off1 := off
|
||||
var q Question
|
||||
|
||||
3
vendor/github.com/miekg/dns/msg_generate.go
сгенерированный
поставляемый
3
vendor/github.com/miekg/dns/msg_generate.go
сгенерированный
поставляемый
@@ -18,8 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var packageHdr = `
|
||||
// *** DO NOT MODIFY ***
|
||||
// AUTOGENERATED BY go generate from msg_generate.go
|
||||
// Code generated by "go run msg_generate.go"; DO NOT EDIT.
|
||||
|
||||
package dns
|
||||
|
||||
|
||||
5
vendor/github.com/miekg/dns/msg_helpers.go
сгенерированный
поставляемый
5
vendor/github.com/miekg/dns/msg_helpers.go
сгенерированный
поставляемый
@@ -406,16 +406,13 @@ Option:
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0SUBNET, EDNS0SUBNETDRAFT:
|
||||
case EDNS0SUBNET:
|
||||
e := new(EDNS0_SUBNET)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
if code == EDNS0SUBNETDRAFT {
|
||||
e.DraftOption = true
|
||||
}
|
||||
case EDNS0COOKIE:
|
||||
e := new(EDNS0_COOKIE)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
|
||||
53
vendor/github.com/miekg/dns/parse_test.go
сгенерированный
поставляемый
53
vendor/github.com/miekg/dns/parse_test.go
сгенерированный
поставляемый
@@ -145,7 +145,7 @@ func TestTXTEscapeParsing(t *testing.T) {
|
||||
}
|
||||
|
||||
func GenerateDomain(r *rand.Rand, size int) []byte {
|
||||
dnLen := size % 70 // artificially limit size so there's less to intrepret if a failure occurs
|
||||
dnLen := size % 70 // artificially limit size so there's less to interpret if a failure occurs
|
||||
var dn []byte
|
||||
done := false
|
||||
for i := 0; i < dnLen && !done; {
|
||||
@@ -203,7 +203,7 @@ func TestDomainQuick(t *testing.T) {
|
||||
}
|
||||
|
||||
func GenerateTXT(r *rand.Rand, size int) []byte {
|
||||
rdLen := size % 300 // artificially limit size so there's less to intrepret if a failure occurs
|
||||
rdLen := size % 300 // artificially limit size so there's less to interpret if a failure occurs
|
||||
var rd []byte
|
||||
for i := 0; i < rdLen; {
|
||||
max := rdLen - 1
|
||||
@@ -433,6 +433,7 @@ func TestParseClass(t *testing.T) {
|
||||
// ClassANY can not occur in zone files
|
||||
// "t.example.com. ANY A 127.0.0.1": "t.example.com. 3600 ANY A 127.0.0.1",
|
||||
"t.example.com. NONE A 127.0.0.1": "t.example.com. 3600 NONE A 127.0.0.1",
|
||||
"t.example.com. CLASS255 A 127.0.0.1": "t.example.com. 3600 CLASS255 A 127.0.0.1",
|
||||
}
|
||||
for i, o := range tests {
|
||||
rr, err := NewRR(i)
|
||||
@@ -1104,6 +1105,7 @@ func TestNewPrivateKey(t *testing.T) {
|
||||
{RSASHA1, 1024},
|
||||
{RSASHA256, 2048},
|
||||
{DSA, 1024},
|
||||
{ED25519, 256},
|
||||
}
|
||||
|
||||
for _, algo := range algorithms {
|
||||
@@ -1406,6 +1408,34 @@ func TestParseAVC(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCSYNC(t *testing.T) {
|
||||
syncs := map[string]string{
|
||||
`example.com. 3600 IN CSYNC 66 3 A NS AAAA`: `example.com. 3600 IN CSYNC 66 3 A NS AAAA`,
|
||||
}
|
||||
for s, o := range syncs {
|
||||
rr, err := NewRR(s)
|
||||
if err != nil {
|
||||
t.Error("failed to parse RR: ", err)
|
||||
continue
|
||||
}
|
||||
if rr.String() != o {
|
||||
t.Errorf("`%s' should be equal to\n`%s', but is `%s'", s, o, rr.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBadNAPTR(t *testing.T) {
|
||||
// Should look like: mplus.ims.vodafone.com. 3600 IN NAPTR 10 100 "S" "SIP+D2U" "" _sip._udp.mplus.ims.vodafone.com.
|
||||
naptr := `mplus.ims.vodafone.com. 3600 IN NAPTR 10 100 S SIP+D2U _sip._udp.mplus.ims.vodafone.com.`
|
||||
_, err := NewRR(naptr) // parse fails, we should not have leaked a goroutine.
|
||||
if err == nil {
|
||||
t.Fatalf("parsing NAPTR should have failed: %s", naptr)
|
||||
}
|
||||
if err := goroutineLeaked(); err != nil {
|
||||
t.Errorf("leaked goroutines: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnbalancedParens(t *testing.T) {
|
||||
sig := `example.com. 3600 IN RRSIG MX 15 2 3600 (
|
||||
1440021600 1438207200 3613 example.com. (
|
||||
@@ -1413,6 +1443,23 @@ func TestUnbalancedParens(t *testing.T) {
|
||||
x8A4M3e23mRZ9VrbpMngwcrqNAg== )`
|
||||
_, err := NewRR(sig)
|
||||
if err == nil {
|
||||
t.Fatalf("Failed to detect extra opening brace")
|
||||
t.Fatalf("failed to detect extra opening brace")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBad(t *testing.T) {
|
||||
tests := []string{
|
||||
`" TYPE257 9 1E12\x00\x105"`,
|
||||
`" TYPE256 9 5"`,
|
||||
`" TYPE257 0\"00000000000000400000000000000000000\x00\x10000000000000000000000000000000000 9 l\x16\x01\x005266"`,
|
||||
}
|
||||
for i := range tests {
|
||||
s, err := strconv.Unquote(tests[i])
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unquote: %q: %s", tests[i], err)
|
||||
}
|
||||
if _, err = NewRR(s); err == nil {
|
||||
t.Errorf("correctly parsed %q", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/github.com/miekg/dns/sanitize.go
сгенерированный
поставляемый
2
vendor/github.com/miekg/dns/sanitize.go
сгенерированный
поставляемый
@@ -3,7 +3,7 @@ package dns
|
||||
// Dedup removes identical RRs from rrs. It preserves the original ordering.
|
||||
// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
|
||||
// rrs.
|
||||
// m is used to store the RRs temporay. If it is nil a new map will be allocated.
|
||||
// m is used to store the RRs temporary. If it is nil a new map will be allocated.
|
||||
func Dedup(rrs []RR, m map[string]RR) []RR {
|
||||
if m == nil {
|
||||
m = make(map[string]RR)
|
||||
|
||||
66
vendor/github.com/miekg/dns/scan.go
сгенерированный
поставляемый
66
vendor/github.com/miekg/dns/scan.go
сгенерированный
поставляемый
@@ -1,23 +1,14 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type debugging bool
|
||||
|
||||
const debug debugging = false
|
||||
|
||||
func (d debugging) Printf(format string, args ...interface{}) {
|
||||
if d {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
const maxTok = 2048 // Largest token we can return.
|
||||
const maxUint16 = 1<<16 - 1
|
||||
|
||||
@@ -127,7 +118,7 @@ func NewRR(s string) (RR, error) {
|
||||
// See NewRR for more documentation.
|
||||
func ReadRR(q io.Reader, filename string) (RR, error) {
|
||||
defttl := &ttlState{defaultTtl, false}
|
||||
r := <-parseZoneHelper(q, ".", defttl, filename, 1)
|
||||
r := <-parseZoneHelper(q, ".", filename, defttl, 1)
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -164,25 +155,37 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
|
||||
// The text "; this is comment" is returned in Token.Comment. Comments inside the
|
||||
// RR are discarded. Comments on a line by themselves are discarded too.
|
||||
func ParseZone(r io.Reader, origin, file string) chan *Token {
|
||||
return parseZoneHelper(r, origin, nil, file, 10000)
|
||||
return parseZoneHelper(r, origin, file, nil, 10000)
|
||||
}
|
||||
|
||||
func parseZoneHelper(r io.Reader, origin string, defttl *ttlState, file string, chansize int) chan *Token {
|
||||
func parseZoneHelper(r io.Reader, origin, file string, defttl *ttlState, chansize int) chan *Token {
|
||||
t := make(chan *Token, chansize)
|
||||
go parseZone(r, origin, defttl, file, t, 0)
|
||||
go parseZone(r, origin, file, defttl, t, 0)
|
||||
return t
|
||||
}
|
||||
|
||||
func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *Token, include int) {
|
||||
func parseZone(r io.Reader, origin, f string, defttl *ttlState, t chan *Token, include int) {
|
||||
defer func() {
|
||||
if include == 0 {
|
||||
close(t)
|
||||
}
|
||||
}()
|
||||
s := scanInit(r)
|
||||
s, cancel := scanInit(r)
|
||||
c := make(chan lex)
|
||||
// Start the lexer
|
||||
go zlexer(s, c)
|
||||
|
||||
defer func() {
|
||||
cancel()
|
||||
// zlexer can send up to three tokens, the next one and possibly 2 remainders.
|
||||
// Do a non-blocking read.
|
||||
_, ok := <-c
|
||||
_, ok = <-c
|
||||
_, ok = <-c
|
||||
if !ok {
|
||||
// too bad
|
||||
}
|
||||
}()
|
||||
// 6 possible beginnings of a line, _ is a space
|
||||
// 0. zRRTYPE -> all omitted until the rrtype
|
||||
// 1. zOwner _ zRrtype -> class/ttl omitted
|
||||
@@ -296,16 +299,24 @@ func parseZone(r io.Reader, origin string, defttl *ttlState, f string, t chan *T
|
||||
return
|
||||
}
|
||||
// Start with the new file
|
||||
r1, e1 := os.Open(l.token)
|
||||
includePath := l.token
|
||||
if !filepath.IsAbs(includePath) {
|
||||
includePath = filepath.Join(filepath.Dir(f), includePath)
|
||||
}
|
||||
r1, e1 := os.Open(includePath)
|
||||
if e1 != nil {
|
||||
t <- &Token{Error: &ParseError{f, "failed to open `" + l.token + "'", l}}
|
||||
msg := fmt.Sprintf("failed to open `%s'", l.token)
|
||||
if !filepath.IsAbs(l.token) {
|
||||
msg += fmt.Sprintf(" as `%s'", includePath)
|
||||
}
|
||||
t <- &Token{Error: &ParseError{f, msg, l}}
|
||||
return
|
||||
}
|
||||
if include+1 > 7 {
|
||||
t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
|
||||
return
|
||||
}
|
||||
parseZone(r1, neworigin, defttl, l.token, t, include+1)
|
||||
parseZone(r1, neworigin, includePath, defttl, t, include+1)
|
||||
st = zExpectOwnerDir
|
||||
case zExpectDirTTLBl:
|
||||
if l.value != zBlank {
|
||||
@@ -497,14 +508,12 @@ func zlexer(s *scan, c chan lex) {
|
||||
if stri >= maxTok {
|
||||
l.token = "token length insufficient for parsing"
|
||||
l.err = true
|
||||
debug.Printf("[%+v]", l.token)
|
||||
c <- l
|
||||
return
|
||||
}
|
||||
if comi >= maxTok {
|
||||
l.token = "comment length insufficient for parsing"
|
||||
l.err = true
|
||||
debug.Printf("[%+v]", l.token)
|
||||
c <- l
|
||||
return
|
||||
}
|
||||
@@ -547,7 +556,6 @@ func zlexer(s *scan, c chan lex) {
|
||||
case "$GENERATE":
|
||||
l.value = zDirGenerate
|
||||
}
|
||||
debug.Printf("[7 %+v]", l.token)
|
||||
c <- l
|
||||
} else {
|
||||
l.value = zString
|
||||
@@ -569,6 +577,7 @@ func zlexer(s *scan, c chan lex) {
|
||||
return
|
||||
}
|
||||
l.value = zRrtpe
|
||||
rrtype = true
|
||||
l.torc = t
|
||||
}
|
||||
}
|
||||
@@ -589,16 +598,14 @@ func zlexer(s *scan, c chan lex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
debug.Printf("[6 %+v]", l.token)
|
||||
c <- l
|
||||
}
|
||||
stri = 0
|
||||
// I reverse space stuff here
|
||||
|
||||
if !space && !commt {
|
||||
l.value = zBlank
|
||||
l.token = " "
|
||||
l.length = 1
|
||||
debug.Printf("[5 %+v]", l.token)
|
||||
c <- l
|
||||
}
|
||||
owner = false
|
||||
@@ -621,7 +628,6 @@ func zlexer(s *scan, c chan lex) {
|
||||
l.token = string(str[:stri])
|
||||
l.tokenUpper = strings.ToUpper(l.token)
|
||||
l.length = stri
|
||||
debug.Printf("[4 %+v]", l.token)
|
||||
c <- l
|
||||
stri = 0
|
||||
}
|
||||
@@ -659,7 +665,6 @@ func zlexer(s *scan, c chan lex) {
|
||||
l.tokenUpper = l.token
|
||||
l.length = 1
|
||||
l.comment = string(com[:comi])
|
||||
debug.Printf("[3 %+v %+v]", l.token, l.comment)
|
||||
c <- l
|
||||
l.comment = ""
|
||||
comi = 0
|
||||
@@ -685,14 +690,12 @@ func zlexer(s *scan, c chan lex) {
|
||||
rrtype = true
|
||||
}
|
||||
}
|
||||
debug.Printf("[2 %+v]", l.token)
|
||||
c <- l
|
||||
}
|
||||
l.value = zNewline
|
||||
l.token = "\n"
|
||||
l.tokenUpper = l.token
|
||||
l.length = 1
|
||||
debug.Printf("[1 %+v]", l.token)
|
||||
c <- l
|
||||
stri = 0
|
||||
commt = false
|
||||
@@ -738,7 +741,6 @@ func zlexer(s *scan, c chan lex) {
|
||||
l.tokenUpper = strings.ToUpper(l.token)
|
||||
l.length = stri
|
||||
|
||||
debug.Printf("[%+v]", l.token)
|
||||
c <- l
|
||||
stri = 0
|
||||
}
|
||||
@@ -774,7 +776,6 @@ func zlexer(s *scan, c chan lex) {
|
||||
l.token = "extra closing brace"
|
||||
l.tokenUpper = l.token
|
||||
l.err = true
|
||||
debug.Printf("[%+v]", l.token)
|
||||
c <- l
|
||||
return
|
||||
}
|
||||
@@ -800,7 +801,6 @@ func zlexer(s *scan, c chan lex) {
|
||||
l.tokenUpper = strings.ToUpper(l.token)
|
||||
l.length = stri
|
||||
l.value = zString
|
||||
debug.Printf("[%+v]", l.token)
|
||||
c <- l
|
||||
}
|
||||
if brace != 0 {
|
||||
|
||||
101
vendor/github.com/miekg/dns/scan_rr.go
сгенерированный
поставляемый
101
vendor/github.com/miekg/dns/scan_rr.go
сгенерированный
поставляемый
@@ -1024,6 +1024,56 @@ func setOPENPGPKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, strin
|
||||
return rr, nil, c1
|
||||
}
|
||||
|
||||
func setCSYNC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
rr := new(CSYNC)
|
||||
rr.Hdr = h
|
||||
|
||||
l := <-c
|
||||
if l.length == 0 { // dynamic update rr.
|
||||
return rr, nil, l.comment
|
||||
}
|
||||
j, e := strconv.ParseUint(l.token, 10, 32)
|
||||
if e != nil {
|
||||
// Serial must be a number
|
||||
return nil, &ParseError{f, "bad CSYNC serial", l}, ""
|
||||
}
|
||||
rr.Serial = uint32(j)
|
||||
|
||||
<-c // zBlank
|
||||
|
||||
l = <-c
|
||||
j, e = strconv.ParseUint(l.token, 10, 16)
|
||||
if e != nil {
|
||||
// Serial must be a number
|
||||
return nil, &ParseError{f, "bad CSYNC flags", l}, ""
|
||||
}
|
||||
rr.Flags = uint16(j)
|
||||
|
||||
rr.TypeBitMap = make([]uint16, 0)
|
||||
var (
|
||||
k uint16
|
||||
ok bool
|
||||
)
|
||||
l = <-c
|
||||
for l.value != zNewline && l.value != zEOF {
|
||||
switch l.value {
|
||||
case zBlank:
|
||||
// Ok
|
||||
case zString:
|
||||
if k, ok = StringToType[l.tokenUpper]; !ok {
|
||||
if k, ok = typeToInt(l.tokenUpper); !ok {
|
||||
return nil, &ParseError{f, "bad CSYNC TypeBitMap", l}, ""
|
||||
}
|
||||
}
|
||||
rr.TypeBitMap = append(rr.TypeBitMap, k)
|
||||
default:
|
||||
return nil, &ParseError{f, "bad CSYNC TypeBitMap", l}, ""
|
||||
}
|
||||
l = <-c
|
||||
}
|
||||
return rr, nil, l.comment
|
||||
}
|
||||
|
||||
func setSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
r, e, s := setRRSIG(h, c, o, f)
|
||||
if r != nil {
|
||||
@@ -1797,7 +1847,7 @@ func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
if err != nil {
|
||||
return nil, err, ""
|
||||
}
|
||||
if len(s) > 1 {
|
||||
if len(s) != 1 {
|
||||
return nil, &ParseError{f, "bad URI Target", l}, ""
|
||||
}
|
||||
rr.Target = s[0]
|
||||
@@ -2027,13 +2077,58 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
if e != nil {
|
||||
return nil, e, ""
|
||||
}
|
||||
if len(s) > 1 {
|
||||
if len(s) != 1 {
|
||||
return nil, &ParseError{f, "bad CAA Value", l}, ""
|
||||
}
|
||||
rr.Value = s[0]
|
||||
return rr, nil, c1
|
||||
}
|
||||
|
||||
func setTKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
rr := new(TKEY)
|
||||
rr.Hdr = h
|
||||
|
||||
l := <-c
|
||||
|
||||
// Algorithm
|
||||
if l.value != zString {
|
||||
return nil, &ParseError{f, "bad TKEY algorithm", l}, ""
|
||||
}
|
||||
rr.Algorithm = l.token
|
||||
<-c // zBlank
|
||||
|
||||
// Get the key length and key values
|
||||
l = <-c
|
||||
i, err := strconv.ParseUint(l.token, 10, 8)
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad TKEY key length", l}, ""
|
||||
}
|
||||
rr.KeySize = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
if l.value != zString {
|
||||
return nil, &ParseError{f, "bad TKEY key", l}, ""
|
||||
}
|
||||
rr.Key = l.token
|
||||
<-c // zBlank
|
||||
|
||||
// Get the otherdata length and string data
|
||||
l = <-c
|
||||
i, err = strconv.ParseUint(l.token, 10, 8)
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad TKEY otherdata length", l}, ""
|
||||
}
|
||||
rr.OtherLen = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
if l.value != zString {
|
||||
return nil, &ParseError{f, "bad TKEY otherday", l}, ""
|
||||
}
|
||||
rr.OtherData = l.token
|
||||
|
||||
return rr, nil, ""
|
||||
}
|
||||
|
||||
var typeToparserFunc = map[uint16]parserFunc{
|
||||
TypeAAAA: {setAAAA, false},
|
||||
TypeAFSDB: {setAFSDB, false},
|
||||
@@ -2043,6 +2138,7 @@ var typeToparserFunc = map[uint16]parserFunc{
|
||||
TypeCDNSKEY: {setCDNSKEY, true},
|
||||
TypeCERT: {setCERT, true},
|
||||
TypeCNAME: {setCNAME, false},
|
||||
TypeCSYNC: {setCSYNC, true},
|
||||
TypeDHCID: {setDHCID, true},
|
||||
TypeDLV: {setDLV, true},
|
||||
TypeDNAME: {setDNAME, false},
|
||||
@@ -2099,4 +2195,5 @@ var typeToparserFunc = map[uint16]parserFunc{
|
||||
TypeUINFO: {setUINFO, true},
|
||||
TypeURI: {setURI, true},
|
||||
TypeX25: {setX25, false},
|
||||
TypeTKEY: {setTKEY, true},
|
||||
}
|
||||
|
||||
17
vendor/github.com/miekg/dns/scanner.go
сгенерированный
поставляемый
17
vendor/github.com/miekg/dns/scanner.go
сгенерированный
поставляемый
@@ -4,6 +4,7 @@ package dns
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"text/scanner"
|
||||
)
|
||||
@@ -12,13 +13,18 @@ type scan struct {
|
||||
src *bufio.Reader
|
||||
position scanner.Position
|
||||
eof bool // Have we just seen a eof
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func scanInit(r io.Reader) *scan {
|
||||
func scanInit(r io.Reader) (*scan, context.CancelFunc) {
|
||||
s := new(scan)
|
||||
s.src = bufio.NewReader(r)
|
||||
s.position.Line = 1
|
||||
return s
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.ctx = ctx
|
||||
|
||||
return s, cancel
|
||||
}
|
||||
|
||||
// tokenText returns the next byte from the input
|
||||
@@ -27,6 +33,13 @@ func (s *scan) tokenText() (byte, error) {
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return c, context.Canceled
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// delay the newline handling until the next token is delivered,
|
||||
// fixes off-by-one errors when reporting a parse error.
|
||||
if s.eof == true {
|
||||
|
||||
27
vendor/github.com/miekg/dns/server.go
сгенерированный
поставляемый
27
vendor/github.com/miekg/dns/server.go
сгенерированный
поставляемый
@@ -285,7 +285,7 @@ type Server struct {
|
||||
WriteTimeout time.Duration
|
||||
// TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966).
|
||||
IdleTimeout func() time.Duration
|
||||
// Secret(s) for Tsig map[<zonename>]<base64 secret>.
|
||||
// Secret(s) for Tsig map[<zonename>]<base64 secret>. The zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2).
|
||||
TsigSecret map[string]string
|
||||
// Unsafe instructs the server to disregard any sanity checks and directly hand the message to
|
||||
// the handler. It will specifically not check if the query has the QR bit not set.
|
||||
@@ -460,6 +460,12 @@ func (srv *Server) serveTCP(l net.Listener) error {
|
||||
// deadline is not used here
|
||||
for {
|
||||
rw, err := l.Accept()
|
||||
srv.lock.RLock()
|
||||
if !srv.started {
|
||||
srv.lock.RUnlock()
|
||||
return nil
|
||||
}
|
||||
srv.lock.RUnlock()
|
||||
if err != nil {
|
||||
if neterr, ok := err.(net.Error); ok && neterr.Temporary() {
|
||||
continue
|
||||
@@ -467,12 +473,6 @@ func (srv *Server) serveTCP(l net.Listener) error {
|
||||
return err
|
||||
}
|
||||
m, err := reader.ReadTCP(rw, rtimeout)
|
||||
srv.lock.RLock()
|
||||
if !srv.started {
|
||||
srv.lock.RUnlock()
|
||||
return nil
|
||||
}
|
||||
srv.lock.RUnlock()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -509,6 +509,12 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
|
||||
}
|
||||
srv.lock.RUnlock()
|
||||
if err != nil {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if len(m) < headerSize {
|
||||
continue
|
||||
}
|
||||
go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
|
||||
@@ -626,11 +632,8 @@ func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *S
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
m := make([]byte, srv.UDPSize)
|
||||
n, s, err := ReadFromSessionUDP(conn, m)
|
||||
if err != nil || n == 0 {
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nil, nil, ErrShortRead
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
m = m[:n]
|
||||
return m, s, nil
|
||||
|
||||
4
vendor/github.com/miekg/dns/server_test.go
сгенерированный
поставляемый
4
vendor/github.com/miekg/dns/server_test.go
сгенерированный
поставляемый
@@ -654,11 +654,11 @@ func TestServerStartStopRace(t *testing.T) {
|
||||
s := &Server{}
|
||||
s, _, _, err = RunLocalUDPServerWithFinChan(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not start server: %s", err)
|
||||
t.Fatalf("could not start server: %s", err)
|
||||
}
|
||||
go func() {
|
||||
if err := s.Shutdown(); err != nil {
|
||||
t.Fatalf("Could not stop server: %s", err)
|
||||
t.Fatalf("could not stop server: %s", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
43
vendor/github.com/miekg/dns/types.go
сгенерированный
поставляемый
43
vendor/github.com/miekg/dns/types.go
сгенерированный
поставляемый
@@ -78,6 +78,7 @@ const (
|
||||
TypeCDS uint16 = 59
|
||||
TypeCDNSKEY uint16 = 60
|
||||
TypeOPENPGPKEY uint16 = 61
|
||||
TypeCSYNC uint16 = 62
|
||||
TypeSPF uint16 = 99
|
||||
TypeUINFO uint16 = 100
|
||||
TypeUID uint16 = 101
|
||||
@@ -1012,14 +1013,18 @@ type TKEY struct {
|
||||
Mode uint16
|
||||
Error uint16
|
||||
KeySize uint16
|
||||
Key string
|
||||
Key string `dns:"size-hex:KeySize"`
|
||||
OtherLen uint16
|
||||
OtherData string
|
||||
OtherData string `dns:"size-hex:OtherLen"`
|
||||
}
|
||||
|
||||
// TKEY has no official presentation format, but this will suffice.
|
||||
func (rr *TKEY) String() string {
|
||||
// It has no presentation format
|
||||
return ""
|
||||
s := "\n;; TKEY PSEUDOSECTION:\n"
|
||||
s += rr.Hdr.String() + " " + rr.Algorithm + " " +
|
||||
strconv.Itoa(int(rr.KeySize)) + " " + rr.Key + " " +
|
||||
strconv.Itoa(int(rr.OtherLen)) + " " + rr.OtherData
|
||||
return s
|
||||
}
|
||||
|
||||
// RFC3597 represents an unknown/generic RR. See RFC 3597.
|
||||
@@ -1267,6 +1272,36 @@ type OPENPGPKEY struct {
|
||||
|
||||
func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
|
||||
|
||||
// CSYNC RR. See RFC 7477.
|
||||
type CSYNC struct {
|
||||
Hdr RR_Header
|
||||
Serial uint32
|
||||
Flags uint16
|
||||
TypeBitMap []uint16 `dns:"nsec"`
|
||||
}
|
||||
|
||||
func (rr *CSYNC) String() string {
|
||||
s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.Itoa(int(rr.Flags))
|
||||
|
||||
for i := 0; i < len(rr.TypeBitMap); i++ {
|
||||
s += " " + Type(rr.TypeBitMap[i]).String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (rr *CSYNC) len() int {
|
||||
l := rr.Hdr.len() + 4 + 2
|
||||
lastwindow := uint32(2 ^ 32 + 1)
|
||||
for _, t := range rr.TypeBitMap {
|
||||
window := t / 256
|
||||
if uint32(window) != lastwindow {
|
||||
l += 1 + 32
|
||||
}
|
||||
lastwindow = uint32(window)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
6
vendor/github.com/miekg/dns/types_generate.go
сгенерированный
поставляемый
6
vendor/github.com/miekg/dns/types_generate.go
сгенерированный
поставляемый
@@ -23,11 +23,11 @@ var skipLen = map[string]struct{}{
|
||||
"NSEC": {},
|
||||
"NSEC3": {},
|
||||
"OPT": {},
|
||||
"CSYNC": {},
|
||||
}
|
||||
|
||||
var packageHdr = `
|
||||
// *** DO NOT MODIFY ***
|
||||
// AUTOGENERATED BY go generate from types_generate.go
|
||||
// Code generated by "go run types_generate.go"; DO NOT EDIT.
|
||||
|
||||
package dns
|
||||
|
||||
@@ -181,6 +181,8 @@ func main() {
|
||||
fallthrough
|
||||
case st.Tag(i) == `dns:"base64"`:
|
||||
o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n")
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex:`): // this has an extra field where the length is stored
|
||||
o("l += len(rr.%s)/2\n")
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex`):
|
||||
fallthrough
|
||||
case st.Tag(i) == `dns:"hex"`:
|
||||
|
||||
52
vendor/github.com/miekg/dns/udp.go
сгенерированный
поставляемый
52
vendor/github.com/miekg/dns/udp.go
сгенерированный
поставляемый
@@ -4,6 +4,9 @@ package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// SessionUDP holds the remote address and the associated
|
||||
@@ -34,12 +37,53 @@ func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, e
|
||||
return n, err
|
||||
}
|
||||
|
||||
func setUDPSocketOptions(conn *net.UDPConn) error {
|
||||
// Try setting the flags for both families and ignore the errors unless they
|
||||
// both error.
|
||||
err6 := ipv6.NewPacketConn(conn).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true)
|
||||
err4 := ipv4.NewPacketConn(conn).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true)
|
||||
if err6 != nil && err4 != nil {
|
||||
return err4
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseDstFromOOB takes oob data and returns the destination IP.
|
||||
func parseDstFromOOB(oob []byte) net.IP {
|
||||
// Start with IPv6 and then fallback to IPv4
|
||||
// TODO(fastest963): Figure out a way to prefer one or the other. Looking at
|
||||
// the lvl of the header for a 0 or 41 isn't cross-platform.
|
||||
var dst net.IP
|
||||
cm6 := new(ipv6.ControlMessage)
|
||||
if cm6.Parse(oob) == nil {
|
||||
dst = cm6.Dst
|
||||
}
|
||||
if dst == nil {
|
||||
cm4 := new(ipv4.ControlMessage)
|
||||
if cm4.Parse(oob) == nil {
|
||||
dst = cm4.Dst
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// correctSource takes oob data and returns new oob data with the Src equal to the Dst
|
||||
func correctSource(oob []byte) []byte {
|
||||
dst, err := parseUDPSocketDst(oob)
|
||||
// If the destination could not be determined, ignore.
|
||||
if err != nil || dst == nil {
|
||||
dst := parseDstFromOOB(oob)
|
||||
if dst == nil {
|
||||
return nil
|
||||
}
|
||||
return marshalUDPSocketSrc(dst)
|
||||
// If the dst is definitely an IPv6, then use ipv6's ControlMessage to
|
||||
// respond otherwise use ipv4's because ipv6's marshal ignores ipv4
|
||||
// addresses.
|
||||
if dst.To4() == nil {
|
||||
cm := new(ipv6.ControlMessage)
|
||||
cm.Src = dst
|
||||
oob = cm.Marshal()
|
||||
} else {
|
||||
cm := new(ipv4.ControlMessage)
|
||||
cm.Src = dst
|
||||
oob = cm.Marshal()
|
||||
}
|
||||
return oob
|
||||
}
|
||||
|
||||
220
vendor/github.com/miekg/dns/udp_linux.go
сгенерированный
поставляемый
220
vendor/github.com/miekg/dns/udp_linux.go
сгенерированный
поставляемый
@@ -1,220 +0,0 @@
|
||||
// +build linux,!appengine
|
||||
|
||||
package dns
|
||||
|
||||
// See:
|
||||
// * http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket and
|
||||
// * http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/
|
||||
//
|
||||
// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outgoing
|
||||
// interface, this might not always be the correct one. This code will make sure the egress
|
||||
// packet's interface matched the ingress' one.
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/miekg/dns/internal/socket"
|
||||
)
|
||||
|
||||
const (
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
sizeofInetPktinfo = 0xc
|
||||
protocolIP = 0
|
||||
protocolIPv6 = 41
|
||||
)
|
||||
|
||||
type inetPktinfo struct {
|
||||
Ifindex int32
|
||||
Spec_dst [4]byte /* in_addr */
|
||||
Addr [4]byte /* in_addr */
|
||||
}
|
||||
|
||||
type inet6Pktinfo struct {
|
||||
Addr [16]byte /* in6_addr */
|
||||
Ifindex int32
|
||||
}
|
||||
|
||||
type inetControlMessage struct {
|
||||
Src net.IP // source address, specifying only
|
||||
Dst net.IP // destination address, receiving only
|
||||
}
|
||||
|
||||
// setUDPSocketOptions sets the UDP socket options.
|
||||
// This function is implemented on a per platform basis. See udp_*.go for more details
|
||||
func setUDPSocketOptions(conn *net.UDPConn) error {
|
||||
sa, err := getUDPSocketName(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch sa.(type) {
|
||||
case *syscall.SockaddrInet6:
|
||||
v6only, err := getUDPSocketOptions6Only(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setUDPSocketOptions6(conn)
|
||||
if !v6only {
|
||||
setUDPSocketOptions4(conn)
|
||||
}
|
||||
case *syscall.SockaddrInet4:
|
||||
setUDPSocketOptions4(conn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setUDPSocketOptions4 prepares the v4 socket for sessions.
|
||||
func setUDPSocketOptions4(conn *net.UDPConn) error {
|
||||
file, err := conn.File()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
// Calling File() above results in the connection becoming blocking, we must fix that.
|
||||
// See https://github.com/miekg/dns/issues/279
|
||||
err = syscall.SetNonblock(int(file.Fd()), true)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// setUDPSocketOptions6 prepares the v6 socket for sessions.
|
||||
func setUDPSocketOptions6(conn *net.UDPConn) error {
|
||||
file, err := conn.File()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
err = syscall.SetNonblock(int(file.Fd()), true)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUDPSocketOption6Only return true if the socket is v6 only and false when it is v4/v6 combined
|
||||
// (dualstack).
|
||||
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
|
||||
file, err := conn.File()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
|
||||
v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return false, err
|
||||
}
|
||||
file.Close()
|
||||
return v6only == 1, nil
|
||||
}
|
||||
|
||||
func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
|
||||
file, err := conn.File()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
return syscall.Getsockname(int(file.Fd()))
|
||||
}
|
||||
|
||||
// marshalInetPacketInfo marshals a ipv4 control message, returning
|
||||
// the byte slice for the next marshal, if any
|
||||
func marshalInetPacketInfo(b []byte, cm *inetControlMessage) []byte {
|
||||
m := socket.ControlMessage(b)
|
||||
m.MarshalHeader(protocolIP, syscall.IP_PKTINFO, sizeofInetPktinfo)
|
||||
if cm != nil {
|
||||
pi := (*inetPktinfo)(unsafe.Pointer(&m.Data(sizeofInetPktinfo)[0]))
|
||||
if ip := cm.Src.To4(); ip != nil {
|
||||
copy(pi.Spec_dst[:], ip)
|
||||
}
|
||||
}
|
||||
return m.Next(sizeofInetPktinfo)
|
||||
}
|
||||
|
||||
// marshalInet6PacketInfo marshals a ipv6 control message, returning
|
||||
// the byte slice for the next marshal, if any
|
||||
func marshalInet6PacketInfo(b []byte, cm *inetControlMessage) []byte {
|
||||
m := socket.ControlMessage(b)
|
||||
m.MarshalHeader(protocolIPv6, syscall.IPV6_PKTINFO, sizeofInet6Pktinfo)
|
||||
if cm != nil {
|
||||
pi := (*inet6Pktinfo)(unsafe.Pointer(&m.Data(sizeofInet6Pktinfo)[0]))
|
||||
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
|
||||
copy(pi.Addr[:], ip)
|
||||
}
|
||||
}
|
||||
return m.Next(sizeofInet6Pktinfo)
|
||||
}
|
||||
|
||||
func parseInetPacketInfo(cm *inetControlMessage, b []byte) {
|
||||
pi := (*inetPktinfo)(unsafe.Pointer(&b[0]))
|
||||
if len(cm.Dst) < net.IPv4len {
|
||||
cm.Dst = make(net.IP, net.IPv4len)
|
||||
}
|
||||
copy(cm.Dst, pi.Addr[:])
|
||||
}
|
||||
|
||||
func parseInet6PacketInfo(cm *inetControlMessage, b []byte) {
|
||||
pi := (*inet6Pktinfo)(unsafe.Pointer(&b[0]))
|
||||
if len(cm.Dst) < net.IPv6len {
|
||||
cm.Dst = make(net.IP, net.IPv6len)
|
||||
}
|
||||
copy(cm.Dst, pi.Addr[:])
|
||||
}
|
||||
|
||||
// parseUDPSocketDst takes out-of-band data from ReadMsgUDP and parses it for
|
||||
// the Dst address
|
||||
func parseUDPSocketDst(oob []byte) (net.IP, error) {
|
||||
cm := new(inetControlMessage)
|
||||
ms, err := socket.ControlMessage(oob).Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range ms {
|
||||
lvl, typ, l, err := m.ParseHeader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lvl == protocolIPv6 { // IPv6
|
||||
if typ == syscall.IPV6_PKTINFO && l >= sizeofInet6Pktinfo {
|
||||
parseInet6PacketInfo(cm, m.Data(l))
|
||||
}
|
||||
} else if lvl == protocolIP { // IPv4
|
||||
if typ == syscall.IP_PKTINFO && l >= sizeofInetPktinfo {
|
||||
parseInetPacketInfo(cm, m.Data(l))
|
||||
}
|
||||
}
|
||||
}
|
||||
return cm.Dst, nil
|
||||
}
|
||||
|
||||
// marshalUDPSocketSrc takes the given src address and returns out-of-band data
|
||||
// to give to WriteMsgUDP
|
||||
func marshalUDPSocketSrc(src net.IP) []byte {
|
||||
var oob []byte
|
||||
// If the dst is definitely an ipv6, then use ipv6 control to respond
|
||||
// otherwise use ipv4 because the ipv6 marshal ignores ipv4 messages.
|
||||
// See marshalInet6PacketInfo
|
||||
cm := new(inetControlMessage)
|
||||
cm.Src = src
|
||||
if src.To4() == nil {
|
||||
oob = make([]byte, socket.ControlMessageSpace(sizeofInet6Pktinfo))
|
||||
marshalInet6PacketInfo(oob, cm)
|
||||
} else {
|
||||
oob = make([]byte, socket.ControlMessageSpace(sizeofInetPktinfo))
|
||||
marshalInetPacketInfo(oob, cm)
|
||||
}
|
||||
return oob
|
||||
}
|
||||
68
vendor/github.com/miekg/dns/udp_linux_test.go
сгенерированный
поставляемый
68
vendor/github.com/miekg/dns/udp_linux_test.go
сгенерированный
поставляемый
@@ -1,68 +0,0 @@
|
||||
// +build linux,!appengine
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseUDPSocketDst(t *testing.T) {
|
||||
// dst is :ffff:100.100.100.100
|
||||
oob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}
|
||||
dst, err := parseUDPSocketDst(oob)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing ipv6 oob: %v", err)
|
||||
}
|
||||
dst4 := dst.To4()
|
||||
if dst4 == nil {
|
||||
t.Errorf("failed to parse ipv4: %v", dst)
|
||||
} else if dst4.String() != "100.100.100.100" {
|
||||
t.Errorf("unexpected ipv4: %v", dst4)
|
||||
}
|
||||
|
||||
// dst is 2001:db8::1
|
||||
oob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}
|
||||
dst, err = parseUDPSocketDst(oob)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing ipv6 oob: %v", err)
|
||||
}
|
||||
dst6 := dst.To16()
|
||||
if dst6 == nil {
|
||||
t.Errorf("failed to parse ipv6: %v", dst)
|
||||
} else if dst6.String() != "2001:db8::1" {
|
||||
t.Errorf("unexpected ipv6: %v", dst4)
|
||||
}
|
||||
|
||||
// dst is 100.100.100.100 but was received on 10.10.10.10
|
||||
oob = []byte{28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10, 10, 100, 100, 100, 100, 0, 0, 0, 0}
|
||||
dst, err = parseUDPSocketDst(oob)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing ipv4 oob: %v", err)
|
||||
}
|
||||
dst4 = dst.To4()
|
||||
if dst4 == nil {
|
||||
t.Errorf("failed to parse ipv4: %v", dst)
|
||||
} else if dst4.String() != "100.100.100.100" {
|
||||
t.Errorf("unexpected ipv4: %v", dst4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUDPSocketSrc(t *testing.T) {
|
||||
// src is 100.100.100.100
|
||||
exoob := []byte{28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 100, 100, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
oob := marshalUDPSocketSrc(net.ParseIP("100.100.100.100"))
|
||||
if !bytes.Equal(exoob, oob) {
|
||||
t.Errorf("expected ipv4 oob:\n%v", exoob)
|
||||
t.Errorf("actual ipv4 oob:\n%v", oob)
|
||||
}
|
||||
|
||||
// src is 2001:db8::1
|
||||
exoob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
oob = marshalUDPSocketSrc(net.ParseIP("2001:db8::1"))
|
||||
if !bytes.Equal(exoob, oob) {
|
||||
t.Errorf("expected ipv6 oob:\n%v", exoob)
|
||||
t.Errorf("actual ipv6 oob:\n%v", oob)
|
||||
}
|
||||
}
|
||||
17
vendor/github.com/miekg/dns/udp_other.go
сгенерированный
поставляемый
17
vendor/github.com/miekg/dns/udp_other.go
сгенерированный
поставляемый
@@ -1,17 +0,0 @@
|
||||
// +build !linux appengine
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// These do nothing. See udp_linux.go for an example of how to implement this.
|
||||
|
||||
// We tried to adhire to some kind of naming scheme.
|
||||
func setUDPSocketOptions(conn *net.UDPConn) error { return nil }
|
||||
func setUDPSocketOptions4(conn *net.UDPConn) error { return nil }
|
||||
func setUDPSocketOptions6(conn *net.UDPConn) error { return nil }
|
||||
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) { return false, nil }
|
||||
func parseUDPSocketDst(oob []byte) (net.IP, error) { return nil, nil }
|
||||
func marshalUDPSocketSrc(src net.IP) []byte { return nil }
|
||||
126
vendor/github.com/miekg/dns/udp_test.go
сгенерированный
поставляемый
Обычный файл
126
vendor/github.com/miekg/dns/udp_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,126 @@
|
||||
// +build linux,!appengine
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
func TestSetUDPSocketOptions(t *testing.T) {
|
||||
// returns an error if we cannot resolve that address
|
||||
testFamily := func(n, addr string) error {
|
||||
a, err := net.ResolveUDPAddr(n, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err := net.ListenUDP(n, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setUDPSocketOptions(c); err != nil {
|
||||
t.Fatalf("failed to set socket options: %v", err)
|
||||
}
|
||||
ch := make(chan *SessionUDP)
|
||||
go func() {
|
||||
// Set some deadline so this goroutine doesn't hang forever
|
||||
c.SetReadDeadline(time.Now().Add(time.Minute))
|
||||
b := make([]byte, 1)
|
||||
_, sess, err := ReadFromSessionUDP(c, b)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read from conn: %v", err)
|
||||
}
|
||||
ch <- sess
|
||||
}()
|
||||
|
||||
c2, err := net.Dial("udp", c.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial udp: %v", err)
|
||||
}
|
||||
if _, err := c2.Write([]byte{1}); err != nil {
|
||||
t.Fatalf("failed to write to conn: %v", err)
|
||||
}
|
||||
sess := <-ch
|
||||
if len(sess.context) == 0 {
|
||||
t.Fatalf("empty session context: %v", sess)
|
||||
}
|
||||
ip := parseDstFromOOB(sess.context)
|
||||
if ip == nil {
|
||||
t.Fatalf("failed to parse dst: %v", sess)
|
||||
}
|
||||
if !strings.Contains(c.LocalAddr().String(), ip.String()) {
|
||||
t.Fatalf("dst was different than listen addr: %v != %v", ip.String(), c.LocalAddr().String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// we require that ipv4 be supported
|
||||
if err := testFamily("udp4", "127.0.0.1:0"); err != nil {
|
||||
t.Fatalf("failed to test socket options on IPv4: %v", err)
|
||||
}
|
||||
// IPv6 might not be supported so these will just log
|
||||
if err := testFamily("udp6", "[::1]:0"); err != nil {
|
||||
t.Logf("failed to test socket options on IPv6-only: %v", err)
|
||||
}
|
||||
if err := testFamily("udp", "[::1]:0"); err != nil {
|
||||
t.Logf("failed to test socket options on IPv6/IPv4: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDstFromOOB(t *testing.T) {
|
||||
// dst is :ffff:100.100.100.100
|
||||
oob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}
|
||||
dst := parseDstFromOOB(oob)
|
||||
dst4 := dst.To4()
|
||||
if dst4 == nil {
|
||||
t.Errorf("failed to parse IPv4 in IPv6: %v", dst)
|
||||
} else if dst4.String() != "100.100.100.100" {
|
||||
t.Errorf("unexpected IPv4: %v", dst4)
|
||||
}
|
||||
|
||||
// dst is 2001:db8::1
|
||||
oob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}
|
||||
dst = parseDstFromOOB(oob)
|
||||
dst6 := dst.To16()
|
||||
if dst6 == nil {
|
||||
t.Errorf("failed to parse IPv6: %v", dst)
|
||||
} else if dst6.String() != "2001:db8::1" {
|
||||
t.Errorf("unexpected IPv6: %v", dst4)
|
||||
}
|
||||
|
||||
// dst is 100.100.100.100 but was received on 10.10.10.10
|
||||
oob = []byte{28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10, 10, 100, 100, 100, 100, 0, 0, 0, 0}
|
||||
dst = parseDstFromOOB(oob)
|
||||
dst4 = dst.To4()
|
||||
if dst4 == nil {
|
||||
t.Errorf("failed to parse IPv4: %v", dst)
|
||||
} else if dst4.String() != "100.100.100.100" {
|
||||
t.Errorf("unexpected IPv4: %v", dst4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorrectSource(t *testing.T) {
|
||||
// dst is :ffff:100.100.100.100 which should be counted as IPv4
|
||||
oob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}
|
||||
soob := correctSource(oob)
|
||||
cm4 := new(ipv4.ControlMessage)
|
||||
cm4.Src = net.ParseIP("100.100.100.100")
|
||||
if !bytes.Equal(soob, cm4.Marshal()) {
|
||||
t.Errorf("unexpected oob for ipv4 address: %v", soob)
|
||||
}
|
||||
|
||||
// dst is 2001:db8::1
|
||||
oob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}
|
||||
soob = correctSource(oob)
|
||||
cm6 := new(ipv6.ControlMessage)
|
||||
cm6.Src = net.ParseIP("2001:db8::1")
|
||||
if !bytes.Equal(soob, cm6.Marshal()) {
|
||||
t.Errorf("unexpected oob for IPv6 address: %v", soob)
|
||||
}
|
||||
}
|
||||
7
vendor/github.com/miekg/dns/udp_windows.go
сгенерированный
поставляемый
7
vendor/github.com/miekg/dns/udp_windows.go
сгенерированный
поставляемый
@@ -14,6 +14,7 @@ func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||
|
||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
||||
// net.UDPAddr.
|
||||
// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP.
|
||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
||||
n, raddr, err := conn.ReadFrom(b)
|
||||
if err != nil {
|
||||
@@ -24,7 +25,13 @@ func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
||||
}
|
||||
|
||||
// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
|
||||
// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP.
|
||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
||||
n, err := conn.WriteTo(b, session.raddr)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods
|
||||
// use the standard method in udp.go for these.
|
||||
func setUDPSocketOptions(*net.UDPConn) error { return nil }
|
||||
func parseDstFromOOB([]byte, net.IP) net.IP { return nil }
|
||||
|
||||
8
vendor/github.com/miekg/dns/update_test.go
сгенерированный
поставляемый
8
vendor/github.com/miekg/dns/update_test.go
сгенерированный
поставляемый
@@ -119,15 +119,15 @@ func TestPreReqAndRemovals(t *testing.T) {
|
||||
;example.org. IN SOA
|
||||
|
||||
;; ANSWER SECTION:
|
||||
name_used. 0 ANY ANY
|
||||
name_used. 0 CLASS255 ANY
|
||||
name_not_used. 0 NONE ANY
|
||||
rrset_used1. 0 ANY A
|
||||
rrset_used1. 0 CLASS255 A
|
||||
rrset_used2. 3600 IN A 127.0.0.1
|
||||
rrset_not_used. 0 NONE A
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
remove1. 0 ANY ANY
|
||||
remove2. 0 ANY A
|
||||
remove1. 0 CLASS255 ANY
|
||||
remove2. 0 CLASS255 A
|
||||
remove3. 0 NONE A 127.0.0.1
|
||||
insert. 3600 IN A 127.0.0.1
|
||||
`
|
||||
|
||||
10
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/.gitattributes
сгенерированный
поставляемый
Обычный файл
10
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/.gitattributes
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,10 @@
|
||||
# Treat all files in this repo as binary, with no git magic updating
|
||||
# line endings. Windows users contributing to Go will need to use a
|
||||
# modern version of git and editors capable of LF line endings.
|
||||
#
|
||||
# We'll prevent accidental CRLF line endings from entering the repo
|
||||
# via the git-review gofmt checks.
|
||||
#
|
||||
# See golang.org/issue/9281
|
||||
|
||||
* -text
|
||||
2
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/.gitignore
сгенерированный
поставляемый
Обычный файл
2
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/.gitignore
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,2 @@
|
||||
# Add no patterns to .hgignore except for files generated by the build.
|
||||
last-change
|
||||
3
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/AUTHORS
сгенерированный
поставляемый
Обычный файл
3
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/AUTHORS
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,3 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at https://tip.golang.org/AUTHORS.
|
||||
31
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/CONTRIBUTING.md
сгенерированный
поставляемый
Обычный файл
31
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/CONTRIBUTING.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,31 @@
|
||||
# Contributing to Go
|
||||
|
||||
Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
|
||||
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
|
||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
## Contributing code
|
||||
|
||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||
before sending patches.
|
||||
|
||||
**We do not accept GitHub pull requests**
|
||||
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
|
||||
|
||||
Unless otherwise noted, the Go source files are distributed under
|
||||
the BSD-style license found in the LICENSE file.
|
||||
|
||||
3
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/CONTRIBUTORS
сгенерированный
поставляемый
Обычный файл
3
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/CONTRIBUTORS
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,3 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at https://tip.golang.org/CONTRIBUTORS.
|
||||
27
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/LICENSE
сгенерированный
поставляемый
Обычный файл
27
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
22
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/PATENTS
сгенерированный
поставляемый
Обычный файл
22
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/PATENTS
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
21
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/README.md
сгенерированный
поставляемый
Обычный файл
21
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,21 @@
|
||||
# Go Cryptography
|
||||
|
||||
This repository holds supplementary Go cryptography libraries.
|
||||
|
||||
## Download/Install
|
||||
|
||||
The easiest way to install is to run `go get -u golang.org/x/crypto/...`. You
|
||||
can also manually git clone the repository to `$GOPATH/src/golang.org/x/crypto`.
|
||||
|
||||
## Report Issues / Send Patches
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||
this repository, see https://golang.org/doc/contribute.html.
|
||||
|
||||
The main issue tracker for the crypto repository is located at
|
||||
https://github.com/golang/go/issues. Prefix your issue with "x/crypto:" in the
|
||||
subject line, so it is easy to find.
|
||||
|
||||
Note that contributions to the cryptography package receive additional scrutiny
|
||||
due to their sensitive nature. Patches may take longer than normal to receive
|
||||
feedback.
|
||||
1058
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/acme.go
сгенерированный
поставляемый
Обычный файл
1058
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/acme.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1352
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/acme_test.go
сгенерированный
поставляемый
Обычный файл
1352
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/acme_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
821
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/autocert.go
сгенерированный
поставляемый
Обычный файл
821
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/autocert.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,821 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package autocert provides automatic access to certificates from Let's Encrypt
|
||||
// and any other ACME-based CA.
|
||||
//
|
||||
// This package is a work in progress and makes no API stability promises.
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
mathrand "math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/acme"
|
||||
)
|
||||
|
||||
// createCertRetryAfter is how much time to wait before removing a failed state
|
||||
// entry due to an unsuccessful createCert call.
|
||||
// This is a variable instead of a const for testing.
|
||||
// TODO: Consider making it configurable or an exp backoff?
|
||||
var createCertRetryAfter = time.Minute
|
||||
|
||||
// pseudoRand is safe for concurrent use.
|
||||
var pseudoRand *lockedMathRand
|
||||
|
||||
func init() {
|
||||
src := mathrand.NewSource(timeNow().UnixNano())
|
||||
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
|
||||
}
|
||||
|
||||
// AcceptTOS is a Manager.Prompt function that always returns true to
|
||||
// indicate acceptance of the CA's Terms of Service during account
|
||||
// registration.
|
||||
func AcceptTOS(tosURL string) bool { return true }
|
||||
|
||||
// HostPolicy specifies which host names the Manager is allowed to respond to.
|
||||
// It returns a non-nil error if the host should be rejected.
|
||||
// The returned error is accessible via tls.Conn.Handshake and its callers.
|
||||
// See Manager's HostPolicy field and GetCertificate method docs for more details.
|
||||
type HostPolicy func(ctx context.Context, host string) error
|
||||
|
||||
// HostWhitelist returns a policy where only the specified host names are allowed.
|
||||
// Only exact matches are currently supported. Subdomains, regexp or wildcard
|
||||
// will not match.
|
||||
func HostWhitelist(hosts ...string) HostPolicy {
|
||||
whitelist := make(map[string]bool, len(hosts))
|
||||
for _, h := range hosts {
|
||||
whitelist[h] = true
|
||||
}
|
||||
return func(_ context.Context, host string) error {
|
||||
if !whitelist[host] {
|
||||
return errors.New("acme/autocert: host not configured")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// defaultHostPolicy is used when Manager.HostPolicy is not set.
|
||||
func defaultHostPolicy(context.Context, string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Manager is a stateful certificate manager built on top of acme.Client.
|
||||
// It obtains and refreshes certificates automatically,
|
||||
// as well as providing them to a TLS server via tls.Config.
|
||||
//
|
||||
// You must specify a cache implementation, such as DirCache,
|
||||
// to reuse obtained certificates across program restarts.
|
||||
// Otherwise your server is very likely to exceed the certificate
|
||||
// issuer's request rate limits.
|
||||
type Manager struct {
|
||||
// Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS).
|
||||
// The registration may require the caller to agree to the CA's TOS.
|
||||
// If so, Manager calls Prompt with a TOS URL provided by the CA. Prompt should report
|
||||
// whether the caller agrees to the terms.
|
||||
//
|
||||
// To always accept the terms, the callers can use AcceptTOS.
|
||||
Prompt func(tosURL string) bool
|
||||
|
||||
// Cache optionally stores and retrieves previously-obtained certificates.
|
||||
// If nil, certs will only be cached for the lifetime of the Manager.
|
||||
//
|
||||
// Manager passes the Cache certificates data encoded in PEM, with private/public
|
||||
// parts combined in a single Cache.Put call, private key first.
|
||||
Cache Cache
|
||||
|
||||
// HostPolicy controls which domains the Manager will attempt
|
||||
// to retrieve new certificates for. It does not affect cached certs.
|
||||
//
|
||||
// If non-nil, HostPolicy is called before requesting a new cert.
|
||||
// If nil, all hosts are currently allowed. This is not recommended,
|
||||
// as it opens a potential attack where clients connect to a server
|
||||
// by IP address and pretend to be asking for an incorrect host name.
|
||||
// Manager will attempt to obtain a certificate for that host, incorrectly,
|
||||
// eventually reaching the CA's rate limit for certificate requests
|
||||
// and making it impossible to obtain actual certificates.
|
||||
//
|
||||
// See GetCertificate for more details.
|
||||
HostPolicy HostPolicy
|
||||
|
||||
// RenewBefore optionally specifies how early certificates should
|
||||
// be renewed before they expire.
|
||||
//
|
||||
// If zero, they're renewed 30 days before expiration.
|
||||
RenewBefore time.Duration
|
||||
|
||||
// Client is used to perform low-level operations, such as account registration
|
||||
// and requesting new certificates.
|
||||
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
|
||||
// directory endpoint and a newly-generated ECDSA P-256 key.
|
||||
//
|
||||
// Mutating the field after the first call of GetCertificate method will have no effect.
|
||||
Client *acme.Client
|
||||
|
||||
// Email optionally specifies a contact email address.
|
||||
// This is used by CAs, such as Let's Encrypt, to notify about problems
|
||||
// with issued certificates.
|
||||
//
|
||||
// If the Client's account key is already registered, Email is not used.
|
||||
Email string
|
||||
|
||||
// ForceRSA makes the Manager generate certificates with 2048-bit RSA keys.
|
||||
//
|
||||
// If false, a default is used. Currently the default
|
||||
// is EC-based keys using the P-256 curve.
|
||||
ForceRSA bool
|
||||
|
||||
clientMu sync.Mutex
|
||||
client *acme.Client // initialized by acmeClient method
|
||||
|
||||
stateMu sync.Mutex
|
||||
state map[string]*certState // keyed by domain name
|
||||
|
||||
// tokenCert is keyed by token domain name, which matches server name
|
||||
// of ClientHello. Keys always have ".acme.invalid" suffix.
|
||||
tokenCertMu sync.RWMutex
|
||||
tokenCert map[string]*tls.Certificate
|
||||
|
||||
// renewal tracks the set of domains currently running renewal timers.
|
||||
// It is keyed by domain name.
|
||||
renewalMu sync.Mutex
|
||||
renewal map[string]*domainRenewal
|
||||
}
|
||||
|
||||
// GetCertificate implements the tls.Config.GetCertificate hook.
|
||||
// It provides a TLS certificate for hello.ServerName host, including answering
|
||||
// *.acme.invalid (TLS-SNI) challenges. All other fields of hello are ignored.
|
||||
//
|
||||
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
||||
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
|
||||
// The error is propagated back to the caller of GetCertificate and is user-visible.
|
||||
// This does not affect cached certs. See HostPolicy field description for more details.
|
||||
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
if m.Prompt == nil {
|
||||
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
||||
}
|
||||
|
||||
name := hello.ServerName
|
||||
if name == "" {
|
||||
return nil, errors.New("acme/autocert: missing server name")
|
||||
}
|
||||
if !strings.Contains(strings.Trim(name, "."), ".") {
|
||||
return nil, errors.New("acme/autocert: server name component count invalid")
|
||||
}
|
||||
if strings.ContainsAny(name, `/\`) {
|
||||
return nil, errors.New("acme/autocert: server name contains invalid character")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// check whether this is a token cert requested for TLS-SNI challenge
|
||||
if strings.HasSuffix(name, ".acme.invalid") {
|
||||
m.tokenCertMu.RLock()
|
||||
defer m.tokenCertMu.RUnlock()
|
||||
if cert := m.tokenCert[name]; cert != nil {
|
||||
return cert, nil
|
||||
}
|
||||
if cert, err := m.cacheGet(ctx, name); err == nil {
|
||||
return cert, nil
|
||||
}
|
||||
// TODO: cache error results?
|
||||
return nil, fmt.Errorf("acme/autocert: no token cert for %q", name)
|
||||
}
|
||||
|
||||
// regular domain
|
||||
name = strings.TrimSuffix(name, ".") // golang.org/issue/18114
|
||||
cert, err := m.cert(ctx, name)
|
||||
if err == nil {
|
||||
return cert, nil
|
||||
}
|
||||
if err != ErrCacheMiss {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// first-time
|
||||
if err := m.hostPolicy()(ctx, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err = m.createCert(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.cachePut(ctx, name, cert)
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// cert returns an existing certificate either from m.state or cache.
|
||||
// If a certificate is found in cache but not in m.state, the latter will be filled
|
||||
// with the cached value.
|
||||
func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, error) {
|
||||
m.stateMu.Lock()
|
||||
if s, ok := m.state[name]; ok {
|
||||
m.stateMu.Unlock()
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return s.tlscert()
|
||||
}
|
||||
defer m.stateMu.Unlock()
|
||||
cert, err := m.cacheGet(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil, errors.New("acme/autocert: private key cannot sign")
|
||||
}
|
||||
if m.state == nil {
|
||||
m.state = make(map[string]*certState)
|
||||
}
|
||||
s := &certState{
|
||||
key: signer,
|
||||
cert: cert.Certificate,
|
||||
leaf: cert.Leaf,
|
||||
}
|
||||
m.state[name] = s
|
||||
go m.renew(name, s.key, s.leaf.NotAfter)
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// cacheGet always returns a valid certificate, or an error otherwise.
|
||||
// If a cached certficate exists but is not valid, ErrCacheMiss is returned.
|
||||
func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) {
|
||||
if m.Cache == nil {
|
||||
return nil, ErrCacheMiss
|
||||
}
|
||||
data, err := m.Cache.Get(ctx, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// private
|
||||
priv, pub := pem.Decode(data)
|
||||
if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
|
||||
return nil, ErrCacheMiss
|
||||
}
|
||||
privKey, err := parsePrivateKey(priv.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// public
|
||||
var pubDER [][]byte
|
||||
for len(pub) > 0 {
|
||||
var b *pem.Block
|
||||
b, pub = pem.Decode(pub)
|
||||
if b == nil {
|
||||
break
|
||||
}
|
||||
pubDER = append(pubDER, b.Bytes)
|
||||
}
|
||||
if len(pub) > 0 {
|
||||
// Leftover content not consumed by pem.Decode. Corrupt. Ignore.
|
||||
return nil, ErrCacheMiss
|
||||
}
|
||||
|
||||
// verify and create TLS cert
|
||||
leaf, err := validCert(domain, pubDER, privKey)
|
||||
if err != nil {
|
||||
return nil, ErrCacheMiss
|
||||
}
|
||||
tlscert := &tls.Certificate{
|
||||
Certificate: pubDER,
|
||||
PrivateKey: privKey,
|
||||
Leaf: leaf,
|
||||
}
|
||||
return tlscert, nil
|
||||
}
|
||||
|
||||
func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Certificate) error {
|
||||
if m.Cache == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// contains PEM-encoded data
|
||||
var buf bytes.Buffer
|
||||
|
||||
// private
|
||||
switch key := tlscert.PrivateKey.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
if err := encodeECDSAKey(&buf, key); err != nil {
|
||||
return err
|
||||
}
|
||||
case *rsa.PrivateKey:
|
||||
b := x509.MarshalPKCS1PrivateKey(key)
|
||||
pb := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: b}
|
||||
if err := pem.Encode(&buf, pb); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("acme/autocert: unknown private key type")
|
||||
}
|
||||
|
||||
// public
|
||||
for _, b := range tlscert.Certificate {
|
||||
pb := &pem.Block{Type: "CERTIFICATE", Bytes: b}
|
||||
if err := pem.Encode(&buf, pb); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return m.Cache.Put(ctx, domain, buf.Bytes())
|
||||
}
|
||||
|
||||
func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
||||
b, err := x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pb := &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||
return pem.Encode(w, pb)
|
||||
}
|
||||
|
||||
// createCert starts the domain ownership verification and returns a certificate
|
||||
// for that domain upon success.
|
||||
//
|
||||
// If the domain is already being verified, it waits for the existing verification to complete.
|
||||
// Either way, createCert blocks for the duration of the whole process.
|
||||
func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certificate, error) {
|
||||
// TODO: maybe rewrite this whole piece using sync.Once
|
||||
state, err := m.certState(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// state may exist if another goroutine is already working on it
|
||||
// in which case just wait for it to finish
|
||||
if !state.locked {
|
||||
state.RLock()
|
||||
defer state.RUnlock()
|
||||
return state.tlscert()
|
||||
}
|
||||
|
||||
// We are the first; state is locked.
|
||||
// Unblock the readers when domain ownership is verified
|
||||
// and we got the cert or the process failed.
|
||||
defer state.Unlock()
|
||||
state.locked = false
|
||||
|
||||
der, leaf, err := m.authorizedCert(ctx, state.key, domain)
|
||||
if err != nil {
|
||||
// Remove the failed state after some time,
|
||||
// making the manager call createCert again on the following TLS hello.
|
||||
time.AfterFunc(createCertRetryAfter, func() {
|
||||
defer testDidRemoveState(domain)
|
||||
m.stateMu.Lock()
|
||||
defer m.stateMu.Unlock()
|
||||
// Verify the state hasn't changed and it's still invalid
|
||||
// before deleting.
|
||||
s, ok := m.state[domain]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if _, err := validCert(domain, s.cert, s.key); err == nil {
|
||||
return
|
||||
}
|
||||
delete(m.state, domain)
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
state.cert = der
|
||||
state.leaf = leaf
|
||||
go m.renew(domain, state.key, state.leaf.NotAfter)
|
||||
return state.tlscert()
|
||||
}
|
||||
|
||||
// certState returns a new or existing certState.
|
||||
// If a new certState is returned, state.exist is false and the state is locked.
|
||||
// The returned error is non-nil only in the case where a new state could not be created.
|
||||
func (m *Manager) certState(domain string) (*certState, error) {
|
||||
m.stateMu.Lock()
|
||||
defer m.stateMu.Unlock()
|
||||
if m.state == nil {
|
||||
m.state = make(map[string]*certState)
|
||||
}
|
||||
// existing state
|
||||
if state, ok := m.state[domain]; ok {
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// new locked state
|
||||
var (
|
||||
err error
|
||||
key crypto.Signer
|
||||
)
|
||||
if m.ForceRSA {
|
||||
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||
} else {
|
||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
state := &certState{
|
||||
key: key,
|
||||
locked: true,
|
||||
}
|
||||
state.Lock() // will be unlocked by m.certState caller
|
||||
m.state[domain] = state
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
||||
// The key argument is the certificate private key.
|
||||
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
|
||||
if err := m.verify(ctx, domain); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
client, err := m.acmeClient(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
csr, err := certRequest(key, domain)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
der, _, err = client.CreateCert(ctx, csr, 0, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
leaf, err = validCert(domain, der, key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return der, leaf, nil
|
||||
}
|
||||
|
||||
// verify starts a new identifier (domain) authorization flow.
|
||||
// It prepares a challenge response and then blocks until the authorization
|
||||
// is marked as "completed" by the CA (either succeeded or failed).
|
||||
//
|
||||
// verify returns nil iff the verification was successful.
|
||||
func (m *Manager) verify(ctx context.Context, domain string) error {
|
||||
client, err := m.acmeClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start domain authorization and get the challenge
|
||||
authz, err := client.Authorize(ctx, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// maybe don't need to at all
|
||||
if authz.Status == acme.StatusValid {
|
||||
return nil
|
||||
}
|
||||
|
||||
// pick a challenge: prefer tls-sni-02 over tls-sni-01
|
||||
// TODO: consider authz.Combinations
|
||||
var chal *acme.Challenge
|
||||
for _, c := range authz.Challenges {
|
||||
if c.Type == "tls-sni-02" {
|
||||
chal = c
|
||||
break
|
||||
}
|
||||
if c.Type == "tls-sni-01" {
|
||||
chal = c
|
||||
}
|
||||
}
|
||||
if chal == nil {
|
||||
return errors.New("acme/autocert: no supported challenge type found")
|
||||
}
|
||||
|
||||
// create a token cert for the challenge response
|
||||
var (
|
||||
cert tls.Certificate
|
||||
name string
|
||||
)
|
||||
switch chal.Type {
|
||||
case "tls-sni-01":
|
||||
cert, name, err = client.TLSSNI01ChallengeCert(chal.Token)
|
||||
case "tls-sni-02":
|
||||
cert, name, err = client.TLSSNI02ChallengeCert(chal.Token)
|
||||
default:
|
||||
err = fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.putTokenCert(ctx, name, &cert)
|
||||
defer func() {
|
||||
// verification has ended at this point
|
||||
// don't need token cert anymore
|
||||
go m.deleteTokenCert(name)
|
||||
}()
|
||||
|
||||
// ready to fulfill the challenge
|
||||
if _, err := client.Accept(ctx, chal); err != nil {
|
||||
return err
|
||||
}
|
||||
// wait for the CA to validate
|
||||
_, err = client.WaitAuthorization(ctx, authz.URI)
|
||||
return err
|
||||
}
|
||||
|
||||
// putTokenCert stores the cert under the named key in both m.tokenCert map
|
||||
// and m.Cache.
|
||||
func (m *Manager) putTokenCert(ctx context.Context, name string, cert *tls.Certificate) {
|
||||
m.tokenCertMu.Lock()
|
||||
defer m.tokenCertMu.Unlock()
|
||||
if m.tokenCert == nil {
|
||||
m.tokenCert = make(map[string]*tls.Certificate)
|
||||
}
|
||||
m.tokenCert[name] = cert
|
||||
m.cachePut(ctx, name, cert)
|
||||
}
|
||||
|
||||
// deleteTokenCert removes the token certificate for the specified domain name
|
||||
// from both m.tokenCert map and m.Cache.
|
||||
func (m *Manager) deleteTokenCert(name string) {
|
||||
m.tokenCertMu.Lock()
|
||||
defer m.tokenCertMu.Unlock()
|
||||
delete(m.tokenCert, name)
|
||||
if m.Cache != nil {
|
||||
m.Cache.Delete(context.Background(), name)
|
||||
}
|
||||
}
|
||||
|
||||
// renew starts a cert renewal timer loop, one per domain.
|
||||
//
|
||||
// The loop is scheduled in two cases:
|
||||
// - a cert was fetched from cache for the first time (wasn't in m.state)
|
||||
// - a new cert was created by m.createCert
|
||||
//
|
||||
// The key argument is a certificate private key.
|
||||
// The exp argument is the cert expiration time (NotAfter).
|
||||
func (m *Manager) renew(domain string, key crypto.Signer, exp time.Time) {
|
||||
m.renewalMu.Lock()
|
||||
defer m.renewalMu.Unlock()
|
||||
if m.renewal[domain] != nil {
|
||||
// another goroutine is already on it
|
||||
return
|
||||
}
|
||||
if m.renewal == nil {
|
||||
m.renewal = make(map[string]*domainRenewal)
|
||||
}
|
||||
dr := &domainRenewal{m: m, domain: domain, key: key}
|
||||
m.renewal[domain] = dr
|
||||
dr.start(exp)
|
||||
}
|
||||
|
||||
// stopRenew stops all currently running cert renewal timers.
|
||||
// The timers are not restarted during the lifetime of the Manager.
|
||||
func (m *Manager) stopRenew() {
|
||||
m.renewalMu.Lock()
|
||||
defer m.renewalMu.Unlock()
|
||||
for name, dr := range m.renewal {
|
||||
delete(m.renewal, name)
|
||||
dr.stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
|
||||
const keyName = "acme_account.key"
|
||||
|
||||
genKey := func() (*ecdsa.PrivateKey, error) {
|
||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
}
|
||||
|
||||
if m.Cache == nil {
|
||||
return genKey()
|
||||
}
|
||||
|
||||
data, err := m.Cache.Get(ctx, keyName)
|
||||
if err == ErrCacheMiss {
|
||||
key, err := genKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := encodeECDSAKey(&buf, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.Cache.Put(ctx, keyName, buf.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
priv, _ := pem.Decode(data)
|
||||
if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
|
||||
return nil, errors.New("acme/autocert: invalid account key found in cache")
|
||||
}
|
||||
return parsePrivateKey(priv.Bytes)
|
||||
}
|
||||
|
||||
func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
|
||||
m.clientMu.Lock()
|
||||
defer m.clientMu.Unlock()
|
||||
if m.client != nil {
|
||||
return m.client, nil
|
||||
}
|
||||
|
||||
client := m.Client
|
||||
if client == nil {
|
||||
client = &acme.Client{DirectoryURL: acme.LetsEncryptURL}
|
||||
}
|
||||
if client.Key == nil {
|
||||
var err error
|
||||
client.Key, err = m.accountKey(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var contact []string
|
||||
if m.Email != "" {
|
||||
contact = []string{"mailto:" + m.Email}
|
||||
}
|
||||
a := &acme.Account{Contact: contact}
|
||||
_, err := client.Register(ctx, a, m.Prompt)
|
||||
if ae, ok := err.(*acme.Error); err == nil || ok && ae.StatusCode == http.StatusConflict {
|
||||
// conflict indicates the key is already registered
|
||||
m.client = client
|
||||
err = nil
|
||||
}
|
||||
return m.client, err
|
||||
}
|
||||
|
||||
func (m *Manager) hostPolicy() HostPolicy {
|
||||
if m.HostPolicy != nil {
|
||||
return m.HostPolicy
|
||||
}
|
||||
return defaultHostPolicy
|
||||
}
|
||||
|
||||
func (m *Manager) renewBefore() time.Duration {
|
||||
if m.RenewBefore > renewJitter {
|
||||
return m.RenewBefore
|
||||
}
|
||||
return 720 * time.Hour // 30 days
|
||||
}
|
||||
|
||||
// certState is ready when its mutex is unlocked for reading.
|
||||
type certState struct {
|
||||
sync.RWMutex
|
||||
locked bool // locked for read/write
|
||||
key crypto.Signer // private key for cert
|
||||
cert [][]byte // DER encoding
|
||||
leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil
|
||||
}
|
||||
|
||||
// tlscert creates a tls.Certificate from s.key and s.cert.
|
||||
// Callers should wrap it in s.RLock() and s.RUnlock().
|
||||
func (s *certState) tlscert() (*tls.Certificate, error) {
|
||||
if s.key == nil {
|
||||
return nil, errors.New("acme/autocert: missing signer")
|
||||
}
|
||||
if len(s.cert) == 0 {
|
||||
return nil, errors.New("acme/autocert: missing certificate")
|
||||
}
|
||||
return &tls.Certificate{
|
||||
PrivateKey: s.key,
|
||||
Certificate: s.cert,
|
||||
Leaf: s.leaf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// certRequest creates a certificate request for the given common name cn
|
||||
// and optional SANs.
|
||||
func certRequest(key crypto.Signer, cn string, san ...string) ([]byte, error) {
|
||||
req := &x509.CertificateRequest{
|
||||
Subject: pkix.Name{CommonName: cn},
|
||||
DNSNames: san,
|
||||
}
|
||||
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
||||
}
|
||||
|
||||
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
|
||||
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
|
||||
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
|
||||
//
|
||||
// Inspired by parsePrivateKey in crypto/tls/tls.go.
|
||||
func parsePrivateKey(der []byte) (crypto.Signer, error) {
|
||||
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
|
||||
switch key := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return key, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
return key, nil
|
||||
default:
|
||||
return nil, errors.New("acme/autocert: unknown private key type in PKCS#8 wrapping")
|
||||
}
|
||||
}
|
||||
if key, err := x509.ParseECPrivateKey(der); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("acme/autocert: failed to parse private key")
|
||||
}
|
||||
|
||||
// validCert parses a cert chain provided as der argument and verifies the leaf, der[0],
|
||||
// corresponds to the private key, as well as the domain match and expiration dates.
|
||||
// It doesn't do any revocation checking.
|
||||
//
|
||||
// The returned value is the verified leaf cert.
|
||||
func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certificate, err error) {
|
||||
// parse public part(s)
|
||||
var n int
|
||||
for _, b := range der {
|
||||
n += len(b)
|
||||
}
|
||||
pub := make([]byte, n)
|
||||
n = 0
|
||||
for _, b := range der {
|
||||
n += copy(pub[n:], b)
|
||||
}
|
||||
x509Cert, err := x509.ParseCertificates(pub)
|
||||
if len(x509Cert) == 0 {
|
||||
return nil, errors.New("acme/autocert: no public key found")
|
||||
}
|
||||
// verify the leaf is not expired and matches the domain name
|
||||
leaf = x509Cert[0]
|
||||
now := timeNow()
|
||||
if now.Before(leaf.NotBefore) {
|
||||
return nil, errors.New("acme/autocert: certificate is not valid yet")
|
||||
}
|
||||
if now.After(leaf.NotAfter) {
|
||||
return nil, errors.New("acme/autocert: expired certificate")
|
||||
}
|
||||
if err := leaf.VerifyHostname(domain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ensure the leaf corresponds to the private key
|
||||
switch pub := leaf.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
prv, ok := key.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("acme/autocert: private key type does not match public key type")
|
||||
}
|
||||
if pub.N.Cmp(prv.N) != 0 {
|
||||
return nil, errors.New("acme/autocert: private key does not match public key")
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
prv, ok := key.(*ecdsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("acme/autocert: private key type does not match public key type")
|
||||
}
|
||||
if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
|
||||
return nil, errors.New("acme/autocert: private key does not match public key")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("acme/autocert: unknown public key algorithm")
|
||||
}
|
||||
return leaf, nil
|
||||
}
|
||||
|
||||
func retryAfter(v string) time.Duration {
|
||||
if i, err := strconv.Atoi(v); err == nil {
|
||||
return time.Duration(i) * time.Second
|
||||
}
|
||||
if t, err := http.ParseTime(v); err == nil {
|
||||
return t.Sub(timeNow())
|
||||
}
|
||||
return time.Second
|
||||
}
|
||||
|
||||
type lockedMathRand struct {
|
||||
sync.Mutex
|
||||
rnd *mathrand.Rand
|
||||
}
|
||||
|
||||
func (r *lockedMathRand) int63n(max int64) int64 {
|
||||
r.Lock()
|
||||
n := r.rnd.Int63n(max)
|
||||
r.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
// For easier testing.
|
||||
var (
|
||||
timeNow = time.Now
|
||||
|
||||
// Called when a state is removed.
|
||||
testDidRemoveState = func(domain string) {}
|
||||
)
|
||||
606
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go
сгенерированный
поставляемый
Обычный файл
606
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,606 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/acme"
|
||||
)
|
||||
|
||||
var discoTmpl = template.Must(template.New("disco").Parse(`{
|
||||
"new-reg": "{{.}}/new-reg",
|
||||
"new-authz": "{{.}}/new-authz",
|
||||
"new-cert": "{{.}}/new-cert"
|
||||
}`))
|
||||
|
||||
var authzTmpl = template.Must(template.New("authz").Parse(`{
|
||||
"status": "pending",
|
||||
"challenges": [
|
||||
{
|
||||
"uri": "{{.}}/challenge/1",
|
||||
"type": "tls-sni-01",
|
||||
"token": "token-01"
|
||||
},
|
||||
{
|
||||
"uri": "{{.}}/challenge/2",
|
||||
"type": "tls-sni-02",
|
||||
"token": "token-02"
|
||||
}
|
||||
]
|
||||
}`))
|
||||
|
||||
type memCache struct {
|
||||
mu sync.Mutex
|
||||
keyData map[string][]byte
|
||||
}
|
||||
|
||||
func (m *memCache) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
v, ok := m.keyData[key]
|
||||
if !ok {
|
||||
return nil, ErrCacheMiss
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (m *memCache) Put(ctx context.Context, key string, data []byte) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.keyData[key] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memCache) Delete(ctx context.Context, key string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
delete(m.keyData, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newMemCache() *memCache {
|
||||
return &memCache{
|
||||
keyData: make(map[string][]byte),
|
||||
}
|
||||
}
|
||||
|
||||
func dummyCert(pub interface{}, san ...string) ([]byte, error) {
|
||||
return dateDummyCert(pub, time.Now(), time.Now().Add(90*24*time.Hour), san...)
|
||||
}
|
||||
|
||||
func dateDummyCert(pub interface{}, start, end time.Time, san ...string) ([]byte, error) {
|
||||
// use EC key to run faster on 386
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
NotBefore: start,
|
||||
NotAfter: end,
|
||||
BasicConstraintsValid: true,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||
DNSNames: san,
|
||||
}
|
||||
if pub == nil {
|
||||
pub = &key.PublicKey
|
||||
}
|
||||
return x509.CreateCertificate(rand.Reader, t, t, pub, key)
|
||||
}
|
||||
|
||||
func decodePayload(v interface{}, r io.Reader) error {
|
||||
var req struct{ Payload string }
|
||||
if err := json.NewDecoder(r).Decode(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(payload, v)
|
||||
}
|
||||
|
||||
func TestGetCertificate(t *testing.T) {
|
||||
man := &Manager{Prompt: AcceptTOS}
|
||||
defer man.stopRenew()
|
||||
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
|
||||
testGetCertificate(t, man, "example.org", hello)
|
||||
}
|
||||
|
||||
func TestGetCertificate_trailingDot(t *testing.T) {
|
||||
man := &Manager{Prompt: AcceptTOS}
|
||||
defer man.stopRenew()
|
||||
hello := &tls.ClientHelloInfo{ServerName: "example.org."}
|
||||
testGetCertificate(t, man, "example.org", hello)
|
||||
}
|
||||
|
||||
func TestGetCertificate_ForceRSA(t *testing.T) {
|
||||
man := &Manager{
|
||||
Prompt: AcceptTOS,
|
||||
Cache: newMemCache(),
|
||||
ForceRSA: true,
|
||||
}
|
||||
defer man.stopRenew()
|
||||
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
|
||||
testGetCertificate(t, man, "example.org", hello)
|
||||
|
||||
cert, err := man.cacheGet(context.Background(), "example.org")
|
||||
if err != nil {
|
||||
t.Fatalf("man.cacheGet: %v", err)
|
||||
}
|
||||
if _, ok := cert.PrivateKey.(*rsa.PrivateKey); !ok {
|
||||
t.Errorf("cert.PrivateKey is %T; want *rsa.PrivateKey", cert.PrivateKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCertificate_nilPrompt(t *testing.T) {
|
||||
man := &Manager{}
|
||||
defer man.stopRenew()
|
||||
url, finish := startACMEServerStub(t, man, "example.org")
|
||||
defer finish()
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
man.Client = &acme.Client{
|
||||
Key: key,
|
||||
DirectoryURL: url,
|
||||
}
|
||||
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
|
||||
if _, err := man.GetCertificate(hello); err == nil {
|
||||
t.Error("got certificate for example.org; wanted error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCertificate_expiredCache(t *testing.T) {
|
||||
// Make an expired cert and cache it.
|
||||
pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tmpl := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: "example.org"},
|
||||
NotAfter: time.Now(),
|
||||
}
|
||||
pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &pk.PublicKey, pk)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tlscert := &tls.Certificate{
|
||||
Certificate: [][]byte{pub},
|
||||
PrivateKey: pk,
|
||||
}
|
||||
|
||||
man := &Manager{Prompt: AcceptTOS, Cache: newMemCache()}
|
||||
defer man.stopRenew()
|
||||
if err := man.cachePut(context.Background(), "example.org", tlscert); err != nil {
|
||||
t.Fatalf("man.cachePut: %v", err)
|
||||
}
|
||||
|
||||
// The expired cached cert should trigger a new cert issuance
|
||||
// and return without an error.
|
||||
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
|
||||
testGetCertificate(t, man, "example.org", hello)
|
||||
}
|
||||
|
||||
func TestGetCertificate_failedAttempt(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
const example = "example.org"
|
||||
d := createCertRetryAfter
|
||||
f := testDidRemoveState
|
||||
defer func() {
|
||||
createCertRetryAfter = d
|
||||
testDidRemoveState = f
|
||||
}()
|
||||
createCertRetryAfter = 0
|
||||
done := make(chan struct{})
|
||||
testDidRemoveState = func(domain string) {
|
||||
if domain != example {
|
||||
t.Errorf("testDidRemoveState: domain = %q; want %q", domain, example)
|
||||
}
|
||||
close(done)
|
||||
}
|
||||
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
man := &Manager{
|
||||
Prompt: AcceptTOS,
|
||||
Client: &acme.Client{
|
||||
Key: key,
|
||||
DirectoryURL: ts.URL,
|
||||
},
|
||||
}
|
||||
defer man.stopRenew()
|
||||
hello := &tls.ClientHelloInfo{ServerName: example}
|
||||
if _, err := man.GetCertificate(hello); err == nil {
|
||||
t.Error("GetCertificate: err is nil")
|
||||
}
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Errorf("took too long to remove the %q state", example)
|
||||
case <-done:
|
||||
man.stateMu.Lock()
|
||||
defer man.stateMu.Unlock()
|
||||
if v, exist := man.state[example]; exist {
|
||||
t.Errorf("state exists for %q: %+v", example, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// startACMEServerStub runs an ACME server
|
||||
// The domain argument is the expected domain name of a certificate request.
|
||||
func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string, finish func()) {
|
||||
// echo token-02 | shasum -a 256
|
||||
// then divide result in 2 parts separated by dot
|
||||
tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid"
|
||||
verifyTokenCert := func() {
|
||||
hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
|
||||
_, err := man.GetCertificate(hello)
|
||||
if err != nil {
|
||||
t.Errorf("verifyTokenCert: GetCertificate(%q): %v", tokenCertName, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ACME CA server stub
|
||||
var ca *httptest.Server
|
||||
ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Replay-Nonce", "nonce")
|
||||
if r.Method == "HEAD" {
|
||||
// a nonce request
|
||||
return
|
||||
}
|
||||
|
||||
switch r.URL.Path {
|
||||
// discovery
|
||||
case "/":
|
||||
if err := discoTmpl.Execute(w, ca.URL); err != nil {
|
||||
t.Errorf("discoTmpl: %v", err)
|
||||
}
|
||||
// client key registration
|
||||
case "/new-reg":
|
||||
w.Write([]byte("{}"))
|
||||
// domain authorization
|
||||
case "/new-authz":
|
||||
w.Header().Set("Location", ca.URL+"/authz/1")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
if err := authzTmpl.Execute(w, ca.URL); err != nil {
|
||||
t.Errorf("authzTmpl: %v", err)
|
||||
}
|
||||
// accept tls-sni-02 challenge
|
||||
case "/challenge/2":
|
||||
verifyTokenCert()
|
||||
w.Write([]byte("{}"))
|
||||
// authorization status
|
||||
case "/authz/1":
|
||||
w.Write([]byte(`{"status": "valid"}`))
|
||||
// cert request
|
||||
case "/new-cert":
|
||||
var req struct {
|
||||
CSR string `json:"csr"`
|
||||
}
|
||||
decodePayload(&req, r.Body)
|
||||
b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
|
||||
csr, err := x509.ParseCertificateRequest(b)
|
||||
if err != nil {
|
||||
t.Errorf("new-cert: CSR: %v", err)
|
||||
}
|
||||
if csr.Subject.CommonName != domain {
|
||||
t.Errorf("CommonName in CSR = %q; want %q", csr.Subject.CommonName, domain)
|
||||
}
|
||||
der, err := dummyCert(csr.PublicKey, domain)
|
||||
if err != nil {
|
||||
t.Errorf("new-cert: dummyCert: %v", err)
|
||||
}
|
||||
chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
|
||||
w.Header().Set("Link", chainUp)
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write(der)
|
||||
// CA chain cert
|
||||
case "/ca-cert":
|
||||
der, err := dummyCert(nil, "ca")
|
||||
if err != nil {
|
||||
t.Errorf("ca-cert: dummyCert: %v", err)
|
||||
}
|
||||
w.Write(der)
|
||||
default:
|
||||
t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
|
||||
}
|
||||
}))
|
||||
finish = func() {
|
||||
ca.Close()
|
||||
|
||||
// make sure token cert was removed
|
||||
cancel := make(chan struct{})
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
tick := time.NewTicker(100 * time.Millisecond)
|
||||
defer tick.Stop()
|
||||
for {
|
||||
hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
|
||||
if _, err := man.GetCertificate(hello); err != nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-tick.C:
|
||||
case <-cancel:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(5 * time.Second):
|
||||
close(cancel)
|
||||
t.Error("token cert was not removed")
|
||||
<-done
|
||||
}
|
||||
}
|
||||
return ca.URL, finish
|
||||
}
|
||||
|
||||
// tests man.GetCertificate flow using the provided hello argument.
|
||||
// The domain argument is the expected domain name of a certificate request.
|
||||
func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) {
|
||||
url, finish := startACMEServerStub(t, man, domain)
|
||||
defer finish()
|
||||
|
||||
// use EC key to run faster on 386
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
man.Client = &acme.Client{
|
||||
Key: key,
|
||||
DirectoryURL: url,
|
||||
}
|
||||
|
||||
// simulate tls.Config.GetCertificate
|
||||
var tlscert *tls.Certificate
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
tlscert, err = man.GetCertificate(hello)
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-time.After(time.Minute):
|
||||
t.Fatal("man.GetCertificate took too long to return")
|
||||
case <-done:
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("man.GetCertificate: %v", err)
|
||||
}
|
||||
|
||||
// verify the tlscert is the same we responded with from the CA stub
|
||||
if len(tlscert.Certificate) == 0 {
|
||||
t.Fatal("len(tlscert.Certificate) is 0")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(tlscert.Certificate[0])
|
||||
if err != nil {
|
||||
t.Fatalf("x509.ParseCertificate: %v", err)
|
||||
}
|
||||
if len(cert.DNSNames) == 0 || cert.DNSNames[0] != domain {
|
||||
t.Errorf("cert.DNSNames = %v; want %q", cert.DNSNames, domain)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAccountKeyCache(t *testing.T) {
|
||||
m := Manager{Cache: newMemCache()}
|
||||
ctx := context.Background()
|
||||
k1, err := m.accountKey(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
k2, err := m.accountKey(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(k1, k2) {
|
||||
t.Errorf("account keys don't match: k1 = %#v; k2 = %#v", k1, k2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tmpl := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: "example.org"},
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
}
|
||||
pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &privKey.PublicKey, privKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tlscert := &tls.Certificate{
|
||||
Certificate: [][]byte{pub},
|
||||
PrivateKey: privKey,
|
||||
}
|
||||
|
||||
man := &Manager{Cache: newMemCache()}
|
||||
defer man.stopRenew()
|
||||
ctx := context.Background()
|
||||
if err := man.cachePut(ctx, "example.org", tlscert); err != nil {
|
||||
t.Fatalf("man.cachePut: %v", err)
|
||||
}
|
||||
res, err := man.cacheGet(ctx, "example.org")
|
||||
if err != nil {
|
||||
t.Fatalf("man.cacheGet: %v", err)
|
||||
}
|
||||
if res == nil {
|
||||
t.Fatal("res is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostWhitelist(t *testing.T) {
|
||||
policy := HostWhitelist("example.com", "example.org", "*.example.net")
|
||||
tt := []struct {
|
||||
host string
|
||||
allow bool
|
||||
}{
|
||||
{"example.com", true},
|
||||
{"example.org", true},
|
||||
{"one.example.com", false},
|
||||
{"two.example.org", false},
|
||||
{"three.example.net", false},
|
||||
{"dummy", false},
|
||||
}
|
||||
for i, test := range tt {
|
||||
err := policy(nil, test.host)
|
||||
if err != nil && test.allow {
|
||||
t.Errorf("%d: policy(%q): %v; want nil", i, test.host, err)
|
||||
}
|
||||
if err == nil && !test.allow {
|
||||
t.Errorf("%d: policy(%q): nil; want an error", i, test.host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidCert(t *testing.T) {
|
||||
key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key3, err := rsa.GenerateKey(rand.Reader, 512)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cert1, err := dummyCert(key1.Public(), "example.org")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cert2, err := dummyCert(key2.Public(), "example.org")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cert3, err := dummyCert(key3.Public(), "example.org")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
now := time.Now()
|
||||
early, err := dateDummyCert(key1.Public(), now.Add(time.Hour), now.Add(2*time.Hour), "example.org")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expired, err := dateDummyCert(key1.Public(), now.Add(-2*time.Hour), now.Add(-time.Hour), "example.org")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tt := []struct {
|
||||
domain string
|
||||
key crypto.Signer
|
||||
cert [][]byte
|
||||
ok bool
|
||||
}{
|
||||
{"example.org", key1, [][]byte{cert1}, true},
|
||||
{"example.org", key3, [][]byte{cert3}, true},
|
||||
{"example.org", key1, [][]byte{cert1, cert2, cert3}, true},
|
||||
{"example.org", key1, [][]byte{cert1, {1}}, false},
|
||||
{"example.org", key1, [][]byte{{1}}, false},
|
||||
{"example.org", key1, [][]byte{cert2}, false},
|
||||
{"example.org", key2, [][]byte{cert1}, false},
|
||||
{"example.org", key1, [][]byte{cert3}, false},
|
||||
{"example.org", key3, [][]byte{cert1}, false},
|
||||
{"example.net", key1, [][]byte{cert1}, false},
|
||||
{"example.org", key1, [][]byte{early}, false},
|
||||
{"example.org", key1, [][]byte{expired}, false},
|
||||
}
|
||||
for i, test := range tt {
|
||||
leaf, err := validCert(test.domain, test.cert, test.key)
|
||||
if err != nil && test.ok {
|
||||
t.Errorf("%d: err = %v", i, err)
|
||||
}
|
||||
if err == nil && !test.ok {
|
||||
t.Errorf("%d: err is nil", i)
|
||||
}
|
||||
if err == nil && test.ok && leaf == nil {
|
||||
t.Errorf("%d: leaf is nil", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type cacheGetFunc func(ctx context.Context, key string) ([]byte, error)
|
||||
|
||||
func (f cacheGetFunc) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
return f(ctx, key)
|
||||
}
|
||||
|
||||
func (f cacheGetFunc) Put(ctx context.Context, key string, data []byte) error {
|
||||
return fmt.Errorf("unsupported Put of %q = %q", key, data)
|
||||
}
|
||||
|
||||
func (f cacheGetFunc) Delete(ctx context.Context, key string) error {
|
||||
return fmt.Errorf("unsupported Delete of %q", key)
|
||||
}
|
||||
|
||||
func TestManagerGetCertificateBogusSNI(t *testing.T) {
|
||||
m := Manager{
|
||||
Prompt: AcceptTOS,
|
||||
Cache: cacheGetFunc(func(ctx context.Context, key string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("cache.Get of %s", key)
|
||||
}),
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
wantErr string
|
||||
}{
|
||||
{"foo.com", "cache.Get of foo.com"},
|
||||
{"foo.com.", "cache.Get of foo.com"},
|
||||
{`a\b.com`, "acme/autocert: server name contains invalid character"},
|
||||
{`a/b.com`, "acme/autocert: server name contains invalid character"},
|
||||
{"", "acme/autocert: missing server name"},
|
||||
{"foo", "acme/autocert: server name component count invalid"},
|
||||
{".foo", "acme/autocert: server name component count invalid"},
|
||||
{"foo.", "acme/autocert: server name component count invalid"},
|
||||
{"fo.o", "cache.Get of fo.o"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
_, err := m.GetCertificate(&tls.ClientHelloInfo{ServerName: tt.name})
|
||||
got := fmt.Sprint(err)
|
||||
if got != tt.wantErr {
|
||||
t.Errorf("GetCertificate(SNI = %q) = %q; want %q", tt.name, got, tt.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
130
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/cache.go
сгенерированный
поставляемый
Обычный файл
130
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/cache.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,130 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// ErrCacheMiss is returned when a certificate is not found in cache.
|
||||
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
|
||||
|
||||
// Cache is used by Manager to store and retrieve previously obtained certificates
|
||||
// as opaque data.
|
||||
//
|
||||
// The key argument of the methods refers to a domain name but need not be an FQDN.
|
||||
// Cache implementations should not rely on the key naming pattern.
|
||||
type Cache interface {
|
||||
// Get returns a certificate data for the specified key.
|
||||
// If there's no such key, Get returns ErrCacheMiss.
|
||||
Get(ctx context.Context, key string) ([]byte, error)
|
||||
|
||||
// Put stores the data in the cache under the specified key.
|
||||
// Underlying implementations may use any data storage format,
|
||||
// as long as the reverse operation, Get, results in the original data.
|
||||
Put(ctx context.Context, key string, data []byte) error
|
||||
|
||||
// Delete removes a certificate data from the cache under the specified key.
|
||||
// If there's no such key in the cache, Delete returns nil.
|
||||
Delete(ctx context.Context, key string) error
|
||||
}
|
||||
|
||||
// DirCache implements Cache using a directory on the local filesystem.
|
||||
// If the directory does not exist, it will be created with 0700 permissions.
|
||||
type DirCache string
|
||||
|
||||
// Get reads a certificate data from the specified file name.
|
||||
func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
|
||||
name = filepath.Join(string(d), name)
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
done = make(chan struct{})
|
||||
)
|
||||
go func() {
|
||||
data, err = ioutil.ReadFile(name)
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return nil, ErrCacheMiss
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
// Put writes the certificate data to the specified file name.
|
||||
// The file will be created with 0600 permissions.
|
||||
func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
|
||||
if err := os.MkdirAll(string(d), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
var err error
|
||||
go func() {
|
||||
defer close(done)
|
||||
var tmp string
|
||||
if tmp, err = d.writeTempFile(name, data); err != nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Don't overwrite the file if the context was canceled.
|
||||
default:
|
||||
newName := filepath.Join(string(d), name)
|
||||
err = os.Rename(tmp, newName)
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete removes the specified file name.
|
||||
func (d DirCache) Delete(ctx context.Context, name string) error {
|
||||
name = filepath.Join(string(d), name)
|
||||
var (
|
||||
err error
|
||||
done = make(chan struct{})
|
||||
)
|
||||
go func() {
|
||||
err = os.Remove(name)
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeTempFile writes b to a temporary file, closes the file and returns its path.
|
||||
func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) {
|
||||
// TempFile uses 0600 permissions
|
||||
f, err := ioutil.TempFile(string(d), prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := f.Write(b); err != nil {
|
||||
f.Close()
|
||||
return "", err
|
||||
}
|
||||
return f.Name(), f.Close()
|
||||
}
|
||||
58
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/cache_test.go
сгенерированный
поставляемый
Обычный файл
58
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/cache_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,58 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// make sure DirCache satisfies Cache interface
|
||||
var _ Cache = DirCache("/")
|
||||
|
||||
func TestDirCache(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "autocert")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
dir = filepath.Join(dir, "certs") // a nonexistent dir
|
||||
cache := DirCache(dir)
|
||||
ctx := context.Background()
|
||||
|
||||
// test cache miss
|
||||
if _, err := cache.Get(ctx, "nonexistent"); err != ErrCacheMiss {
|
||||
t.Errorf("get: %v; want ErrCacheMiss", err)
|
||||
}
|
||||
|
||||
// test put/get
|
||||
b1 := []byte{1}
|
||||
if err := cache.Put(ctx, "dummy", b1); err != nil {
|
||||
t.Fatalf("put: %v", err)
|
||||
}
|
||||
b2, err := cache.Get(ctx, "dummy")
|
||||
if err != nil {
|
||||
t.Fatalf("get: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(b1, b2) {
|
||||
t.Errorf("b1 = %v; want %v", b1, b2)
|
||||
}
|
||||
name := filepath.Join(dir, "dummy")
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// test delete
|
||||
if err := cache.Delete(ctx, "dummy"); err != nil {
|
||||
t.Fatalf("delete: %v", err)
|
||||
}
|
||||
if _, err := cache.Get(ctx, "dummy"); err != ErrCacheMiss {
|
||||
t.Errorf("get: %v; want ErrCacheMiss", err)
|
||||
}
|
||||
}
|
||||
35
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/example_test.go
сгенерированный
поставляемый
Обычный файл
35
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/example_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,35 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package autocert_test
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
func ExampleNewListener() {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, TLS user! Your config: %+v", r.TLS)
|
||||
})
|
||||
log.Fatal(http.Serve(autocert.NewListener("example.com"), mux))
|
||||
}
|
||||
|
||||
func ExampleManager() {
|
||||
m := autocert.Manager{
|
||||
Cache: autocert.DirCache("secret-dir"),
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist("example.org"),
|
||||
}
|
||||
s := &http.Server{
|
||||
Addr: ":https",
|
||||
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
|
||||
}
|
||||
s.ListenAndServeTLS("", "")
|
||||
}
|
||||
160
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/listener.go
сгенерированный
поставляемый
Обычный файл
160
vendor/github.com/miekg/dns/vendor/golang.org/x/crypto/acme/autocert/listener.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,160 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewListener returns a net.Listener that listens on the standard TLS
|
||||
// port (443) on all interfaces and returns *tls.Conn connections with
|
||||
// LetsEncrypt certificates for the provided domain or domains.
|
||||
//
|
||||
// It enables one-line HTTPS servers:
|
||||
//
|
||||
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
|
||||
//
|
||||
// NewListener is a convenience function for a common configuration.
|
||||
// More complex or custom configurations can use the autocert.Manager
|
||||
// type instead.
|
||||
//
|
||||
// Use of this function implies acceptance of the LetsEncrypt Terms of
|
||||
// Service. If domains is not empty, the provided domains are passed
|
||||
// to HostWhitelist. If domains is empty, the listener will do
|
||||
// LetsEncrypt challenges for any requested domain, which is not
|
||||
// recommended.
|
||||
//
|
||||
// Certificates are cached in a "golang-autocert" directory under an
|
||||
// operating system-specific cache or temp directory. This may not
|
||||
// be suitable for servers spanning multiple machines.
|
||||
//
|
||||
// The returned listener uses a *tls.Config that enables HTTP/2, and
|
||||
// should only be used with servers that support HTTP/2.
|
||||
//
|
||||
// The returned Listener also enables TCP keep-alives on the accepted
|
||||
// connections. The returned *tls.Conn are returned before their TLS
|
||||
// handshake has completed.
|
||||
func NewListener(domains ...string) net.Listener {
|
||||
m := &Manager{
|
||||
Prompt: AcceptTOS,
|
||||
}
|
||||
if len(domains) > 0 {
|
||||
m.HostPolicy = HostWhitelist(domains...)
|
||||
}
|
||||
dir := cacheDir()
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
log.Printf("warning: autocert.NewListener not using a cache: %v", err)
|
||||
} else {
|
||||
m.Cache = DirCache(dir)
|
||||
}
|
||||
return m.Listener()
|
||||
}
|
||||
|
||||
// Listener listens on the standard TLS port (443) on all interfaces
|
||||
// and returns a net.Listener returning *tls.Conn connections.
|
||||
//
|
||||
// The returned listener uses a *tls.Config that enables HTTP/2, and
|
||||
// should only be used with servers that support HTTP/2.
|
||||
//
|
||||
// The returned Listener also enables TCP keep-alives on the accepted
|
||||
// connections. The returned *tls.Conn are returned before their TLS
|
||||
// handshake has completed.
|
||||
//
|
||||
// Unlike NewListener, it is the caller's responsibility to initialize
|
||||
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
|
||||
func (m *Manager) Listener() net.Listener {
|
||||
ln := &listener{
|
||||
m: m,
|
||||
conf: &tls.Config{
|
||||
GetCertificate: m.GetCertificate, // bonus: panic on nil m
|
||||
NextProtos: []string{"h2", "http/1.1"}, // Enable HTTP/2
|
||||
},
|
||||
}
|
||||
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
||||
return ln
|
||||
}
|
||||
|
||||
type listener struct {
|
||||
m *Manager
|
||||
conf *tls.Config
|
||||
|
||||
tcpListener net.Listener
|
||||
tcpListenErr error
|
||||
}
|
||||
|
||||
func (ln *listener) Accept() (net.Conn, error) {
|
||||
if ln.tcpListenErr != nil {
|
||||
return nil, ln.tcpListenErr
|
||||
}
|
||||
conn, err := ln.tcpListener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tcpConn := conn.(*net.TCPConn)
|
||||
|
||||
// Because Listener is a convenience function, help out with
|
||||
// this too. This is not possible for the caller to set once
|
||||
// we return a *tcp.Conn wrapping an inaccessible net.Conn.
|
||||
// If callers don't want this, they can do things the manual
|
||||
// way and tweak as needed. But this is what net/http does
|
||||
// itself, so copy that. If net/http changes, we can change
|
||||
// here too.
|
||||
tcpConn.SetKeepAlive(true)
|
||||
tcpConn.SetKeepAlivePeriod(3 * time.Minute)
|
||||
|
||||
return tls.Server(tcpConn, ln.conf), nil
|
||||
}
|
||||
|
||||
func (ln *listener) Addr() net.Addr {
|
||||
if ln.tcpListener != nil {
|
||||
return ln.tcpListener.Addr()
|
||||
}
|
||||
// net.Listen failed. Return something non-nil in case callers
|
||||
// call Addr before Accept:
|
||||
return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443}
|
||||
}
|
||||
|
||||
func (ln *listener) Close() error {
|
||||
if ln.tcpListenErr != nil {
|
||||
return ln.tcpListenErr
|
||||
}
|
||||
return ln.tcpListener.Close()
|
||||
}
|
||||
|
||||
func homeDir() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
||||
}
|
||||
if h := os.Getenv("HOME"); h != "" {
|
||||
return h
|
||||
}
|
||||
return "/"
|
||||
}
|
||||
|
||||
func cacheDir() string {
|
||||
const base = "golang-autocert"
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return filepath.Join(homeDir(), "Library", "Caches", base)
|
||||
case "windows":
|
||||
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
|
||||
if v := os.Getenv(ev); v != "" {
|
||||
return filepath.Join(v, base)
|
||||
}
|
||||
}
|
||||
// Worst case:
|
||||
return filepath.Join(homeDir(), base)
|
||||
}
|
||||
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
|
||||
return filepath.Join(xdg, base)
|
||||
}
|
||||
return filepath.Join(homeDir(), ".cache", base)
|
||||
}
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user