mirror of
https://github.com/AdguardTeam/dnsproxy.git
synced 2026-10-12 10:09:55 +00:00
Pull request: AG-24891: skel-scripts
Merge in GO/dnsproxy from AG-24891-scripts to master
Squashed commit of the following:
commit 76d9aa9aef37f67657966d4d3faa95c85f731603
Author: Dimitry Kolyshev <[email protected]>
Date: Thu Aug 17 15:08:01 2023 +0300
all: imp code
commit 313063650c7c406c934153961cd06bbd7f1eb301
Author: Dimitry Kolyshev <[email protected]>
Date: Thu Aug 17 11:10:54 2023 +0300
all: tests timeout
commit e71a409731048b9dc1a9aac97e6ae4df78497253
Merge: 4c29792 d1469a4
Author: Dimitry Kolyshev <[email protected]>
Date: Thu Aug 17 10:47:54 2023 +0300
Merge remote-tracking branch 'origin/master' into AG-24891-scripts
commit 4c29792c3d63ea6e3a866d0ff18aabbc7e785aa4
Author: Dimitry Kolyshev <[email protected]>
Date: Thu Aug 17 10:46:58 2023 +0300
all: scripts imp
commit 5fc221435349b745166ba31f3d1603f50b9dd7f9
Author: Dimitry Kolyshev <[email protected]>
Date: Wed Aug 16 16:59:56 2023 +0300
all: scripts imp
commit 8a332f751a785c2d651caa2218423ad45007b5fa
Author: Dimitry Kolyshev <[email protected]>
Date: Wed Aug 16 16:53:08 2023 +0300
docs: imp
commit 4519f2aa2e82f1c263222909cdec31f6f8eb7691
Author: Dimitry Kolyshev <[email protected]>
Date: Wed Aug 16 16:50:26 2023 +0300
tools: upd
commit 719b7adb76ec02f0d36bd5ecdaf478f0b91b0171
Author: Dimitry Kolyshev <[email protected]>
Date: Wed Aug 16 16:49:31 2023 +0300
all: scripts
commit 48d6c1b58c1369603048481fcc9dd3e1f578a9b1
Author: Dimitry Kolyshev <[email protected]>
Date: Wed Aug 16 16:48:13 2023 +0300
all: scripts docker
commit 7f5446faf72e6e74fd3cf4a6681024eb1670723c
Author: Dimitry Kolyshev <[email protected]>
Date: Wed Aug 16 16:41:13 2023 +0300
all: scripts release
commit 2a85a8f2f2fd36d154b2262c07936d2387d113ea
Author: Dimitry Kolyshev <[email protected]>
Date: Wed Aug 16 16:27:57 2023 +0300
all: scripts build
commit 7c25f8ffb25713134d9c77e5a9f20c492cd19588
Author: Dimitry Kolyshev <[email protected]>
Date: Wed Aug 16 15:36:41 2023 +0300
all: scripts
commit 5ee0589b8044f8a86b1dc91e2de8277eb7b49593
Author: Dimitry Kolyshev <[email protected]>
Date: Tue Aug 15 17:11:55 2023 +0300
all: tools
This commit is contained in:
+12
-4
@@ -1,10 +1,18 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
.vscode
|
||||
# Please, DO NOT put your text editors' temporary files here. The more are
|
||||
# added, the harder it gets to maintain and manage projects' gitignores. Put
|
||||
# them into your global gitignore file instead.
|
||||
#
|
||||
# See https://stackoverflow.com/a/7335487/1892060.
|
||||
#
|
||||
# Only build, run, and test outputs here. Sorted. With negations at the
|
||||
# bottom to make sure they take effect.
|
||||
*.out
|
||||
*.test
|
||||
/bin/
|
||||
build
|
||||
dnsproxy
|
||||
dnsproxy.exe
|
||||
example.crt
|
||||
example.key
|
||||
coverage.txt
|
||||
config.yaml
|
||||
config.yaml
|
||||
|
||||
@@ -11,43 +11,64 @@
|
||||
#
|
||||
# See https://unix.stackexchange.com/q/646255/105635.
|
||||
GO.MACRO = $${GO:-go}
|
||||
VERBOSE.MACRO = $${VERBOSE:-0}
|
||||
|
||||
BRANCH = $$( git rev-parse --abbrev-ref HEAD )
|
||||
GOAMD64 = v1
|
||||
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
|
||||
COMMIT = $$( git rev-parse --short HEAD )
|
||||
DIST_DIR=build
|
||||
DIST_DIR = build
|
||||
OUT = dnsproxy
|
||||
RACE = 0
|
||||
VERBOSE = 0
|
||||
VERSION = dev
|
||||
REVISION = $$( git rev-parse --short HEAD )
|
||||
VERSION = 0
|
||||
|
||||
ENV = env\
|
||||
BRANCH="$(BRANCH)"\
|
||||
COMMIT='$(COMMIT)'\
|
||||
DIST_DIR='$(DIST_DIR)'\
|
||||
GO="$(GO.MACRO)"\
|
||||
GOAMD64='$(GOAMD64)'\
|
||||
GOPROXY='$(GOPROXY)'\
|
||||
OUT='$(OUT)'\
|
||||
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
|
||||
RACE='$(RACE)'\
|
||||
VERBOSE='$(VERBOSE)'\
|
||||
VERSION='$(VERSION)'\
|
||||
REVISION="$(REVISION)"\
|
||||
VERBOSE="$(VERBOSE.MACRO)"\
|
||||
VERSION="$(VERSION)"\
|
||||
|
||||
# Keep the line above blank.
|
||||
|
||||
# Keep this target first, so that a naked make invocation triggers
|
||||
# a full build.
|
||||
build: ; $(ENV) "$(SHELL)" ./scripts/make/build.sh
|
||||
# Keep this target first, so that a naked make invocation triggers a
|
||||
# full build.
|
||||
build: go-deps go-build
|
||||
|
||||
clean: ; $(ENV) $(GO.MACRO) clean && rm -f -r '$(DIST_DIR)'
|
||||
test: ; $(ENV) RACE='1' "$(SHELL)" ./scripts/make/test.sh
|
||||
init: ; git config core.hooksPath ./scripts/hooks
|
||||
|
||||
release: clean
|
||||
$(ENV) "$(SHELL)" ./scripts/make/release.sh
|
||||
test: go-test
|
||||
|
||||
docker: release
|
||||
$(ENV) "$(SHELL)" ./scripts/make/build-docker.sh
|
||||
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
|
||||
go-deps: ; $(ENV) "$(SHELL)" ./scripts/make/go-deps.sh
|
||||
go-lint: ; $(ENV) "$(SHELL)" ./scripts/make/go-lint.sh
|
||||
go-test: ; $(ENV) RACE='1' "$(SHELL)" ./scripts/make/go-test.sh
|
||||
go-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-tools.sh
|
||||
|
||||
# A quick check to make sure that all supported operating systems can be
|
||||
# typechecked and built successfully.
|
||||
os-check:
|
||||
go-check: go-tools go-lint go-test
|
||||
|
||||
# A quick check to make sure that all operating systems relevant to the
|
||||
# development of the project can be typechecked and built successfully.
|
||||
go-os-check:
|
||||
env GOOS='darwin' "$(GO.MACRO)" vet ./...
|
||||
env GOOS='freebsd' "$(GO.MACRO)" vet ./...
|
||||
env GOOS='linux' "$(GO.MACRO)" vet ./...
|
||||
env GOOS='openbsd' "$(GO.MACRO)" vet ./...
|
||||
env GOOS='windows' "$(GO.MACRO)" vet ./...
|
||||
|
||||
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
|
||||
|
||||
clean: ; $(ENV) $(GO.MACRO) clean && rm -f -r '$(DIST_DIR)'
|
||||
|
||||
release: clean
|
||||
$(ENV) "$(SHELL)" ./scripts/make/build-release.sh
|
||||
|
||||
docker: release
|
||||
$(ENV) "$(SHELL)" ./scripts/make/build-docker.sh
|
||||
|
||||
@@ -36,10 +36,10 @@ There are several options how to install `dnsproxy`.
|
||||
|
||||
## How to build
|
||||
|
||||
You will need Go v1.19 or later.
|
||||
You will need Go v1.20 or later.
|
||||
|
||||
```shell
|
||||
$ go build -mod=vendor
|
||||
$ make build
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -3,8 +3,8 @@ module github.com/AdguardTeam/dnsproxy
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/golibs v0.14.0
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.6
|
||||
github.com/AdguardTeam/golibs v0.15.0
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.7
|
||||
github.com/ameshkov/dnsstamps v1.0.3
|
||||
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0
|
||||
github.com/bluele/gcache v0.0.2
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
github.com/AdguardTeam/golibs v0.14.0 h1:/vfJshXBVaevMuBgzAIr+F64XdNqZL+j9F33GXJmgeQ=
|
||||
github.com/AdguardTeam/golibs v0.14.0/go.mod h1:hOtcb8dPfKcFjWTPA904hTA4dl1aWvzeebdJpE72IPk=
|
||||
github.com/AdguardTeam/golibs v0.15.0 h1:yOv/fdVkJIOWKr0NlUXAE9RA0DK9GKiBbiGzq47vY7o=
|
||||
github.com/AdguardTeam/golibs v0.15.0/go.mod h1:66ZLs8P7nk/3IfKroQ1rqtieLk+5eXYXMBKXlVL7KeI=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.6 h1:rE7AFbPWebq7me7RVS66Cipd1m7ef1yf2+C8QzjQXXE=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.6/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.7 h1:aEitLIR8HcxVodZ79mgRcCiC0A0I5kZPBuWGFwwulAw=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.7/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG++IsjHiI2p4bxALD1Y2nQKGMR5zDQM=
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Package tools and its main module are a nested internal module containing our
|
||||
// development tool dependencies.
|
||||
//
|
||||
// See https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module.
|
||||
package tools
|
||||
@@ -0,0 +1,34 @@
|
||||
module github.com/AdguardTeam/dnsproxy/internal/tools
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/fzipp/gocyclo v0.6.0
|
||||
github.com/golangci/misspell v0.4.1
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601
|
||||
github.com/kisielk/errcheck v1.6.3
|
||||
github.com/kyoh86/looppointer v0.2.1
|
||||
github.com/securego/gosec/v2 v2.16.0
|
||||
github.com/uudashr/gocognit v1.0.7
|
||||
golang.org/x/tools v0.12.0
|
||||
golang.org/x/vuln v1.0.0
|
||||
honnef.co/go/tools v0.4.3
|
||||
mvdan.cc/gofumpt v0.5.0
|
||||
mvdan.cc/unparam v0.0.0-20230815095028-f7c6fb1088f0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/kyoh86/nolint v0.0.1 // indirect
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20230811145659-89c5cff77bcb // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -0,0 +1,115 @@
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/golangci/misspell v0.4.1 h1:+y73iSicVy2PqyX7kmUefHusENlrP9YwuHZHPLGQj/g=
|
||||
github.com/golangci/misspell v0.4.1/go.mod h1:9mAN1quEo3DlpbaIKKyEvRxK1pwqR9s/Sea1bJCtlNI=
|
||||
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 h1:mrEEilTAUmaAORhssPPkxj84TsHrPMLBGW2Z4SoTxm8=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
|
||||
github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8=
|
||||
github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kyoh86/looppointer v0.2.1 h1:Jx9fnkBj/JrIryBLMTYNTj9rvc2SrPS98Dg0w7fxdJg=
|
||||
github.com/kyoh86/looppointer v0.2.1/go.mod h1:q358WcM8cMWU+5vzqukvaZtnJi1kw/MpRHQm3xvTrjw=
|
||||
github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU=
|
||||
github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
|
||||
github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/securego/gosec/v2 v2.16.0 h1:Pi0JKoasQQ3NnoRao/ww/N/XdynIB9NRYYZT5CyOs5U=
|
||||
github.com/securego/gosec/v2 v2.16.0/go.mod h1:xvLcVZqUfo4aAQu56TNv7/Ltz6emAOQAEsrZrt7uGlI=
|
||||
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/uudashr/gocognit v1.0.7 h1:e9aFXgKgUJrQ5+bs61zBigmj7bFJ/5cC6HmMahVzuDo=
|
||||
github.com/uudashr/gocognit v1.0.7/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s=
|
||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230811145659-89c5cff77bcb h1:v3JOchFBzuOEFQgVl0t5JnLg3yx29q2e1IjrEovxAt4=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/vuln v1.0.0 h1:tYLAU3jD9LQr98Y+3el06lWyGMCnvzw06PIWP3LIy7g=
|
||||
golang.org/x/vuln v1.0.0/go.mod h1:V0eyhHwaAaHrt42J9bgrN6rd12f6GU4T0Lu0ex2wDg4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.4.3 h1:o/n5/K5gXqk8Gozvs2cnL0F2S1/g1vcGCAx2vETjITw=
|
||||
honnef.co/go/tools v0.4.3/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA=
|
||||
mvdan.cc/gofumpt v0.5.0 h1:0EQ+Z56k8tXjj/6TQD25BFNKQXpCvT0rnansIc7Ug5E=
|
||||
mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js=
|
||||
mvdan.cc/unparam v0.0.0-20230815095028-f7c6fb1088f0 h1:NAENkqZ+Xofhqs4R4Af+i3HpZj1M23SFn/lHfRh1D4E=
|
||||
mvdan.cc/unparam v0.0.0-20230815095028-f7c6fb1088f0/go.mod h1:flQN1deud3vIpPdF88533Lpp/MvzGLgPIPjB1kgBf4I=
|
||||
@@ -0,0 +1,19 @@
|
||||
//go:build tools
|
||||
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "github.com/fzipp/gocyclo/cmd/gocyclo"
|
||||
_ "github.com/golangci/misspell/cmd/misspell"
|
||||
_ "github.com/gordonklaus/ineffassign"
|
||||
_ "github.com/kisielk/errcheck"
|
||||
_ "github.com/kyoh86/looppointer"
|
||||
_ "github.com/securego/gosec/v2/cmd/gosec"
|
||||
_ "github.com/uudashr/gocognit/cmd/gocognit"
|
||||
_ "golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness"
|
||||
_ "golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow"
|
||||
_ "golang.org/x/vuln/cmd/govulncheck"
|
||||
_ "honnef.co/go/tools/cmd/staticcheck"
|
||||
_ "mvdan.cc/gofumpt"
|
||||
_ "mvdan.cc/unparam"
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
// Package version contains dnsproxy version information.
|
||||
package version
|
||||
|
||||
// Versions
|
||||
|
||||
// These are set by the linker. Unfortunately, we cannot set constants during
|
||||
// linking, and Go doesn't have a concept of immutable variables, so to be
|
||||
// thorough we have to only export them through getters.
|
||||
var (
|
||||
branch string
|
||||
committime string
|
||||
revision string
|
||||
version string
|
||||
)
|
||||
|
||||
// Branch returns the compiled-in value of the Git branch.
|
||||
func Branch() (b string) {
|
||||
return branch
|
||||
}
|
||||
|
||||
// CommitTime returns the compiled-in value of the build time as a string.
|
||||
func CommitTime() (t string) {
|
||||
return committime
|
||||
}
|
||||
|
||||
// Revision returns the compiled-in value of the Git revision.
|
||||
func Revision() (r string) {
|
||||
return revision
|
||||
}
|
||||
|
||||
// Version returns the compiled-in value of the build version as a string.
|
||||
func Version() (v string) {
|
||||
return version
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/dnsproxy/internal/version"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
@@ -189,9 +190,6 @@ type Options struct {
|
||||
Version bool `yaml:"version" long:"version" description:"Prints the program version"`
|
||||
}
|
||||
|
||||
// VersionString will be set through ldflags, contains current version
|
||||
var VersionString = "dev" // nolint:gochecknoglobals
|
||||
|
||||
const (
|
||||
defaultLocalTimeout = 1 * time.Second
|
||||
)
|
||||
@@ -201,7 +199,8 @@ func main() {
|
||||
|
||||
for _, arg := range os.Args {
|
||||
if arg == "--version" {
|
||||
fmt.Printf("dnsproxy version: %s\n", VersionString)
|
||||
fmt.Printf("dnsproxy version: %s\n", version.Version())
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@@ -252,7 +251,7 @@ func run(options *Options) {
|
||||
|
||||
runPprof(options)
|
||||
|
||||
log.Info("Starting dnsproxy %s", VersionString)
|
||||
log.Info("Starting dnsproxy %s", version.Version())
|
||||
|
||||
// Prepare the proxy server and its configuration.
|
||||
config := createProxyConfig(options)
|
||||
|
||||
@@ -15,7 +15,7 @@ readonly debug_flags
|
||||
set -e -f -u
|
||||
|
||||
# Require these to be set.
|
||||
commit="${COMMIT:?please set COMMIT}"
|
||||
commit="${REVISION:?please set REVISION}"
|
||||
dist_dir="${DIST_DIR:?please set DIST_DIR}"
|
||||
version="${VERSION:?please set VERSION}"
|
||||
readonly commit dist_dir version
|
||||
|
||||
@@ -113,7 +113,7 @@ build() {
|
||||
VERBOSE="$(( verbose - 1 ))"\
|
||||
VERSION="$version"\
|
||||
OUT="$build_output"\
|
||||
sh ./scripts/make/build.sh\
|
||||
sh ./scripts/make/go-build.sh\
|
||||
;
|
||||
|
||||
log "$build_output"
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
|
||||
if [ "$verbose" -gt '1' ]
|
||||
then
|
||||
env
|
||||
set -x
|
||||
v_flags='-v=1'
|
||||
x_flags='-x=1'
|
||||
elif [ "$verbose" -gt '0' ]
|
||||
then
|
||||
set -x
|
||||
v_flags='-v=1'
|
||||
x_flags='-x=0'
|
||||
else
|
||||
set +x
|
||||
v_flags='-v=0'
|
||||
x_flags='-x=0'
|
||||
fi
|
||||
readonly x_flags v_flags
|
||||
|
||||
set -e -f -u
|
||||
|
||||
go="${GO:-go}"
|
||||
readonly go
|
||||
|
||||
ldflags="-s -w -X main.VersionString=${VERSION:-dev}"
|
||||
readonly ldflags
|
||||
|
||||
parallelism="${PARALLELISM:-}"
|
||||
readonly parallelism
|
||||
|
||||
# Use GOFLAGS for -p, because -p=0 simply disables the build instead of leaving
|
||||
# the default value.
|
||||
if [ "${parallelism}" != '' ]
|
||||
then
|
||||
GOFLAGS="${GOFLAGS:-} -p=${parallelism}"
|
||||
fi
|
||||
readonly GOFLAGS
|
||||
export GOFLAGS
|
||||
|
||||
# Allow users to specify a different output name.
|
||||
out="${OUT:-dnsproxy}"
|
||||
readonly out
|
||||
|
||||
o_flags="-o=${out}"
|
||||
readonly o_flags
|
||||
|
||||
# Allow users to enable the race detector. Unfortunately, that means that cgo
|
||||
# must be enabled.
|
||||
if [ "${RACE:-0}" -eq '0' ]
|
||||
then
|
||||
cgo_enabled='0'
|
||||
race_flags='--race=0'
|
||||
else
|
||||
cgo_enabled='1'
|
||||
race_flags='--race=1'
|
||||
fi
|
||||
readonly cgo_enabled race_flags
|
||||
|
||||
CGO_ENABLED="$cgo_enabled"
|
||||
GO111MODULE='on'
|
||||
export CGO_ENABLED GO111MODULE
|
||||
|
||||
"$go" build --ldflags "$ldflags" "$race_flags" --trimpath "$o_flags" "$v_flags" "$x_flags"
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/bin/sh
|
||||
|
||||
# dnsproxy build script
|
||||
#
|
||||
# The commentary in this file is written with the assumption that the reader
|
||||
# only has superficial knowledge of the POSIX shell language and alike.
|
||||
# Experienced readers may find it overly verbose.
|
||||
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a significant change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 1
|
||||
|
||||
# The default verbosity level is 0. Show every command that is run and every
|
||||
# package that is processed if the caller requested verbosity level greater than
|
||||
# 0. Also show subcommands if the requested verbosity level is greater than 1.
|
||||
# Otherwise, do nothing.
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
|
||||
if [ "$verbose" -gt '1' ]
|
||||
then
|
||||
env
|
||||
set -x
|
||||
v_flags='-v=1'
|
||||
x_flags='-x=1'
|
||||
elif [ "$verbose" -gt '0' ]
|
||||
then
|
||||
set -x
|
||||
v_flags='-v=1'
|
||||
x_flags='-x=0'
|
||||
else
|
||||
set +x
|
||||
v_flags='-v=0'
|
||||
x_flags='-x=0'
|
||||
fi
|
||||
readonly x_flags v_flags
|
||||
|
||||
# Exit the script if a pipeline fails (-e), prevent accidental filename
|
||||
# expansion (-f), and consider undefined variables as errors (-u).
|
||||
set -e -f -u
|
||||
|
||||
# Allow users to override the go command from environment. For example, to
|
||||
# build two releases with two different Go versions and test the difference.
|
||||
go="${GO:-go}"
|
||||
readonly go
|
||||
|
||||
# Set the build parameters unless already set.
|
||||
branch="${BRANCH:-$( git rev-parse --abbrev-ref HEAD )}"
|
||||
revision="${REVISION:-$( git rev-parse --short HEAD )}"
|
||||
version="${VERSION:-0}"
|
||||
readonly branch revision version
|
||||
|
||||
# Set date and time of the latest commit unless already set.
|
||||
committime="${SOURCE_DATE_EPOCH:-$( git log -1 --pretty=%ct )}"
|
||||
readonly committime
|
||||
|
||||
# Compile them in.
|
||||
version_pkg='github.com/AdguardTeam/dnsproxy/internal/version'
|
||||
ldflags="-s -w"
|
||||
ldflags="${ldflags} -X ${version_pkg}.branch=${branch}"
|
||||
ldflags="${ldflags} -X ${version_pkg}.committime=${committime}"
|
||||
ldflags="${ldflags} -X ${version_pkg}.revision=${revision}"
|
||||
ldflags="${ldflags} -X ${version_pkg}.version=${version}"
|
||||
readonly ldflags version_pkg
|
||||
|
||||
# Allow users to limit the build's parallelism.
|
||||
parallelism="${PARALLELISM:-}"
|
||||
readonly parallelism
|
||||
|
||||
# Use GOFLAGS for -p, because -p=0 simply disables the build instead of leaving
|
||||
# the default value.
|
||||
if [ "${parallelism}" != '' ]
|
||||
then
|
||||
GOFLAGS="${GOFLAGS:-} -p=${parallelism}"
|
||||
fi
|
||||
readonly GOFLAGS
|
||||
export GOFLAGS
|
||||
|
||||
# Allow users to specify a different output name.
|
||||
out="${OUT:-dnsproxy}"
|
||||
readonly out
|
||||
|
||||
o_flags="-o=${out}"
|
||||
readonly o_flags
|
||||
|
||||
# Allow users to enable the race detector. Unfortunately, that means that cgo
|
||||
# must be enabled.
|
||||
if [ "${RACE:-0}" -eq '0' ]
|
||||
then
|
||||
CGO_ENABLED='0'
|
||||
race_flags='--race=0'
|
||||
else
|
||||
CGO_ENABLED='1'
|
||||
race_flags='--race=1'
|
||||
fi
|
||||
readonly CGO_ENABLED race_flags
|
||||
export CGO_ENABLED
|
||||
|
||||
GO111MODULE='on'
|
||||
export GO111MODULE
|
||||
|
||||
if [ "$verbose" -gt '0' ]
|
||||
then
|
||||
"$go" env
|
||||
fi
|
||||
|
||||
"$go" build\
|
||||
--ldflags="$ldflags"\
|
||||
"$race_flags"\
|
||||
--trimpath\
|
||||
"$o_flags"\
|
||||
"$v_flags"\
|
||||
"$x_flags"
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a significant change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 1
|
||||
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
|
||||
if [ "$verbose" -gt '1' ]
|
||||
then
|
||||
env
|
||||
set -x
|
||||
x_flags='-x=1'
|
||||
elif [ "$verbose" -gt '0' ]
|
||||
then
|
||||
set -x
|
||||
x_flags='-x=0'
|
||||
else
|
||||
set +x
|
||||
x_flags='-x=0'
|
||||
fi
|
||||
readonly x_flags
|
||||
|
||||
set -e -f -u
|
||||
|
||||
go="${GO:-go}"
|
||||
readonly go
|
||||
|
||||
"$go" mod download "$x_flags"
|
||||
@@ -0,0 +1,196 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a significant change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 5
|
||||
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
|
||||
if [ "$verbose" -gt '0' ]
|
||||
then
|
||||
set -x
|
||||
fi
|
||||
|
||||
# Set $EXIT_ON_ERROR to zero to see all errors.
|
||||
if [ "${EXIT_ON_ERROR:-1}" -eq '0' ]
|
||||
then
|
||||
set +e
|
||||
else
|
||||
set -e
|
||||
fi
|
||||
|
||||
set -f -u
|
||||
|
||||
|
||||
|
||||
# Source the common helpers, including not_found and run_linter.
|
||||
. ./scripts/make/helper.sh
|
||||
|
||||
|
||||
|
||||
# Warnings
|
||||
|
||||
go_version="$( "${GO:-go}" version )"
|
||||
readonly go_version
|
||||
|
||||
go_min_version='go1.20.7'
|
||||
go_version_msg="
|
||||
warning: your go version (${go_version}) is different from the recommended minimal one (${go_min_version}).
|
||||
if you have the version installed, please set the GO environment variable.
|
||||
for example:
|
||||
|
||||
export GO='${go_min_version}'
|
||||
"
|
||||
readonly go_min_version go_version_msg
|
||||
|
||||
case "$go_version"
|
||||
in
|
||||
('go version'*"$go_min_version"*)
|
||||
# Go on.
|
||||
;;
|
||||
(*)
|
||||
echo "$go_version_msg" 1>&2
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
|
||||
# Simple analyzers
|
||||
|
||||
# blocklist_imports is a simple check against unwanted packages. The following
|
||||
# packages are banned:
|
||||
#
|
||||
# * Packages errors and log are replaced by our own packages in the
|
||||
# github.com/AdguardTeam/golibs module.
|
||||
#
|
||||
# * Package io/ioutil is soft-deprecated.
|
||||
#
|
||||
# * Package reflect is often an overkill, and for deep comparisons there are
|
||||
# much better functions in module github.com/google/go-cmp. Which is
|
||||
# already our indirect dependency and which may or may not enter the stdlib
|
||||
# at some point.
|
||||
#
|
||||
# See https://github.com/golang/go/issues/45200.
|
||||
#
|
||||
# * Package sort is replaced by golang.org/x/exp/slices.
|
||||
#
|
||||
# * Package unsafe is… unsafe.
|
||||
#
|
||||
# * Package golang.org/x/net/context has been moved into stdlib.
|
||||
#
|
||||
# Currently, the only standard exception are files generated from protobuf
|
||||
# schemas, which use package reflect. If your project needs more exceptions,
|
||||
# add and document them.
|
||||
#
|
||||
# TODO(a.garipov): Add deprecated packages golang.org/x/exp/maps and
|
||||
# golang.org/x/exp/slices once all projects switch to Go 1.21.
|
||||
blocklist_imports() {
|
||||
git grep\
|
||||
-e '[[:space:]]"errors"$'\
|
||||
-e '[[:space:]]"io/ioutil"$'\
|
||||
-e '[[:space:]]"log"$'\
|
||||
-e '[[:space:]]"reflect"$'\
|
||||
-e '[[:space:]]"sort"$'\
|
||||
-e '[[:space:]]"unsafe"$'\
|
||||
-e '[[:space:]]"golang.org/x/net/context"$'\
|
||||
-n\
|
||||
-- '*.go'\
|
||||
':!*.pb.go'\
|
||||
| sed -e 's/^\([^[:space:]]\+\)\(.*\)$/\1 blocked import:\2/'\
|
||||
|| exit 0
|
||||
}
|
||||
|
||||
# method_const is a simple check against the usage of some raw strings and
|
||||
# numbers where one should use named constants.
|
||||
method_const() {
|
||||
git grep -F\
|
||||
-e '"DELETE"'\
|
||||
-e '"GET"'\
|
||||
-e '"PATCH"'\
|
||||
-e '"POST"'\
|
||||
-e '"PUT"'\
|
||||
-n\
|
||||
-- '*.go'\
|
||||
| sed -e 's/^\([^[:space:]]\+\)\(.*\)$/\1 http method literal:\2/'\
|
||||
|| exit 0
|
||||
}
|
||||
|
||||
# underscores is a simple check against Go filenames with underscores. Add new
|
||||
# build tags and OS as you go. The main goal of this check is to discourage the
|
||||
# use of filenames like client_manager.go.
|
||||
underscores() {
|
||||
underscore_files="$(
|
||||
git ls-files '*_*.go'\
|
||||
| grep -F\
|
||||
-e '_darwin.go'\
|
||||
-e '_generate.go'\
|
||||
-e '_linux.go'\
|
||||
-e '_test.go'\
|
||||
-e '_unix.go'\
|
||||
-e '_windows.go'\
|
||||
-v\
|
||||
| sed -e 's/./\t\0/'
|
||||
)"
|
||||
readonly underscore_files
|
||||
|
||||
if [ "$underscore_files" != '' ]
|
||||
then
|
||||
echo 'found file names with underscores:'
|
||||
echo "$underscore_files"
|
||||
fi
|
||||
}
|
||||
|
||||
# TODO(a.garipov): Add an analyzer to look for `fallthrough`, `goto`, and `new`?
|
||||
|
||||
|
||||
|
||||
# Checks
|
||||
|
||||
run_linter -e blocklist_imports
|
||||
|
||||
run_linter -e method_const
|
||||
|
||||
run_linter -e underscores
|
||||
|
||||
run_linter -e gofumpt --extra -e -l .
|
||||
|
||||
# TODO(a.garipov): golint is deprecated, find a suitable replacement.
|
||||
|
||||
run_linter "$GO" vet ./...
|
||||
|
||||
run_linter govulncheck ./...
|
||||
|
||||
run_linter gocyclo --over 10 .
|
||||
|
||||
run_linter gocognit --over 10 .
|
||||
|
||||
run_linter ineffassign ./...
|
||||
|
||||
run_linter unparam ./...
|
||||
|
||||
git ls-files -- 'Makefile' '*.conf' '*.go' '*.mod' '*.sh' '*.yaml' '*.yml'\
|
||||
| xargs misspell --error\
|
||||
| sed -e 's/^/misspell: /'
|
||||
|
||||
run_linter looppointer ./...
|
||||
|
||||
run_linter nilness ./...
|
||||
|
||||
run_linter fieldalignment ./...
|
||||
|
||||
run_linter -e shadow --strict ./...
|
||||
|
||||
run_linter gosec --quiet ./...
|
||||
|
||||
run_linter errcheck ./...
|
||||
|
||||
staticcheck_matrix='
|
||||
darwin: GOOS=darwin
|
||||
linux: GOOS=linux
|
||||
windows: GOOS=windows
|
||||
'
|
||||
readonly staticcheck_matrix
|
||||
|
||||
echo "$staticcheck_matrix" | run_linter staticcheck --matrix ./...
|
||||
@@ -1,5 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a significant change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 1
|
||||
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
|
||||
@@ -35,11 +40,16 @@ fi
|
||||
readonly race_flags
|
||||
|
||||
go="${GO:-go}"
|
||||
readonly go
|
||||
|
||||
count_flags='--count=1'
|
||||
cover_flags='--coverprofile=./coverage.txt'
|
||||
shuffle_flags='--shuffle=on'
|
||||
readonly count_flags cover_flags shuffle_flags
|
||||
timeout_flags="${TIMEOUT_FLAGS:---timeout=2m}"
|
||||
readonly go count_flags shuffle_flags timeout_flags
|
||||
|
||||
"$go" test "$count_flags" "$cover_flags" "$race_flags" "$shuffle_flags" "$x_flags" "$v_flags" ./...
|
||||
"$go" test\
|
||||
"$count_flags"\
|
||||
"$race_flags"\
|
||||
"$shuffle_flags"\
|
||||
"$timeout_flags"\
|
||||
"$v_flags"\
|
||||
"$x_flags"\
|
||||
./...
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a significant change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 3
|
||||
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
|
||||
if [ "$verbose" -gt '1' ]
|
||||
then
|
||||
set -x
|
||||
v_flags='-v=1'
|
||||
x_flags='-x=1'
|
||||
elif [ "$verbose" -gt '0' ]
|
||||
then
|
||||
set -x
|
||||
v_flags='-v=1'
|
||||
x_flags='-x=0'
|
||||
else
|
||||
set +x
|
||||
v_flags='-v=0'
|
||||
x_flags='-x=0'
|
||||
fi
|
||||
readonly v_flags x_flags
|
||||
|
||||
set -e -f -u
|
||||
|
||||
go="${GO:-go}"
|
||||
readonly go
|
||||
|
||||
# Remove only the actual binaries in the bin/ directory, as developers may add
|
||||
# their own scripts there. Most commonly, a script named “go” for tools that
|
||||
# call the go binary and need a particular version.
|
||||
rm -f\
|
||||
bin/errcheck\
|
||||
bin/fieldalignment\
|
||||
bin/gocognit\
|
||||
bin/gocyclo\
|
||||
bin/gofumpt\
|
||||
bin/gosec\
|
||||
bin/govulncheck\
|
||||
bin/ineffassign\
|
||||
bin/looppointer\
|
||||
bin/misspell\
|
||||
bin/nilness\
|
||||
bin/shadow\
|
||||
bin/staticcheck\
|
||||
bin/unparam\
|
||||
;
|
||||
|
||||
# Reset GOARCH and GOOS to make sure we install the tools for the native
|
||||
# architecture even when we're cross-compiling the main binary, and also to
|
||||
# prevent the "cannot install cross-compiled binaries when GOBIN is set" error.
|
||||
env\
|
||||
GOARCH=""\
|
||||
GOBIN="${PWD}/bin"\
|
||||
GOOS=""\
|
||||
GOWORK='off'\
|
||||
"$go" install\
|
||||
--modfile=./internal/tools/go.mod\
|
||||
"$v_flags"\
|
||||
"$x_flags"\
|
||||
github.com/fzipp/gocyclo/cmd/gocyclo\
|
||||
github.com/golangci/misspell/cmd/misspell\
|
||||
github.com/gordonklaus/ineffassign\
|
||||
github.com/kisielk/errcheck\
|
||||
github.com/kyoh86/looppointer/cmd/looppointer\
|
||||
github.com/securego/gosec/v2/cmd/gosec\
|
||||
github.com/uudashr/gocognit/cmd/gocognit\
|
||||
golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment\
|
||||
golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness\
|
||||
golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow\
|
||||
golang.org/x/vuln/cmd/govulncheck\
|
||||
honnef.co/go/tools/cmd/staticcheck\
|
||||
mvdan.cc/gofumpt\
|
||||
mvdan.cc/unparam\
|
||||
;
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Common script helpers
|
||||
#
|
||||
# This file contains common script helpers. It should be sourced in scripts
|
||||
# right after the initial environment processing.
|
||||
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a remarkable change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 2
|
||||
|
||||
|
||||
|
||||
# Deferred helpers
|
||||
|
||||
not_found_msg='
|
||||
looks like a binary not found error.
|
||||
make sure you have installed the linter binaries using:
|
||||
|
||||
$ make go-tools
|
||||
'
|
||||
readonly not_found_msg
|
||||
|
||||
not_found() {
|
||||
if [ "$?" -eq '127' ]
|
||||
then
|
||||
# Code 127 is the exit status a shell uses when a command or a file is
|
||||
# not found, according to the Bash Hackers wiki.
|
||||
#
|
||||
# See https://wiki.bash-hackers.org/dict/terms/exit_status.
|
||||
echo "$not_found_msg" 1>&2
|
||||
fi
|
||||
}
|
||||
trap not_found EXIT
|
||||
|
||||
|
||||
|
||||
# Helpers
|
||||
|
||||
# run_linter runs the given linter with two additions:
|
||||
#
|
||||
# 1. If the first argument is "-e", run_linter exits with a nonzero exit code
|
||||
# if there is anything in the command's combined output.
|
||||
#
|
||||
# 2. In any case, run_linter adds the program's name to its combined output.
|
||||
run_linter() (
|
||||
set +e
|
||||
|
||||
if [ "$VERBOSE" -lt '2' ]
|
||||
then
|
||||
set +x
|
||||
fi
|
||||
|
||||
cmd="${1:?run_linter: provide a command}"
|
||||
shift
|
||||
|
||||
exit_on_output='0'
|
||||
if [ "$cmd" = '-e' ]
|
||||
then
|
||||
exit_on_output='1'
|
||||
cmd="${1:?run_linter: provide a command}"
|
||||
shift
|
||||
fi
|
||||
|
||||
readonly cmd
|
||||
|
||||
output="$( "$cmd" "$@" )"
|
||||
exitcode="$?"
|
||||
|
||||
readonly output
|
||||
|
||||
if [ "$output" != '' ]
|
||||
then
|
||||
echo "$output" | sed -e "s/^/${cmd}: /"
|
||||
|
||||
if [ "$exitcode" -eq '0' ] && [ "$exit_on_output" -eq '1' ]
|
||||
then
|
||||
exitcode='1'
|
||||
fi
|
||||
fi
|
||||
|
||||
return "$exitcode"
|
||||
)
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a remarkable change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 4
|
||||
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
|
||||
if [ "$verbose" -gt '0' ]
|
||||
then
|
||||
set -x
|
||||
fi
|
||||
|
||||
# Set $EXIT_ON_ERROR to zero to see all errors.
|
||||
if [ "${EXIT_ON_ERROR:-1}" -eq '0' ]
|
||||
then
|
||||
set +e
|
||||
else
|
||||
set -e
|
||||
fi
|
||||
|
||||
# We don't need glob expansions and we want to see errors about unset variables.
|
||||
set -f -u
|
||||
|
||||
# Source the common helpers, including not_found.
|
||||
. ./scripts/make/helper.sh
|
||||
|
||||
# Simple analyzers
|
||||
|
||||
# trailing_newlines is a simple check that makes sure that all plain-text files
|
||||
# have a trailing newlines to make sure that all tools work correctly with them.
|
||||
trailing_newlines() {
|
||||
nl="$( printf "\n" )"
|
||||
readonly nl
|
||||
|
||||
# NOTE: Adjust for your project.
|
||||
# TODO(d.kolyshev): Remove "vendor" after moving out from vendor dir usage.
|
||||
git ls-files\
|
||||
':!vendor/*'\
|
||||
| while read -r f
|
||||
do
|
||||
if [ "$( tail -c -1 "$f" )" != "$nl" ]
|
||||
then
|
||||
printf '%s: must have a trailing newline\n' "$f"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# trailing_whitespace is a simple check that makes sure that there are no
|
||||
# trailing whitespace in plain-text files.
|
||||
trailing_whitespace() {
|
||||
# NOTE: Adjust for your project.
|
||||
# TODO(d.kolyshev): Remove "vendor" after moving out from vendor dir usage.
|
||||
git ls-files\
|
||||
':!vendor/*'\
|
||||
| while read -r f
|
||||
do
|
||||
grep -e '[[:space:]]$' -n -- "$f"\
|
||||
| sed -e "s:^:${f}\::" -e 's/ \+$/>>>&<<</'
|
||||
done
|
||||
}
|
||||
|
||||
run_linter -e trailing_newlines
|
||||
|
||||
run_linter -e trailing_whitespace
|
||||
|
||||
git ls-files -- '*.conf' '*.md' '*.txt' '*.yaml' '*.yml'\
|
||||
| xargs misspell --error\
|
||||
| sed -e 's/^/misspell: /'
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS 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.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package cache
|
||||
|
||||
// Config - configuration
|
||||
type Config struct {
|
||||
// Max. cache size (in bytes) of keys and values. Default: unlimited
|
||||
MaxSize uint
|
||||
|
||||
// Max. element size (in bytes). Default: =MaxSize
|
||||
MaxElementSize uint
|
||||
|
||||
// Max. elements number. Default: unlimited
|
||||
MaxCount uint
|
||||
|
||||
// When cache is full, the least recently used element is deleted automatically
|
||||
EnableLRU bool
|
||||
|
||||
// User callback function which is called after an element has been deleted automatically
|
||||
OnDelete onDeleteType
|
||||
}
|
||||
|
||||
// New - create cache object
|
||||
func New(conf Config) Cache {
|
||||
return newCache(conf)
|
||||
}
|
||||
|
||||
// Cache - interface
|
||||
type Cache interface {
|
||||
// Set data
|
||||
// Return FALSE if data was added; TRUE if data was replaced
|
||||
Set(key, val []byte) bool
|
||||
|
||||
// Get data
|
||||
// Return nil if item with this key doesn't exist
|
||||
Get(key []byte) []byte
|
||||
|
||||
// Delete data
|
||||
Del(key []byte)
|
||||
|
||||
// Clear all data and statistics
|
||||
Clear()
|
||||
|
||||
// Get statistics data
|
||||
Stats() Stats
|
||||
}
|
||||
|
||||
// Stats - counters
|
||||
type Stats struct {
|
||||
Count int
|
||||
Size int
|
||||
Hit int
|
||||
Miss int
|
||||
}
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type onDeleteType func(key, val []byte)
|
||||
|
||||
type cache struct {
|
||||
items map[string]*item
|
||||
|
||||
// LRU: for removing the least recently used item on reaching cache size limit
|
||||
// Note: slows down Get() due to an additional work with pointers
|
||||
// Example: [ (sentinel) <-> item1 <-> item2 <-> (sentinel) ]
|
||||
// When the item is accessed, it's moved to the end of the list.
|
||||
usage listItem
|
||||
|
||||
lock sync.Mutex
|
||||
size uint // current size in bytes (keys+values)
|
||||
|
||||
conf Config
|
||||
|
||||
// stats:
|
||||
miss int32 // number of misses
|
||||
hit int32 // number of hits
|
||||
}
|
||||
|
||||
type item struct {
|
||||
key []byte
|
||||
value []byte
|
||||
used listItem
|
||||
}
|
||||
|
||||
const maxUint = (1 << (unsafe.Sizeof(uint(0)) * 8)) - 1
|
||||
|
||||
func newCache(conf Config) *cache {
|
||||
c := cache{}
|
||||
c.items = make(map[string]*item)
|
||||
listInit(&c.usage)
|
||||
c.conf = conf
|
||||
if c.conf.MaxSize == 0 {
|
||||
c.conf.MaxSize = maxUint
|
||||
}
|
||||
if c.conf.MaxCount == 0 {
|
||||
c.conf.MaxCount = maxUint
|
||||
}
|
||||
if c.conf.MaxElementSize == 0 {
|
||||
c.conf.MaxElementSize = c.conf.MaxSize
|
||||
}
|
||||
if c.conf.MaxElementSize > c.conf.MaxSize {
|
||||
c.conf.MaxElementSize = c.conf.MaxSize
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *cache) Clear() {
|
||||
c.lock.Lock()
|
||||
c.items = make(map[string]*item)
|
||||
listInit(&c.usage)
|
||||
c.size = 0
|
||||
c.lock.Unlock()
|
||||
atomic.StoreInt32(&c.hit, 0)
|
||||
atomic.StoreInt32(&c.miss, 0)
|
||||
}
|
||||
|
||||
// Set value
|
||||
func (c *cache) Set(key, val []byte) bool {
|
||||
addSize := uint(len(key) + len(val))
|
||||
if addSize > c.conf.MaxElementSize {
|
||||
return false // too large data
|
||||
}
|
||||
|
||||
it := item{}
|
||||
it.key = key
|
||||
it.value = val
|
||||
|
||||
c.lock.Lock()
|
||||
|
||||
if !c.conf.EnableLRU &&
|
||||
(c.size+addSize > c.conf.MaxSize || uint(len(c.items)) == c.conf.MaxCount) {
|
||||
c.lock.Unlock()
|
||||
return false // cache is full
|
||||
}
|
||||
|
||||
for c.size+addSize > c.conf.MaxSize || uint(len(c.items)) == c.conf.MaxCount {
|
||||
first := listFirst(&c.usage)
|
||||
it := (*item)(structPtr(unsafe.Pointer(first), unsafe.Offsetof(item{}.used)))
|
||||
c.size -= uint(len(it.key) + len(it.value))
|
||||
listUnlink(first)
|
||||
delete(c.items, string(it.key))
|
||||
|
||||
if c.conf.OnDelete != nil {
|
||||
c.lock.Unlock()
|
||||
c.conf.OnDelete(it.key, it.value)
|
||||
c.lock.Lock()
|
||||
}
|
||||
}
|
||||
|
||||
if c.conf.EnableLRU {
|
||||
listAppend(&it.used, listLast(&c.usage))
|
||||
}
|
||||
|
||||
it2, exists := c.items[string(key)]
|
||||
if exists {
|
||||
listUnlink(&it2.used)
|
||||
c.size -= uint(len(it2.key) + len(it2.value))
|
||||
}
|
||||
c.items[string(key)] = &it
|
||||
c.size += addSize
|
||||
c.lock.Unlock()
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
// Get value
|
||||
func (c *cache) Get(key []byte) []byte {
|
||||
c.lock.Lock()
|
||||
val, ok := c.items[string(key)]
|
||||
if ok && c.conf.EnableLRU {
|
||||
listUnlink(&val.used)
|
||||
listAppend(&val.used, listLast(&c.usage))
|
||||
}
|
||||
c.lock.Unlock()
|
||||
if !ok {
|
||||
atomic.AddInt32(&c.miss, 1)
|
||||
return nil
|
||||
}
|
||||
atomic.AddInt32(&c.hit, 1)
|
||||
return val.value
|
||||
}
|
||||
|
||||
// Del - delete element
|
||||
func (c *cache) Del(key []byte) {
|
||||
c.lock.Lock()
|
||||
it, ok := c.items[string(key)]
|
||||
if !ok {
|
||||
c.lock.Unlock()
|
||||
return
|
||||
}
|
||||
listUnlink(&it.used)
|
||||
c.size -= uint(len(it.key) + len(it.value))
|
||||
delete(c.items, string(key))
|
||||
c.lock.Unlock()
|
||||
}
|
||||
|
||||
// GetStats - get counters
|
||||
func (c *cache) Stats() Stats {
|
||||
s := Stats{}
|
||||
s.Count = len(c.items)
|
||||
s.Size = int(c.size)
|
||||
s.Hit = int(atomic.LoadInt32(&c.hit))
|
||||
s.Miss = int(atomic.LoadInt32(&c.miss))
|
||||
return s
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
// Package cache provides a simple LRU cache implementation
|
||||
package cache
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
// Double-linked list
|
||||
// User adds listItem object into his structure
|
||||
// and uses structPtr() to get the pointer to his object by the pointer to listItem object.
|
||||
|
||||
package cache
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type listItem struct {
|
||||
next *listItem
|
||||
prev *listItem
|
||||
}
|
||||
|
||||
// initialize list
|
||||
func listInit(l *listItem) {
|
||||
l.next = l
|
||||
l.prev = l
|
||||
}
|
||||
|
||||
// first list item
|
||||
func listFirst(l *listItem) *listItem {
|
||||
return l.next
|
||||
}
|
||||
|
||||
// last list item
|
||||
func listLast(l *listItem) *listItem {
|
||||
return l.prev
|
||||
}
|
||||
|
||||
// link 2 items to each other
|
||||
func listLink2(l, r *listItem) {
|
||||
l.next = r
|
||||
r.prev = l
|
||||
}
|
||||
|
||||
// unlink
|
||||
func listUnlink(item *listItem) {
|
||||
listLink2(item.prev, item.next)
|
||||
}
|
||||
|
||||
// append
|
||||
func listAppend(item, after *listItem) {
|
||||
listLink2(item, after.next)
|
||||
listLink2(after, item)
|
||||
}
|
||||
|
||||
// Get pointer to structure object by pointer and offset to its field
|
||||
// e.g.:
|
||||
// userObject := (*userStruct)(structPtr(unsafe.Pointer(listPtr), unsafe.Offsetof(userStruct{}.listName)))
|
||||
func structPtr(fieldPtr unsafe.Pointer, fieldOff uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(fieldPtr) - fieldOff)
|
||||
}
|
||||
-293
@@ -1,293 +0,0 @@
|
||||
// Package errors is a drop-in replacement and extension of the Go standard
|
||||
// library's package [errors].
|
||||
package errors
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error is the constant error type.
|
||||
//
|
||||
// See https://dave.cheney.net/2016/04/07/constant-errors.
|
||||
type Error string
|
||||
|
||||
// Error implements the error interface for Error.
|
||||
func (err Error) Error() (msg string) {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
// Wrapper is a copy of the hidden wrapper interface from the Go standard
|
||||
// library. It is added here for tests, linting, etc.
|
||||
type Wrapper interface {
|
||||
Unwrap() error
|
||||
}
|
||||
|
||||
// As finds the first error in err's chain that matches target, and if so, sets
|
||||
// target to that error value and returns true. Otherwise, it returns false.
|
||||
//
|
||||
// It calls [errors.As] from the Go standard library.
|
||||
func As(err error, target any) (ok bool) {
|
||||
return stderrors.As(err, target)
|
||||
}
|
||||
|
||||
// Aser is a copy of the hidden aser interface from the Go standard library. It
|
||||
// is added here for tests, linting, etc.
|
||||
type Aser interface {
|
||||
As(target any) (ok bool)
|
||||
}
|
||||
|
||||
// Is reports whether any error in err's chain matches target.
|
||||
//
|
||||
// It calls [errors.Is] from the Go standard library.
|
||||
func Is(err, target error) (ok bool) {
|
||||
return stderrors.Is(err, target)
|
||||
}
|
||||
|
||||
// Iser is a copy of the hidden iser interface from the Go standard library. It
|
||||
// is added here for tests, linting, etc.
|
||||
type Iser interface {
|
||||
Is(target error) (ok bool)
|
||||
}
|
||||
|
||||
// New returns an error that formats as the given msg. Each call to New returns
|
||||
// a distinct error value even if the text is identical.
|
||||
//
|
||||
// It calls [errors.New] from the Go standard library.
|
||||
//
|
||||
// Deprecated: Use type [Error] and constant errors instead.
|
||||
func New(msg string) (err error) {
|
||||
return stderrors.New(msg)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type
|
||||
// contains an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
//
|
||||
// It calls [errors.Unwrap] from the Go standard library.
|
||||
func Unwrap(err error) (unwrapped error) {
|
||||
return stderrors.Unwrap(err)
|
||||
}
|
||||
|
||||
// Deferred is the interface for errors that were returned by cleanup functions,
|
||||
// such as Close. This is useful in APIs which desire to handle such errors
|
||||
// differently, for example to log them as warnings.
|
||||
//
|
||||
// Method Deferred returns a bool to mirror the behavior of types like
|
||||
// [net.Error] and allow implementations to decide if the error is a deferred
|
||||
// one dynamically. Users of this API must check it's return value as well as
|
||||
// the result [errors.As].
|
||||
//
|
||||
// if derr := errors.Deferred(nil); errors.As(err, &derr) && derr.Deferred() {
|
||||
// // …
|
||||
// }
|
||||
//
|
||||
// See https://dave.cheney.net/2014/12/24/inspecting-errors.
|
||||
type Deferred interface {
|
||||
error
|
||||
Deferred() (ok bool)
|
||||
}
|
||||
|
||||
// deferredError is a helper to implement Deferred.
|
||||
type deferredError struct {
|
||||
error
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Deferred = deferredError{}
|
||||
|
||||
// Deferred implements the [Deferred] interface for deferredError.
|
||||
func (err deferredError) Deferred() (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ error = deferredError{}
|
||||
|
||||
// Error implements the error interface for deferredError.
|
||||
func (err deferredError) Error() (msg string) {
|
||||
return fmt.Sprintf("deferred: %s", err.error)
|
||||
}
|
||||
|
||||
// Unwrap implements the [Wrapper] interface for deferredError.
|
||||
func (err deferredError) Unwrap() (unwrapped error) {
|
||||
return err.error
|
||||
}
|
||||
|
||||
// Pair is a pair of errors. The Returned error is the main error that has been
|
||||
// returned by a function. The Deferred error is the error returned by the
|
||||
// cleanup function, such as Close.
|
||||
//
|
||||
// In pairs returned from [WithDeferred], the Deferred error always implements
|
||||
// the [Deferred] interface.
|
||||
type Pair struct {
|
||||
Returned error
|
||||
Deferred error
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ error = (*Pair)(nil)
|
||||
|
||||
// Error implements the error interface for *Pair.
|
||||
func (err *Pair) Error() string {
|
||||
return fmt.Sprintf("returned: %q, deferred: %q", err.Returned, Unwrap(err.Deferred))
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Wrapper = (*Pair)(nil)
|
||||
|
||||
// Unwrap implements the [Wrapper] interface for *Pair. It returns the
|
||||
// Returned error.
|
||||
func (err *Pair) Unwrap() (unwrapped error) {
|
||||
return err.Returned
|
||||
}
|
||||
|
||||
// WithDeferred is a helper function for deferred errors. For example, to
|
||||
// preserve errors from the Close method, replace this:
|
||||
//
|
||||
// defer f.Close()
|
||||
//
|
||||
// With this:
|
||||
//
|
||||
// defer func() { err = errors.WithDeferred(err, f.Close()) }
|
||||
//
|
||||
// If returned is nil and deferred is non-nil, the returned error implements the
|
||||
// [Deferred] interface. If both returned and deferred are non-nil, result has
|
||||
// the underlying type of [*Pair].
|
||||
//
|
||||
// # Warning
|
||||
//
|
||||
// This function requires that there be only ONE error named "err" in the
|
||||
// function and that it is always the one that is returned. Example (Bad)
|
||||
// provides an example of the incorrect usage of WithDeferred.
|
||||
func WithDeferred(returned, deferred error) (result error) {
|
||||
if deferred == nil {
|
||||
return returned
|
||||
}
|
||||
|
||||
if returned == nil {
|
||||
return deferredError{error: deferred}
|
||||
}
|
||||
|
||||
return &Pair{
|
||||
Returned: returned,
|
||||
Deferred: deferredError{error: deferred},
|
||||
}
|
||||
}
|
||||
|
||||
// listError is an error containing several wrapped errors.
|
||||
type listError struct {
|
||||
msg string
|
||||
errs []error
|
||||
}
|
||||
|
||||
// List wraps several errors into a single error with an additional message.
|
||||
//
|
||||
// TODO(a.garipov): Deprecate once golibs switches to Go 1.20.
|
||||
func List(msg string, errs ...error) (err error) {
|
||||
return &listError{
|
||||
msg: msg,
|
||||
errs: errs,
|
||||
}
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ error = (*listError)(nil)
|
||||
|
||||
// Error implements the error interface for *listError.
|
||||
func (err *listError) Error() (msg string) {
|
||||
switch l := len(err.errs); l {
|
||||
case 0:
|
||||
return err.msg
|
||||
case 1:
|
||||
return fmt.Sprintf("%s: %s", err.msg, err.errs[0])
|
||||
default:
|
||||
b := &strings.Builder{}
|
||||
|
||||
// Here and further, ignore the errors since they are known to
|
||||
// be nil.
|
||||
_, _ = fmt.Fprintf(b, "%s: %d errors: ", err.msg, l)
|
||||
|
||||
for i, e := range err.errs {
|
||||
if i == l-1 {
|
||||
_, _ = fmt.Fprintf(b, "%q", e)
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(b, "%q, ", e)
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Wrapper = (*listError)(nil)
|
||||
|
||||
// Unwrap implements the Wrapper interface for *listError.
|
||||
func (err *listError) Unwrap() (unwrapped error) {
|
||||
if len(err.errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err.errs[0]
|
||||
}
|
||||
|
||||
// Annotate annotates the error with the message, unless the error is nil. The
|
||||
// last verb in format must be a verb compatible with errors, for example "%w".
|
||||
//
|
||||
// # In Defers
|
||||
//
|
||||
// The primary use case for this function is to simplify code like this:
|
||||
//
|
||||
// func (f *foo) doStuff(s string) (err error) {
|
||||
// defer func() {
|
||||
// if err != nil {
|
||||
// err = fmt.Errorf("bad foo %q: %w", s, err)
|
||||
// }
|
||||
// }()
|
||||
//
|
||||
// // …
|
||||
// }
|
||||
//
|
||||
// Instead, write:
|
||||
//
|
||||
// func (f *foo) doStuff(s string) (err error) {
|
||||
// defer func() { err = errors.Annotate(err, "bad foo %q: %w", s) }()
|
||||
//
|
||||
// // …
|
||||
// }
|
||||
//
|
||||
// # At The End Of Functions
|
||||
//
|
||||
// Another possible use case is to simplify final checks like this:
|
||||
//
|
||||
// func (f *foo) doStuff(s string) (err error) {
|
||||
// // …
|
||||
//
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("doing stuff with %s: %w", s, err)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// Instead, you could write:
|
||||
//
|
||||
// func (f *foo) doStuff(s string) (err error) {
|
||||
// // …
|
||||
//
|
||||
// return errors.Annotate(err, "doing stuff with %s: %w", s)
|
||||
// }
|
||||
//
|
||||
// # Warning
|
||||
//
|
||||
// This function requires that there be only ONE error named "err" in the
|
||||
// function and that it is always the one that is returned. Example (Bad)
|
||||
// provides an example of the incorrect usage of WithDeferred.
|
||||
func Annotate(err error, format string, args ...any) (annotated error) {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(format, append(args, err)...)
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
//go:build go1.20
|
||||
|
||||
package errors
|
||||
|
||||
import stderrors "errors"
|
||||
|
||||
// TODO(a.garipov): Move to errors.go and add examples once golibs switches to
|
||||
// Go 1.20.
|
||||
|
||||
// WrapperSlice is a copy of the hidden wrapper interface added to the Go standard
|
||||
// library in Go 1.20. It is added here for tests, linting, etc.
|
||||
type WrapperSlice interface {
|
||||
Unwrap() []error
|
||||
}
|
||||
|
||||
// Join returns an error that wraps the given errors. Any nil error values are
|
||||
// discarded. Join returns nil if errs contains no non-nil values. The error
|
||||
// formats as the concatenation of the strings obtained by calling the Error
|
||||
// method of each element of errs, with a newline between each string.
|
||||
//
|
||||
// It calls [errors.Join] from the Go standard library.
|
||||
func Join(errs ...error) error {
|
||||
return stderrors.Join(errs...)
|
||||
}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
// Package httphdr contains the names of HTTP headers.
|
||||
//
|
||||
// Please keep the values in their canonical form.
|
||||
package httphdr
|
||||
|
||||
// Common standard headers for authentication.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#authentication.
|
||||
const (
|
||||
Authorization = "Authorization"
|
||||
)
|
||||
|
||||
// Common standard headers for caching.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#caching.
|
||||
const (
|
||||
CacheControl = "Cache-Control"
|
||||
Expires = "Expires"
|
||||
Pragma = "Pragma"
|
||||
)
|
||||
|
||||
// Common standard headers for client hints.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#client_hints.
|
||||
const (
|
||||
SecGPC = "Sec-Gpc"
|
||||
)
|
||||
|
||||
// Common standard headers for cookie management.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#cookies.
|
||||
const (
|
||||
Cookie = "Cookie"
|
||||
SetCookie = "Set-Cookie"
|
||||
)
|
||||
|
||||
// Common standard headers for conditionals.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#conditionals.
|
||||
const (
|
||||
IfMatch = "If-Match"
|
||||
IfModifiedSince = "If-Modified-Since"
|
||||
IfNoneMatch = "If-None-Match"
|
||||
IfRange = "If-Range"
|
||||
IfUnmodifiedSince = "If-Unmodified-Since"
|
||||
LastModified = "Last-Modified"
|
||||
Vary = "Vary"
|
||||
)
|
||||
|
||||
// Common standard headers for content negotiation.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#content_negotiation.
|
||||
const (
|
||||
Accept = "Accept"
|
||||
AcceptEncoding = "Accept-Encoding"
|
||||
AcceptLanguage = "Accept-Language"
|
||||
)
|
||||
|
||||
// Common standard headers for CORS.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#cors.
|
||||
const (
|
||||
AccessControlAllowOrigin = "Access-Control-Allow-Origin"
|
||||
Origin = "Origin"
|
||||
)
|
||||
|
||||
// Common standard headers for controlling downloads.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#downloads.
|
||||
const (
|
||||
ContentDisposition = "Content-Disposition"
|
||||
)
|
||||
|
||||
// Common standard headers for fetch metadata.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#fetch_metadata_request_headers.
|
||||
const (
|
||||
SecFetchDest = "Sec-Fetch-Dest"
|
||||
)
|
||||
|
||||
// Common standard headers for message body information.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#message_body_information.
|
||||
const (
|
||||
ContentEncoding = "Content-Encoding"
|
||||
ContentLength = "Content-Length"
|
||||
ContentType = "Content-Type"
|
||||
)
|
||||
|
||||
// Common standard headers for proxies.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#proxies.
|
||||
const (
|
||||
Forwarded = "Forwarded"
|
||||
)
|
||||
|
||||
// Common standard headers for redirects.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#redirects
|
||||
const (
|
||||
Location = "Location"
|
||||
)
|
||||
|
||||
// Common standard headers for request contexts.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#request_context.
|
||||
const (
|
||||
Host = "Host"
|
||||
Referer = "Referer"
|
||||
UserAgent = "User-Agent"
|
||||
)
|
||||
|
||||
// Common standard headers for response contexts.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#response_context.
|
||||
const (
|
||||
Server = "Server"
|
||||
)
|
||||
|
||||
// Common standard headers for security.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#security.
|
||||
const (
|
||||
ContentSecurityPolicy = "Content-Security-Policy"
|
||||
ContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
|
||||
StrictTransportSecurity = "Strict-Transport-Security"
|
||||
)
|
||||
|
||||
// Common standard headers for transfer coding.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#transfer_coding.
|
||||
const (
|
||||
TransferEncoding = "Transfer-Encoding"
|
||||
Trailer = "Trailer"
|
||||
)
|
||||
|
||||
// Miscellaneous common standard headers.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#other.
|
||||
const (
|
||||
AltSvc = "Alt-Svc"
|
||||
Date = "Date"
|
||||
RetryAfter = "Retry-After"
|
||||
)
|
||||
|
||||
// Common deprecated headers.
|
||||
const (
|
||||
DNT = "Dnt"
|
||||
)
|
||||
|
||||
// Common extension headers.
|
||||
const (
|
||||
AdminToken = "Admin-Token"
|
||||
TrueClientIP = "True-Client-IP"
|
||||
|
||||
XError = "X-Error"
|
||||
XForwardedFor = "X-Forwarded-For"
|
||||
XForwardedHost = "X-Forwarded-Host"
|
||||
XForwardedProto = "X-Forwarded-Proto"
|
||||
XProjectName = "X-Project-Name"
|
||||
XProxyTarget = "X-Proxy-Target"
|
||||
XRealIP = "X-Real-Ip"
|
||||
XRequestID = "X-Request-Id"
|
||||
XRequestedWith = "X-Requested-With"
|
||||
)
|
||||
|
||||
// Common Cloudflare extension headers.
|
||||
const (
|
||||
CFConnectingIP = "Cf-Connecting-Ip"
|
||||
CFIPCountry = "Cf-Ipcountry"
|
||||
)
|
||||
-333
@@ -1,333 +0,0 @@
|
||||
// Package log contains necessary logging functions
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Level is the log level type.
|
||||
type Level uint32
|
||||
|
||||
// Level constants.
|
||||
const (
|
||||
ERROR Level = iota
|
||||
INFO
|
||||
DEBUG
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer for Level
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case DEBUG:
|
||||
return "debug"
|
||||
case INFO:
|
||||
return "info"
|
||||
case ERROR:
|
||||
return "error"
|
||||
default:
|
||||
panic(fmt.Sprintf("not a valid Level: %d", l))
|
||||
}
|
||||
}
|
||||
|
||||
// level is the current logging level. It must only be updated atomically.
|
||||
var level = uint32(INFO)
|
||||
|
||||
// Timer is a wrapper for time
|
||||
type Timer struct {
|
||||
start time.Time
|
||||
}
|
||||
|
||||
// StartTimer returns a Timer with a start time
|
||||
func StartTimer() Timer {
|
||||
return Timer{start: time.Now()}
|
||||
}
|
||||
|
||||
// LogElapsed writes to log message and elapsed time
|
||||
func (t *Timer) LogElapsed(message string, args ...any) {
|
||||
var buf strings.Builder
|
||||
buf.WriteString(message)
|
||||
buf.WriteString(fmt.Sprintf("; Elapsed time: %dms", int(time.Since(t.start)/time.Millisecond)))
|
||||
|
||||
pc := make([]uintptr, 10)
|
||||
runtime.Callers(2, pc)
|
||||
f := runtime.FuncForPC(pc[0])
|
||||
|
||||
levelStr := "info"
|
||||
if atomic.LoadUint32(&level) >= uint32(DEBUG) {
|
||||
levelStr = "debug"
|
||||
}
|
||||
writeLog(levelStr, f.Name(), buf.String(), args...)
|
||||
}
|
||||
|
||||
// Writer returns the output destination for the default logger.
|
||||
func Writer() io.Writer {
|
||||
return log.Writer()
|
||||
}
|
||||
|
||||
// SetLevel sets logging level.
|
||||
func SetLevel(l Level) {
|
||||
atomic.SwapUint32(&level, uint32(l))
|
||||
}
|
||||
|
||||
// GetLevel returns level
|
||||
func GetLevel() (l Level) {
|
||||
return Level(atomic.LoadUint32(&level))
|
||||
}
|
||||
|
||||
// These constants are the same as in the standard package "log".
|
||||
//
|
||||
// See the documentation for [log.Ldate], etc.
|
||||
const (
|
||||
Ldate = 1 << iota
|
||||
Ltime
|
||||
Lmicroseconds
|
||||
Llongfile
|
||||
Lshortfile
|
||||
LUTC
|
||||
Lmsgprefix
|
||||
LstdFlags = Ldate | Ltime
|
||||
)
|
||||
|
||||
// SetOutput sets output printing method
|
||||
func SetOutput(w io.Writer) {
|
||||
log.SetOutput(w)
|
||||
}
|
||||
|
||||
// SetFlags sets the output flags for the default logger. The flag bits are
|
||||
// Ldate, Ltime, and so on.
|
||||
func SetFlags(flags int) {
|
||||
log.SetFlags(flags)
|
||||
}
|
||||
|
||||
// Fatal writes to error log and exits application
|
||||
func Fatal(args ...any) {
|
||||
writeLog("fatal", "", "%s", fmt.Sprint(args...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fatalf writes to error log and exits application
|
||||
func Fatalf(format string, args ...any) {
|
||||
writeLog("fatal", "", format, args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Error writes to error log
|
||||
func Error(format string, args ...any) {
|
||||
writeLog("error", "", format, args...)
|
||||
}
|
||||
|
||||
// Panic is equivalent to Print() followed by a call to panic().
|
||||
func Panic(args ...any) {
|
||||
s := fmt.Sprint(args...)
|
||||
writeLog("panic", "", "%s", s)
|
||||
|
||||
panic(s)
|
||||
}
|
||||
|
||||
// Panicf is equivalent to Printf() followed by a call to panic().
|
||||
func Panicf(format string, args ...any) {
|
||||
s := fmt.Sprintf(format, args...)
|
||||
writeLog("panic", "", "%s", s)
|
||||
|
||||
panic(s)
|
||||
}
|
||||
|
||||
// Print writes to info log
|
||||
func Print(args ...any) {
|
||||
Info("%s", fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
// Printf writes to info log
|
||||
func Printf(format string, args ...any) {
|
||||
Info(format, args...)
|
||||
}
|
||||
|
||||
// Println writes to info log
|
||||
func Println(args ...any) {
|
||||
Info("%s", fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
// Info writes to info log
|
||||
func Info(format string, args ...any) {
|
||||
if atomic.LoadUint32(&level) >= uint32(INFO) {
|
||||
writeLog("info", "", format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug writes to debug log
|
||||
func Debug(format string, args ...any) {
|
||||
if atomic.LoadUint32(&level) >= uint32(DEBUG) {
|
||||
writeLog("debug", "", format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracef writes to debug log and adds the calling function's name
|
||||
func Tracef(format string, args ...any) {
|
||||
if atomic.LoadUint32(&level) >= uint32(DEBUG) {
|
||||
writeLog("debug", getCallerName(), format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Get goroutine ID
|
||||
// (https://blog.sgmansfield.com/2015/12/goroutine-ids/)
|
||||
func goroutineID() uint64 {
|
||||
b := make([]byte, 64)
|
||||
b = b[:runtime.Stack(b, false)]
|
||||
b = bytes.TrimPrefix(b, []byte("goroutine "))
|
||||
b = b[:bytes.IndexByte(b, ' ')]
|
||||
n, _ := strconv.ParseUint(string(b), 10, 64)
|
||||
return n
|
||||
}
|
||||
|
||||
// Construct a log message and write it
|
||||
// TIME PID#GOID [LEVEL] FUNCNAME(): TEXT
|
||||
func writeLog(levelStr, funcName, format string, args ...any) {
|
||||
var buf strings.Builder
|
||||
|
||||
if atomic.LoadUint32(&level) >= uint32(DEBUG) {
|
||||
buf.WriteString(fmt.Sprintf("%d#%d ", os.Getpid(), goroutineID()))
|
||||
}
|
||||
|
||||
buf.WriteString(fmt.Sprintf("[%s] ", levelStr))
|
||||
|
||||
if len(funcName) != 0 {
|
||||
buf.WriteString(fmt.Sprintf("%s(): ", funcName))
|
||||
}
|
||||
|
||||
buf.WriteString(fmt.Sprintf(format, args...))
|
||||
log.Println(buf.String())
|
||||
}
|
||||
|
||||
// StdLog returns a Go standard library logger that writes everything to logs
|
||||
// the way this library's logger would. This is useful for cases that require
|
||||
// a stdlib logger, for example http.Server.ErrorLog.
|
||||
func StdLog(prefix string, l Level) (std *log.Logger) {
|
||||
slw := &stdLogWriter{
|
||||
prefix: prefix,
|
||||
level: l,
|
||||
}
|
||||
|
||||
return log.New(slw, "", 0)
|
||||
}
|
||||
|
||||
type stdLogWriter struct {
|
||||
prefix string
|
||||
level Level
|
||||
}
|
||||
|
||||
func (w *stdLogWriter) Write(p []byte) (n int, err error) {
|
||||
if atomic.LoadUint32(&level) < uint32(w.level) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// The log.(*Logger).Output() method always appends a new line symbol to
|
||||
// the message before calling Write. We do the same thing, so trim it.
|
||||
p = bytes.TrimSuffix(p, []byte{'\n'})
|
||||
|
||||
var logFunc func(format string, args ...any)
|
||||
switch w.level {
|
||||
case ERROR:
|
||||
logFunc = Error
|
||||
case DEBUG:
|
||||
logFunc = Debug
|
||||
case INFO:
|
||||
logFunc = Info
|
||||
}
|
||||
|
||||
if prefix := w.prefix; prefix == "" {
|
||||
logFunc("%s", p)
|
||||
} else {
|
||||
logFunc("%s: %s", prefix, p)
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// OnPanic is a convenient deferred helper function to log a panic in
|
||||
// a goroutine. It should not be used where proper error handling is required.
|
||||
func OnPanic(prefix string) {
|
||||
if v := recover(); v != nil {
|
||||
if prefix != "" {
|
||||
Error("%s: recovered from panic: %v", prefix, v)
|
||||
debug.PrintStack()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Error("recovered from panic: %v", v)
|
||||
debug.PrintStack()
|
||||
}
|
||||
}
|
||||
|
||||
// OnPanicAndExit is a convenient deferred helper function to log a panic in
|
||||
// a goroutine. Once a panic happens, it logs it and then calls os.Exit with
|
||||
// the specified exit code.
|
||||
func OnPanicAndExit(prefix string, exitCode int) {
|
||||
if v := recover(); v != nil {
|
||||
if prefix != "" {
|
||||
Error("%s: panic encountered, exiting: %v", prefix, v)
|
||||
debug.PrintStack()
|
||||
|
||||
os.Exit(exitCode)
|
||||
return
|
||||
}
|
||||
|
||||
Error("panic encountered, exiting: %v", v)
|
||||
debug.PrintStack()
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
// OnCloserError is a convenient helper to log errors returned by io.Closer
|
||||
// The point is to not lose information from deferred Close calls. The error is
|
||||
// logged with the specified logging level.
|
||||
//
|
||||
// Instead of:
|
||||
//
|
||||
// defer f.Close()
|
||||
//
|
||||
// You can now write:
|
||||
//
|
||||
// defer log.OnCloserError(f, log.DEBUG)
|
||||
//
|
||||
// Note that if closer is nil, it is simply ignored.
|
||||
func OnCloserError(closer io.Closer, l Level) {
|
||||
if closer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := closer.Close()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if atomic.LoadUint32(&level) >= uint32(l) {
|
||||
format := "error occurred in a Close call: %v"
|
||||
writeLog(l.String(), getCallerName(), format, err)
|
||||
}
|
||||
}
|
||||
|
||||
// getCallerName tries to get the caller name.
|
||||
// Returns empty string if it fails.
|
||||
func getCallerName() string {
|
||||
pc := make([]uintptr, 10)
|
||||
|
||||
// This method is supposed to be used only from other log package
|
||||
// so it skips three calls.
|
||||
runtime.Callers(3, pc)
|
||||
if len(pc) > 0 {
|
||||
f := runtime.FuncForPC(pc[0])
|
||||
return f.Name()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
// Package mathutil contains generic helpers for common algorithms and
|
||||
// mathematic operations.
|
||||
package mathutil
|
||||
|
||||
import "golang.org/x/exp/constraints"
|
||||
|
||||
// Number is a type constraint for all numbers.
|
||||
type Number interface {
|
||||
constraints.Integer | constraints.Float
|
||||
}
|
||||
|
||||
// BoolToNumber returns 1 if cond is true and 0 otherwise.
|
||||
func BoolToNumber[T Number](cond bool) (res T) {
|
||||
if cond {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Max returns the larger of x or y.
|
||||
func Max[T constraints.Integer | ~string](x, y T) (res T) {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
|
||||
return y
|
||||
}
|
||||
|
||||
// Min returns the smaller of x or y.
|
||||
func Min[T constraints.Integer | ~string](x, y T) (res T) {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
|
||||
return y
|
||||
}
|
||||
-450
@@ -1,450 +0,0 @@
|
||||
// Package netutil contains common utilities for IP, MAC, and other kinds of
|
||||
// network addresses.
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// Various Network Address Utilities
|
||||
|
||||
// CloneURL returns a deep clone of u. The User pointer of clone is the same,
|
||||
// since a *url.Userinfo is effectively an immutable value.
|
||||
func CloneURL(u *url.URL) (clone *url.URL) {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cloneVal := *u
|
||||
|
||||
return &cloneVal
|
||||
}
|
||||
|
||||
// IsValidHostInnerRune returns true if r is a valid inner—that is, neither
|
||||
// initial nor final—rune for a hostname label.
|
||||
func IsValidHostInnerRune(r rune) (ok bool) {
|
||||
return r == '-' || IsValidHostOuterRune(r)
|
||||
}
|
||||
|
||||
// IsValidHostOuterRune returns true if r is a valid initial or final rune for
|
||||
// a hostname label.
|
||||
func IsValidHostOuterRune(r rune) (ok bool) {
|
||||
return (r >= 'a' && r <= 'z') ||
|
||||
(r >= 'A' && r <= 'Z') ||
|
||||
(r >= '0' && r <= '9')
|
||||
}
|
||||
|
||||
// JoinHostPort is a convenient wrapper for net.JoinHostPort with port of type
|
||||
// int. As opposed to net.JoinHostPort it also trims the host from square
|
||||
// brackets if any. This may be useful when passing url.URL.Host field
|
||||
// containing an IPv6 address.
|
||||
func JoinHostPort(host string, port int) (hostport string) {
|
||||
return net.JoinHostPort(strings.Trim(host, "[]"), strconv.Itoa(port))
|
||||
}
|
||||
|
||||
// SplitHostPort is a convenient wrapper for [net.SplitHostPort] with port of
|
||||
// type int.
|
||||
func SplitHostPort(hostport string) (host string, port int, err error) {
|
||||
var portStr string
|
||||
host, portStr, err = net.SplitHostPort(hostport)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
var portUint uint64
|
||||
portUint, err = strconv.ParseUint(portStr, 10, 16)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("parsing port: %w", err)
|
||||
}
|
||||
|
||||
return host, int(portUint), nil
|
||||
}
|
||||
|
||||
// SplitHost is a wrapper for [net.SplitHostPort] for cases when the hostport
|
||||
// may or may not contain a port.
|
||||
func SplitHost(hostport string) (host string, err error) {
|
||||
host, _, err = net.SplitHostPort(hostport)
|
||||
if err != nil {
|
||||
// Check for the missing port error. If it is that error, just
|
||||
// use the host as is.
|
||||
//
|
||||
// See the source code for net.SplitHostPort.
|
||||
const missingPort = "missing port in address"
|
||||
|
||||
addrErr := &net.AddrError{}
|
||||
if !errors.As(err, &addrErr) || addrErr.Err != missingPort {
|
||||
return "", err
|
||||
}
|
||||
|
||||
host = hostport
|
||||
}
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
// Subdomains returns all subdomains of domain, starting from domain itself.
|
||||
// domain must be a valid, non-fully-qualified domain name. If domain is empty,
|
||||
// Subdomains returns nil.
|
||||
func Subdomains(domain string) (sub []string) {
|
||||
if domain == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
sub = []string{domain}
|
||||
|
||||
for domain != "" {
|
||||
i := strings.IndexByte(domain, '.')
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
domain = domain[i+1:]
|
||||
sub = append(sub, domain)
|
||||
}
|
||||
|
||||
return sub
|
||||
}
|
||||
|
||||
// IsSubdomain returns true if domain is a subdomain of top. domain and top
|
||||
// should be valid domain names, qualified in the same manner, and have the same
|
||||
// letter case.
|
||||
func IsSubdomain(domain, top string) (ok bool) {
|
||||
// TODO(a.garipov): Use stringutil.HasSuffixFold when it is added.
|
||||
return len(domain) > len(top)+1 &&
|
||||
strings.HasSuffix(domain, top) &&
|
||||
domain[len(domain)-len(top)-1] == '.'
|
||||
}
|
||||
|
||||
// IsImmediateSubdomain returns true if domain is an immediate subdomain of top.
|
||||
// domain and top should be valid domain names, qualified in the same manner,
|
||||
// and have the same letter case.
|
||||
func IsImmediateSubdomain(domain, top string) (ok bool) {
|
||||
return IsSubdomain(domain, top) && strings.Count(domain, ".") == strings.Count(top, ".")+1
|
||||
}
|
||||
|
||||
// ValidateMAC returns an error if mac is not a valid EUI-48, EUI-64, or
|
||||
// 20-octet InfiniBand link-layer address.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*AddrError].
|
||||
func ValidateMAC(mac net.HardwareAddr) (err error) {
|
||||
defer makeAddrError(&err, mac.String(), AddrKindMAC)
|
||||
|
||||
switch l := len(mac); l {
|
||||
case 0:
|
||||
return &LengthError{
|
||||
Kind: AddrKindMAC,
|
||||
Length: 0,
|
||||
}
|
||||
case 6, 8, 20:
|
||||
return nil
|
||||
default:
|
||||
return &LengthError{
|
||||
Kind: AddrKindMAC,
|
||||
Allowed: []int{6, 8, 20},
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MaxDomainLabelLen is the maximum allowed length of a domain name label
|
||||
// according to [RFC 1035].
|
||||
//
|
||||
// [RFC 1035]: https://datatracker.ietf.org/doc/html/rfc1035
|
||||
const MaxDomainLabelLen = 63
|
||||
|
||||
// ValidateTLDLabel validates the top-level domain label in accordance to [RFC
|
||||
// 3696 Section 2]. In addition to the validations performed by
|
||||
// [ValidateHostnameLabel], it also checks that the label contains at least one
|
||||
// non-digit character.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*LabelError].
|
||||
//
|
||||
// [RFC 3696 Section 2]: https://datatracker.ietf.org/doc/html/rfc3696#section-2
|
||||
func ValidateTLDLabel(tld string) (err error) {
|
||||
defer makeLabelError(&err, tld, LabelKindTLD)
|
||||
|
||||
if err = ValidateHostnameLabel(tld); err != nil {
|
||||
err = errors.Unwrap(err)
|
||||
replaceKind(err, LabelKindTLD)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range tld {
|
||||
if r < '0' || r > '9' {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// There is a requirement for top-level domain label to contain at least one
|
||||
// non-digit character. See [RFC 3696 Section 2].
|
||||
//
|
||||
// [RFC 3696 Section 2]: https://datatracker.ietf.org/doc/html/rfc3696#section-2
|
||||
return errors.Error("all octets are numeric")
|
||||
}
|
||||
|
||||
// MaxDomainNameLen is the maximum allowed length of a full domain name
|
||||
// according to [RFC 1035].
|
||||
//
|
||||
// See also: https://stackoverflow.com/a/32294443/1892060.
|
||||
//
|
||||
// [RFC 1035]: https://datatracker.ietf.org/doc/html/rfc1035
|
||||
const MaxDomainNameLen = 253
|
||||
|
||||
// ValidateDomainName validates the domain name in accordance to [RFC 1035] and
|
||||
// [RFC 3696 Section 2]. As opposed to [ValidateHostname], this function only
|
||||
// validates the lengths of the name itself and its labels, except the TLD.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*AddrError].
|
||||
//
|
||||
// [RFC 1035]: https://datatracker.ietf.org/doc/html/rfc1035
|
||||
// [RFC 3696 Section 2]: https://datatracker.ietf.org/doc/html/rfc3696#section-2
|
||||
func ValidateDomainName(name string) (err error) {
|
||||
defer makeAddrError(&err, name, AddrKindDomainName)
|
||||
|
||||
name, err = idna.ToASCII(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return &LengthError{
|
||||
Kind: AddrKindDomainName,
|
||||
Length: 0,
|
||||
}
|
||||
} else if l := len(name); l > MaxDomainNameLen {
|
||||
return &LengthError{
|
||||
Kind: AddrKindDomainName,
|
||||
Max: MaxDomainNameLen,
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
|
||||
labels := strings.Split(name, ".")
|
||||
tldIdx := len(labels) - 1
|
||||
for _, l := range labels[:tldIdx] {
|
||||
err = ValidateDomainNameLabel(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Use stricter rules for the TLD.
|
||||
return ValidateTLDLabel(labels[tldIdx])
|
||||
}
|
||||
|
||||
// ValidateDomainNameLabel returns an error if label is not a valid label of a
|
||||
// domain name. An empty label is considered invalid. Essentially it validates
|
||||
// the length of the label since the name in DNS is permitted to contain any
|
||||
// printable ASCII character, see [RFC 3696 Section 2]. label must only contain
|
||||
// ASCII characters, see [idna.ToASCII].
|
||||
//
|
||||
// Any error returned will have the underlying type of [*LabelError].
|
||||
//
|
||||
// [RFC 3696 Section 2]: https://datatracker.ietf.org/doc/html/rfc3696#section-2
|
||||
func ValidateDomainNameLabel(label string) (err error) {
|
||||
defer makeLabelError(&err, label, LabelKindDomain)
|
||||
|
||||
if label == "" {
|
||||
return &LengthError{
|
||||
Kind: LabelKindDomain,
|
||||
Length: 0,
|
||||
}
|
||||
}
|
||||
|
||||
l := len(label)
|
||||
if l > MaxDomainLabelLen {
|
||||
return &LengthError{
|
||||
Kind: LabelKindDomain,
|
||||
Max: MaxDomainLabelLen,
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateHostnameLabel returns an error if label is not a valid label of a
|
||||
// domain name. An empty label is considered invalid.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*LabelError].
|
||||
func ValidateHostnameLabel(label string) (err error) {
|
||||
defer makeLabelError(&err, label, LabelKindHost)
|
||||
|
||||
if err = ValidateDomainNameLabel(label); err != nil {
|
||||
err = errors.Unwrap(err)
|
||||
replaceKind(err, LabelKindHost)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
l := len(label)
|
||||
if r := rune(label[0]); !IsValidHostOuterRune(r) {
|
||||
return &RuneError{
|
||||
Kind: LabelKindHost,
|
||||
Rune: r,
|
||||
}
|
||||
} else if l == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, r := range label[1 : l-1] {
|
||||
if !IsValidHostInnerRune(r) {
|
||||
return &RuneError{
|
||||
Kind: LabelKindHost,
|
||||
Rune: r,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r := rune(label[l-1]); !IsValidHostOuterRune(r) {
|
||||
return &RuneError{
|
||||
Kind: LabelKindHost,
|
||||
Rune: r,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateHostname validates the domain name in accordance to [RFC 952], [RFC
|
||||
// 1035], and with [RFC 1123]'s inclusion of digits at the start of the host.
|
||||
// It doesn't validate against two or more hyphens to allow punycode and
|
||||
// internationalized domains.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*AddrError].
|
||||
//
|
||||
// [RFC 952]: https://datatracker.ietf.org/doc/html/rfc952
|
||||
// [RFC 1035]: https://datatracker.ietf.org/doc/html/rfc1035
|
||||
// [RFC 1123]: https://datatracker.ietf.org/doc/html/rfc1123
|
||||
func ValidateHostname(name string) (err error) {
|
||||
defer makeAddrError(&err, name, AddrKindName)
|
||||
|
||||
name, err = idna.ToASCII(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return &LengthError{
|
||||
Kind: AddrKindName,
|
||||
Length: 0,
|
||||
}
|
||||
} else if l := len(name); l > MaxDomainNameLen {
|
||||
return &LengthError{
|
||||
Kind: AddrKindName,
|
||||
Max: MaxDomainNameLen,
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
|
||||
labels := strings.Split(name, ".")
|
||||
tldIdx := len(labels) - 1
|
||||
for _, l := range labels[:tldIdx] {
|
||||
err = ValidateHostnameLabel(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ValidateTLDLabel(labels[tldIdx])
|
||||
}
|
||||
|
||||
// MaxServiceLabelLen is the maximum allowed length of a service name label
|
||||
// according to [RFC 6335].
|
||||
//
|
||||
// [RFC 6335]: https://datatracker.ietf.org/doc/html/rfc6335
|
||||
const MaxServiceLabelLen = 16
|
||||
|
||||
// ValidateServiceNameLabel returns an error if label is not a valid label of
|
||||
// a service domain name. An empty label is considered invalid.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*LabelError].
|
||||
func ValidateServiceNameLabel(label string) (err error) {
|
||||
defer makeLabelError(&err, label, LabelKindSRV)
|
||||
|
||||
if label == "" || label == "_" {
|
||||
return &LengthError{
|
||||
Kind: LabelKindSRV,
|
||||
Length: 0,
|
||||
}
|
||||
} else if r := rune(label[0]); r != '_' {
|
||||
return &RuneError{
|
||||
Kind: LabelKindSRV,
|
||||
Rune: r,
|
||||
}
|
||||
}
|
||||
|
||||
l := len(label)
|
||||
if l > MaxServiceLabelLen {
|
||||
return &LengthError{
|
||||
Kind: LabelKindSRV,
|
||||
Max: MaxServiceLabelLen,
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(e.burkov): Validate adjacent hyphens since service labels can't be
|
||||
// internationalized. See RFC 6336 Section 5.1.
|
||||
if err = ValidateHostnameLabel(label[1:]); err != nil {
|
||||
err = errors.Unwrap(err)
|
||||
replaceKind(err, LabelKindSRV)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateSRVDomainName validates name assuming it belongs to the superset of
|
||||
// service domain names in accordance to [RFC 2782] and [RFC 6763]. It doesn't
|
||||
// validate against two or more hyphens to allow punycode and internationalized
|
||||
// domains.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*AddrError].
|
||||
//
|
||||
// [RFC 2782]: https://datatracker.ietf.org/doc/html/rfc2782
|
||||
// [RFC 6763]: https://datatracker.ietf.org/doc/html/rfc6763
|
||||
func ValidateSRVDomainName(name string) (err error) {
|
||||
defer makeAddrError(&err, name, AddrKindSRVName)
|
||||
|
||||
name, err = idna.ToASCII(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return &LengthError{
|
||||
Kind: AddrKindSRVName,
|
||||
Length: 0,
|
||||
}
|
||||
} else if l := len(name); l > MaxDomainNameLen {
|
||||
return &LengthError{
|
||||
Kind: AddrKindSRVName,
|
||||
Max: MaxDomainNameLen,
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
|
||||
labels := strings.Split(name, ".")
|
||||
tldIdx := len(labels) - 1
|
||||
for _, l := range labels[:tldIdx] {
|
||||
if strings.HasPrefix(l, "_") {
|
||||
err = ValidateServiceNameLabel(l)
|
||||
} else {
|
||||
err = ValidateHostnameLabel(l)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ValidateTLDLabel(labels[tldIdx])
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// IPv4Localhost returns the IPv4 localhost address "127.0.0.1".
|
||||
func IPv4Localhost() (ip netip.Addr) { return netip.AddrFrom4([4]byte{127, 0, 0, 1}) }
|
||||
|
||||
// IPv6Localhost returns the IPv6 localhost address "::1".
|
||||
func IPv6Localhost() (ip netip.Addr) { return netip.AddrFrom16([16]byte{15: 1}) }
|
||||
|
||||
// ZeroPrefix returns an IP subnet with prefix 0 and all bytes of the IP address
|
||||
// set to 0. fam must be either [AddrFamilyIPv4] or [AddrFamilyIPv6].
|
||||
func ZeroPrefix(fam AddrFamily) (n netip.Prefix) {
|
||||
switch fam {
|
||||
case AddrFamilyIPv4:
|
||||
return netip.PrefixFrom(netip.IPv4Unspecified(), 0)
|
||||
case AddrFamilyIPv6:
|
||||
return netip.PrefixFrom(netip.IPv6Unspecified(), 0)
|
||||
default:
|
||||
panic(badAddrFam("ZeroPrefix", fam))
|
||||
}
|
||||
}
|
||||
|
||||
// badAddrFam is a helper that returns an informative error for panics caused by
|
||||
// bad address-family values.
|
||||
func badAddrFam(fn string, fam AddrFamily) (err error) {
|
||||
return fmt.Errorf("netutil.%s: bad address family %d", fn, fam)
|
||||
}
|
||||
|
||||
// IPToAddr converts a [net.IP] into a [netip.Addr] of the given family and
|
||||
// returns a meaningful error. ip should not be nil. fam must be either
|
||||
// [AddrFamilyIPv4] or [AddrFamilyIPv6].
|
||||
//
|
||||
// See also [IPToAddrNoMapped].
|
||||
func IPToAddr(ip net.IP, fam AddrFamily) (addr netip.Addr, err error) {
|
||||
if ip == nil {
|
||||
return netip.Addr{}, errors.Error("nil ip")
|
||||
}
|
||||
|
||||
switch fam {
|
||||
case AddrFamilyIPv4:
|
||||
// Make sure that we use the IPv4 form of the address to make sure that
|
||||
// netip.Addr doesn't turn out to be an IPv6 one when it really should
|
||||
// be an IPv4 one.
|
||||
ip4 := ip.To4()
|
||||
if ip4 == nil {
|
||||
return netip.Addr{}, fmt.Errorf("bad ipv4 net.IP %v", ip)
|
||||
}
|
||||
|
||||
ip = ip4
|
||||
case AddrFamilyIPv6:
|
||||
// Again, make sure that we use the correct form according to the
|
||||
// address family.
|
||||
ip = ip.To16()
|
||||
default:
|
||||
panic(badAddrFam("IPToAddr", fam))
|
||||
}
|
||||
|
||||
addr, ok := netip.AddrFromSlice(ip)
|
||||
if !ok {
|
||||
return netip.Addr{}, fmt.Errorf("bad net.IP value %v", ip)
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// IPToAddrNoMapped is like [IPToAddr] but it detects the address family
|
||||
// automatically by assuming that every IPv6-mapped IPv4 address is actually an
|
||||
// IPv4 address. Do not use IPToAddrNoMapped where this assumption isn't safe.
|
||||
func IPToAddrNoMapped(ip net.IP) (addr netip.Addr, err error) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return IPToAddr(ip4, AddrFamilyIPv4)
|
||||
}
|
||||
|
||||
return IPToAddr(ip, AddrFamilyIPv6)
|
||||
}
|
||||
|
||||
// IPNetToPrefix is a helper that converts a [*net.IPNet] into a [netip.Prefix].
|
||||
// subnet should not be nil. fam must be either [AddrFamilyIPv4] or
|
||||
// [AddrFamilyIPv6].
|
||||
//
|
||||
// See also [IPNetToPrefixNoMapped].
|
||||
func IPNetToPrefix(subnet *net.IPNet, fam AddrFamily) (p netip.Prefix, err error) {
|
||||
if subnet == nil {
|
||||
return netip.Prefix{}, errors.Error("nil subnet")
|
||||
}
|
||||
|
||||
addr, err := IPToAddr(subnet.IP, fam)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, fmt.Errorf("bad ip for subnet %v: %w", subnet, err)
|
||||
}
|
||||
|
||||
ones, _ := subnet.Mask.Size()
|
||||
p = netip.PrefixFrom(addr, ones)
|
||||
if !p.IsValid() {
|
||||
return netip.Prefix{}, fmt.Errorf("bad subnet %v", subnet)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// IPNetToPrefixNoMapped is like [IPNetToPrefix] but it detects the address
|
||||
// family automatically by assuming that every IPv6-mapped IPv4 address is
|
||||
// actually an IPv4 address. Do not use IPNetToPrefixNoMapped where this
|
||||
// assumption isn't safe.
|
||||
func IPNetToPrefixNoMapped(subnet *net.IPNet) (p netip.Prefix, err error) {
|
||||
if subnet == nil {
|
||||
return netip.Prefix{}, errors.Error("nil subnet")
|
||||
}
|
||||
|
||||
if ip4 := subnet.IP.To4(); ip4 != nil {
|
||||
subnet.IP = ip4
|
||||
|
||||
return IPNetToPrefix(subnet, AddrFamilyIPv4)
|
||||
}
|
||||
|
||||
return IPNetToPrefix(subnet, AddrFamilyIPv6)
|
||||
}
|
||||
|
||||
// NetAddrToAddrPort converts a [net.Addr] into a [netip.AddrPort] if it can.
|
||||
// Otherwise, it returns an empty netip.AddrPort. addr must not be nil.
|
||||
//
|
||||
// Since [net.TCPAddr.AddrPort] and [net.UDPAddr.AddrPort] perform a naïve
|
||||
// conversion of their [net.IP] values into [netip.Addr] ones, that does not
|
||||
// take mapped addresses into account, IPv4-mapped IPv6 addresses are assumed to
|
||||
// actually be IPv4 addresses and are normalized into them.
|
||||
//
|
||||
// Those who want a conversion without this normalization may use:
|
||||
//
|
||||
// if ap, ok := addr.(interface{ AddrPort() (a netip.AddrPort) }); ok {
|
||||
// return ap.AddrPort()
|
||||
// }
|
||||
//
|
||||
// See https://github.com/golang/go/issues/53607.
|
||||
func NetAddrToAddrPort(addr net.Addr) (addrPort netip.AddrPort) {
|
||||
if ap, ok := addr.(interface{ AddrPort() (a netip.AddrPort) }); ok {
|
||||
addrPort = ap.AddrPort()
|
||||
ip := addrPort.Addr()
|
||||
if ip.Is4In6() {
|
||||
addrPort = netip.AddrPortFrom(ip.Unmap(), addrPort.Port())
|
||||
}
|
||||
}
|
||||
|
||||
return addrPort
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AddrFamily is the type for IANA address family numbers.
|
||||
type AddrFamily uint16
|
||||
|
||||
// An incomplete list of IANA address family numbers.
|
||||
//
|
||||
// See https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml.
|
||||
const (
|
||||
AddrFamilyNone AddrFamily = 0
|
||||
AddrFamilyIPv4 AddrFamily = 1
|
||||
AddrFamilyIPv6 AddrFamily = 2
|
||||
)
|
||||
|
||||
// type check
|
||||
var _ fmt.Stringer = AddrFamilyNone
|
||||
|
||||
// String implements the [fmt.Stringer] interface for AddrFamily.
|
||||
func (f AddrFamily) String() (s string) {
|
||||
switch f {
|
||||
case AddrFamilyNone:
|
||||
return "none"
|
||||
case AddrFamilyIPv4:
|
||||
return "ipv4"
|
||||
case AddrFamilyIPv6:
|
||||
return "ipv6"
|
||||
default:
|
||||
return fmt.Sprintf("!bad_addr_fam_%d", f)
|
||||
}
|
||||
}
|
||||
|
||||
// Constants to avoid a dependency on github.com/miekg/dns.
|
||||
//
|
||||
// See https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4.
|
||||
const (
|
||||
dnsTypeA uint16 = 1
|
||||
dnsTypeAAAA uint16 = 28
|
||||
)
|
||||
|
||||
// AddrFamilyFromRRType returns an AddrFamily appropriate for the DNS resource
|
||||
// record type rr. That is, [AddrFamilyIPv4] for DNS type A (1),
|
||||
// [AddrFamilyIPv6] for DNS type AAAA (28), and [AddrFamilyNone] otherwise.
|
||||
func AddrFamilyFromRRType(rr uint16) (fam AddrFamily) {
|
||||
switch rr {
|
||||
case dnsTypeA:
|
||||
return AddrFamilyIPv4
|
||||
case dnsTypeAAAA:
|
||||
return AddrFamilyIPv6
|
||||
default:
|
||||
return AddrFamilyNone
|
||||
}
|
||||
}
|
||||
-224
@@ -1,224 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// Errors
|
||||
//
|
||||
// TODO(a.garipov): Implement the errors.Iser interface and use errors.Is in
|
||||
// tests to test the whole content of the errors.
|
||||
|
||||
const (
|
||||
// ErrNotAReversedIP is the underlying error returned from validation
|
||||
// functions when a domain name is not a full reversed IP address.
|
||||
ErrNotAReversedIP errors.Error = "not a full reversed ip address"
|
||||
|
||||
// ErrNotAReversedSubnet is the underlying error returned from validation
|
||||
// functions when a domain name is not a valid reversed IP network.
|
||||
ErrNotAReversedSubnet errors.Error = "not a reversed ip network"
|
||||
)
|
||||
|
||||
// AddrKind is a convenient alias for string describing the kind of address and
|
||||
// used for error reporting.
|
||||
type AddrKind = string
|
||||
|
||||
// Kinds of addresses for [AddrError].
|
||||
const (
|
||||
AddrKindARPA AddrKind = "arpa domain name"
|
||||
AddrKindCIDR AddrKind = "cidr address"
|
||||
AddrKindHostPort AddrKind = "hostport address"
|
||||
AddrKindIP AddrKind = "ip address"
|
||||
AddrKindIPPort AddrKind = "ipport address"
|
||||
AddrKindIPv4 AddrKind = "ipv4 address"
|
||||
AddrKindMAC AddrKind = "mac address"
|
||||
AddrKindName AddrKind = "hostname"
|
||||
AddrKindDomainName AddrKind = "domain name"
|
||||
AddrKindSRVName AddrKind = "service domain name"
|
||||
)
|
||||
|
||||
// AddrError is the underlying type of errors returned from validation
|
||||
// functions when a domain name is invalid.
|
||||
type AddrError struct {
|
||||
// Err is the underlying error, if any.
|
||||
Err error
|
||||
|
||||
// Kind is the kind of address or address part.
|
||||
Kind AddrKind
|
||||
|
||||
// Addr is the text of the invalid address.
|
||||
Addr string
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ error = (*AddrError)(nil)
|
||||
|
||||
// Error implements the error interface for *AddrError.
|
||||
func (err *AddrError) Error() (msg string) {
|
||||
if err.Err != nil {
|
||||
return fmt.Sprintf("bad %s %q: %s", err.Kind, err.Addr, err.Err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("bad %s %q", err.Kind, err.Addr)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ errors.Wrapper = (*AddrError)(nil)
|
||||
|
||||
// Unwrap implements the [errors.Wrapper] interface for *AddrError. It returns
|
||||
// err.Err.
|
||||
func (err *AddrError) Unwrap() (unwrapped error) {
|
||||
return err.Err
|
||||
}
|
||||
|
||||
// makeAddrError is a deferrable helper for functions that return [*AddrError].
|
||||
// errPtr must be non-nil. Usage example:
|
||||
//
|
||||
// defer makeAddrError(&err, addr, AddrKindARPA)
|
||||
func makeAddrError(errPtr *error, addr string, k AddrKind) {
|
||||
err := *errPtr
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
*errPtr = &AddrError{
|
||||
Err: err,
|
||||
Kind: k,
|
||||
Addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
// LabelKind is a convenient alias for string describing the kind of label of a
|
||||
// name and used for error reporting.
|
||||
type LabelKind = string
|
||||
|
||||
// Kinds of labels for [LabelError].
|
||||
const (
|
||||
LabelKindDomain LabelKind = "domain name label"
|
||||
LabelKindHost LabelKind = "hostname label"
|
||||
LabelKindSRV LabelKind = "service name label"
|
||||
LabelKindTLD LabelKind = "top-level domain name label"
|
||||
)
|
||||
|
||||
// LabelError is the underlying type of errors returned from validation
|
||||
// functions when a label of a name is invalid.
|
||||
type LabelError struct {
|
||||
// Err is the underlying error, if any.
|
||||
Err error
|
||||
|
||||
// Kind is the kind of label of the name.
|
||||
Kind LabelKind
|
||||
|
||||
// Label is the text of the invalid label.
|
||||
Label string
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ error = (*AddrError)(nil)
|
||||
|
||||
// Error implements the error interface for *AddrError.
|
||||
func (err *LabelError) Error() (msg string) {
|
||||
if err.Err != nil {
|
||||
return fmt.Sprintf("bad %s %q: %s", err.Kind, err.Label, err.Err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("bad %s %q", err.Kind, err.Label)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ errors.Wrapper = (*AddrError)(nil)
|
||||
|
||||
// Unwrap implements the [errors.Wrapper] interface for *AddrError. It returns
|
||||
// err.Err.
|
||||
func (err *LabelError) Unwrap() (unwrapped error) {
|
||||
return err.Err
|
||||
}
|
||||
|
||||
// makeLabelError is a deferrable helper for functions that return
|
||||
// [*LabelError]. errPtr must be non-nil. Usage example:
|
||||
//
|
||||
// defer makeLabelError(&err, label, LabelKindDomain)
|
||||
func makeLabelError(errPtr *error, label string, k LabelKind) {
|
||||
err := *errPtr
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
*errPtr = &LabelError{
|
||||
Err: err,
|
||||
Kind: k,
|
||||
Label: label,
|
||||
}
|
||||
}
|
||||
|
||||
// LengthError is the underlying type of errors returned from validation
|
||||
// functions when an address or a part of an address has a bad length. Kind is
|
||||
// either [AddrKind] or [LabelKind].
|
||||
type LengthError struct {
|
||||
// Kind is the kind of address or address part.
|
||||
Kind string
|
||||
|
||||
// Allowed are the allowed lengths for this kind of address. If allowed
|
||||
// is empty, Max should be non-zero.
|
||||
Allowed []int
|
||||
|
||||
// Max is the maximum length for this part or address kind. If Max is
|
||||
// zero, Allowed should be non-empty.
|
||||
Max int
|
||||
|
||||
// Length is the length of the provided address.
|
||||
Length int
|
||||
}
|
||||
|
||||
// Error implements the error interface for *LengthError.
|
||||
func (err *LengthError) Error() (msg string) {
|
||||
if err.Max > 0 {
|
||||
return fmt.Sprintf("%s is too long: got %d, max %d", err.Kind, err.Length, err.Max)
|
||||
}
|
||||
|
||||
if err.Length == 0 {
|
||||
return fmt.Sprintf("%s is empty", err.Kind)
|
||||
}
|
||||
|
||||
format := "bad %s length %d, allowed: %v"
|
||||
if len(err.Allowed) == 1 {
|
||||
return fmt.Sprintf(format, err.Kind, err.Length, err.Allowed[0])
|
||||
}
|
||||
|
||||
return fmt.Sprintf(format, err.Kind, err.Length, err.Allowed)
|
||||
}
|
||||
|
||||
// RuneError is the underlying type of errors returned from validation functions
|
||||
// when a rune in the address is invalid. Kind is either [AddrKind] or
|
||||
// [LabelKind].
|
||||
type RuneError struct {
|
||||
// Kind is the kind of address or address part.
|
||||
Kind string
|
||||
|
||||
// Rune is the invalid rune.
|
||||
Rune rune
|
||||
}
|
||||
|
||||
// Error implements the error interface for *RuneError.
|
||||
func (err *RuneError) Error() (msg string) {
|
||||
return fmt.Sprintf("bad %s rune %q", err.Kind, err.Rune)
|
||||
}
|
||||
|
||||
// replaceKind replaces the Kind field of err with newKind. err must be of type
|
||||
// [*LabelError], [*AddrError], [*LengthError], or [*RuneError].
|
||||
func replaceKind(err error, newKind string) {
|
||||
switch err := err.(type) {
|
||||
case *LabelError:
|
||||
err.Kind = newKind
|
||||
case *AddrError:
|
||||
err.Kind = newKind
|
||||
case *LengthError:
|
||||
err.Kind = newKind
|
||||
case *RuneError:
|
||||
err.Kind = newKind
|
||||
default:
|
||||
panic(fmt.Errorf("unexpected error type %T", err))
|
||||
}
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// HostPort And Utilities
|
||||
|
||||
// HostPort is a convenient type for addresses that contain a hostname and
|
||||
// a port, like "example.com:12345", "1.2.3.4:56789", or "[1234::cdef]:12345".
|
||||
type HostPort struct {
|
||||
Host string
|
||||
Port int
|
||||
}
|
||||
|
||||
// ParseHostPort parses a HostPort from addr. Any error returned will have the
|
||||
// underlying type of [*AddrError].
|
||||
func ParseHostPort(addr string) (hp *HostPort, err error) {
|
||||
defer makeAddrError(&err, addr, AddrKindHostPort)
|
||||
|
||||
var host string
|
||||
var port int
|
||||
host, port, err = SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &HostPort{
|
||||
Host: host,
|
||||
Port: port,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CloneHostPorts returns a deep copy of hps.
|
||||
func CloneHostPorts(hps []*HostPort) (clone []*HostPort) {
|
||||
if hps == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
clone = make([]*HostPort, len(hps))
|
||||
for i, hp := range hps {
|
||||
clone[i] = hp.Clone()
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// Clone returns a clone of hp.
|
||||
func (hp *HostPort) Clone() (clone *HostPort) {
|
||||
if hp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &HostPort{
|
||||
Host: hp.Host,
|
||||
Port: hp.Port,
|
||||
}
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ encoding.TextMarshaler = HostPort{}
|
||||
|
||||
// MarshalText implements the [encoding.TextMarshaler] interface for HostPort.
|
||||
func (hp HostPort) MarshalText() (b []byte, err error) {
|
||||
return []byte(hp.String()), nil
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ fmt.Stringer = HostPort{}
|
||||
|
||||
// String implements the [fmt.Stringer] interface for *HostPort.
|
||||
func (hp HostPort) String() (s string) {
|
||||
return JoinHostPort(hp.Host, hp.Port)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ encoding.TextUnmarshaler = (*HostPort)(nil)
|
||||
|
||||
// UnmarshalText implements the [encoding.TextUnmarshaler] interface for
|
||||
// *HostPort. Any error returned will have the underlying type of [*AddrError].
|
||||
func (hp *HostPort) UnmarshalText(b []byte) (err error) {
|
||||
var newHP *HostPort
|
||||
newHP, err = ParseHostPort(string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*hp = *newHP
|
||||
|
||||
return nil
|
||||
}
|
||||
-235
@@ -1,235 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// IP Address Constants And Utilities
|
||||
|
||||
// Bit lengths of IP addresses.
|
||||
const (
|
||||
IPv4BitLen = net.IPv4len * 8
|
||||
IPv6BitLen = net.IPv6len * 8
|
||||
)
|
||||
|
||||
// CloneIPs returns a deep clone of ips.
|
||||
func CloneIPs(ips []net.IP) (clone []net.IP) {
|
||||
if ips == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
clone = make([]net.IP, len(ips))
|
||||
for i, ip := range ips {
|
||||
clone[i] = slices.Clone(ip)
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// IPAndPortFromAddr returns the IP address and the port from addr. If addr is
|
||||
// neither a [*net.TCPAddr] nor a [*net.UDPAddr], it returns nil and 0.
|
||||
func IPAndPortFromAddr(addr net.Addr) (ip net.IP, port int) {
|
||||
switch addr := addr.(type) {
|
||||
case *net.TCPAddr:
|
||||
return addr.IP, addr.Port
|
||||
case *net.UDPAddr:
|
||||
return addr.IP, addr.Port
|
||||
}
|
||||
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
// IPv4bcast returns a new limited broadcast IPv4 address, 255.255.255.255. It
|
||||
// has the same name as the variable in package net, but the result always has
|
||||
// four bytes.
|
||||
func IPv4bcast() (ip net.IP) { return net.IP{255, 255, 255, 255} }
|
||||
|
||||
// IPv4allsys returns a new all systems (aka all hosts) IPv4 address, 224.0.0.1.
|
||||
// It has the same name as the variable in package net, but the result always
|
||||
// has four bytes.
|
||||
func IPv4allsys() (ip net.IP) { return net.IP{224, 0, 0, 1} }
|
||||
|
||||
// IPv4allrouter returns a new all routers IPv4 address, 224.0.0.2. It has the
|
||||
// same name as the variable in package net, but the result always has four
|
||||
// bytes.
|
||||
func IPv4allrouter() (ip net.IP) { return net.IP{224, 0, 0, 2} }
|
||||
|
||||
// IPv4Zero returns a new unspecified (aka empty or null) IPv4 address, 0.0.0.0.
|
||||
// It has the same name as the variable in package net, but the result always
|
||||
// has four bytes.
|
||||
func IPv4Zero() (ip net.IP) { return net.IP{0, 0, 0, 0} }
|
||||
|
||||
// IPv6Zero returns a new unspecified (aka empty or null) IPv6 address, [::].
|
||||
// It has the same name as the variable in package net.
|
||||
func IPv6Zero() (ip net.IP) {
|
||||
return net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
|
||||
// ParseIP is a wrapper around net.ParseIP that returns a useful error.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*AddrError].
|
||||
func ParseIP(s string) (ip net.IP, err error) {
|
||||
ip = net.ParseIP(s)
|
||||
if ip == nil {
|
||||
return nil, &AddrError{
|
||||
Kind: AddrKindIP,
|
||||
Addr: s,
|
||||
}
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// ParseIPv4 is a wrapper around net.ParseIP that makes sure that the parsed IP
|
||||
// is an IPv4 address and returns a useful error.
|
||||
//
|
||||
// Any error returned will have the underlying type of either [*AddrError].
|
||||
func ParseIPv4(s string) (ip net.IP, err error) {
|
||||
ip, err = ParseIP(s)
|
||||
if err != nil {
|
||||
err.(*AddrError).Kind = AddrKindIPv4
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ip = ip.To4(); ip == nil {
|
||||
return nil, &AddrError{
|
||||
Kind: AddrKindIPv4,
|
||||
Addr: s,
|
||||
}
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// CloneIPNet returns a deep clone of n.
|
||||
func CloneIPNet(n *net.IPNet) (clone *net.IPNet) {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &net.IPNet{
|
||||
IP: slices.Clone(n.IP),
|
||||
// TODO(e.burkov): Consider adding CloneIPMask.
|
||||
Mask: net.IPMask(slices.Clone(net.IP(n.Mask))),
|
||||
}
|
||||
}
|
||||
|
||||
// ParseSubnet parses a subnet which can be either a CIDR or a single IP. In
|
||||
// the latter case, n is a single-IP subnet. It also keeps the original parsed
|
||||
// IP address inside n.
|
||||
//
|
||||
// If s contains a CIDR with an IP address that is an IPv4-mapped IPv6 address,
|
||||
// the behavior is undefined.
|
||||
//
|
||||
// Any error returned will have the underlying type of either [*AddrError].
|
||||
func ParseSubnet(s string) (n *net.IPNet, err error) {
|
||||
var ip net.IP
|
||||
|
||||
// Detect if this is a CIDR or an IP early, so that the path to returning an
|
||||
// error is shorter.
|
||||
if !strings.Contains(s, "/") {
|
||||
ip, err = ParseIP(s)
|
||||
if err != nil {
|
||||
return nil, &AddrError{
|
||||
Err: err,
|
||||
Kind: AddrKindCIDR,
|
||||
Addr: s,
|
||||
}
|
||||
}
|
||||
|
||||
return SingleIPSubnet(ip), nil
|
||||
}
|
||||
|
||||
ip, n, err = net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
// Don't include the original error here, because it is basically
|
||||
// the same as ours but worse and has no additional information.
|
||||
return nil, &AddrError{
|
||||
Kind: AddrKindCIDR,
|
||||
Addr: s,
|
||||
}
|
||||
}
|
||||
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
// Reduce the length of IP and mask if possible so that
|
||||
// IPNet.Contains doesn't waste time converting between 16- and
|
||||
// 4-byte versions.
|
||||
ip = ip4
|
||||
|
||||
if ones, bits := n.Mask.Size(); ones >= 96 && bits == IPv6BitLen {
|
||||
// Copy the IPv4-length tail of the underlying slice to it's
|
||||
// beginning to avoid allocations in case of subsequent appending.
|
||||
copy(n.Mask, n.Mask[net.IPv6len-net.IPv4len:])
|
||||
n.Mask = n.Mask[:net.IPv4len]
|
||||
}
|
||||
}
|
||||
|
||||
n.IP = ip
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// SingleIPSubnet returns an IP network that only contains ip. If ip is not
|
||||
// a valid IPv4 or IPv6 address, n is nil.
|
||||
func SingleIPSubnet(ip net.IP) (n *net.IPNet) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return &net.IPNet{
|
||||
IP: ip4,
|
||||
Mask: net.CIDRMask(IPv4BitLen, IPv4BitLen),
|
||||
}
|
||||
} else if len(ip) == net.IPv6len {
|
||||
return &net.IPNet{
|
||||
IP: ip,
|
||||
Mask: net.CIDRMask(IPv6BitLen, IPv6BitLen),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseSubnets returns the slice of *net.IPNet parsed from ss.
|
||||
func ParseSubnets(ss ...string) (ns []*net.IPNet, err error) {
|
||||
l := len(ss)
|
||||
if l == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ns = make([]*net.IPNet, l)
|
||||
for i, s := range ss {
|
||||
ns[i], err = ParseSubnet(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing network at index %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
// ValidateIP returns an error if ip is not a valid IPv4 or IPv6 address.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*AddrError].
|
||||
func ValidateIP(ip net.IP) (err error) {
|
||||
// TODO(a.garipov): Get rid of unnecessary allocations in case of valid IP.
|
||||
defer makeAddrError(&err, ip.String(), AddrKindIP)
|
||||
|
||||
switch l := len(ip); l {
|
||||
case 0:
|
||||
return &LengthError{
|
||||
Kind: AddrKindIP,
|
||||
Length: 0,
|
||||
}
|
||||
case net.IPv4len, net.IPv6len:
|
||||
return nil
|
||||
default:
|
||||
return &LengthError{
|
||||
Kind: AddrKindIP,
|
||||
Allowed: []int{net.IPv4len, net.IPv6len},
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
}
|
||||
-357
@@ -1,357 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
// Reversed ARPA Addresses
|
||||
|
||||
// fromHexByte converts a single hexadecimal ASCII digit character into an
|
||||
// integer from 0 to 15. For all other characters it returns 0xff.
|
||||
//
|
||||
// TODO(e.burkov): This should be covered with tests after adding HasSuffixFold
|
||||
// into stringutil.
|
||||
func fromHexByte(c byte) (n byte) {
|
||||
switch {
|
||||
case c >= '0' && c <= '9':
|
||||
return c - '0'
|
||||
case c >= 'a' && c <= 'f':
|
||||
return c - 'a' + 10
|
||||
case c >= 'A' && c <= 'F':
|
||||
return c - 'A' + 10
|
||||
default:
|
||||
return 0xff
|
||||
}
|
||||
}
|
||||
|
||||
// ARPA reverse address domains.
|
||||
const (
|
||||
arpaV4Suffix = ".in-addr.arpa"
|
||||
arpaV6Suffix = ".ip6.arpa"
|
||||
)
|
||||
|
||||
// The maximum lengths of the ARPA-formatted reverse addresses.
|
||||
//
|
||||
// An example of IPv4 with a maximum length:
|
||||
//
|
||||
// 49.91.20.104.in-addr.arpa
|
||||
//
|
||||
// An example of IPv6 with a maximum length:
|
||||
//
|
||||
// 1.3.b.5.4.1.8.6.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.0.7.4.6.0.6.2.ip6.arpa
|
||||
const (
|
||||
arpaV4MaxIPLen = len("000.000.000.000")
|
||||
arpaV6MaxIPLen = len("0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0")
|
||||
|
||||
arpaV4MaxLen = arpaV4MaxIPLen + len(arpaV4Suffix)
|
||||
arpaV6MaxLen = arpaV6MaxIPLen + len(arpaV6Suffix)
|
||||
)
|
||||
|
||||
// reverseIP inverts the order of bytes in an IP address in-place.
|
||||
func reverseIP(ip net.IP) {
|
||||
l := len(ip)
|
||||
for i := range ip[:l/2] {
|
||||
ip[i], ip[l-i-1] = ip[l-i-1], ip[i]
|
||||
}
|
||||
}
|
||||
|
||||
// ipv6FromReversed parses an IPv6 reverse address. It assumes that arpa is a
|
||||
// valid domain name.
|
||||
func ipv6FromReversed(arpa string) (ip net.IP, err error) {
|
||||
const addrStep = len("0.0.")
|
||||
|
||||
ip = make(net.IP, net.IPv6len)
|
||||
for i := range ip {
|
||||
// Get the two half-byte and merge them together. Validate the dots
|
||||
// between them since while arpa is assumed to be a valid domain name,
|
||||
// those labels can still be invalid on their own.
|
||||
sIdx := i * addrStep
|
||||
|
||||
c := arpa[sIdx]
|
||||
lo := fromHexByte(c)
|
||||
if lo == 0xff {
|
||||
return nil, &RuneError{
|
||||
Kind: AddrKindARPA,
|
||||
Rune: rune(c),
|
||||
}
|
||||
}
|
||||
|
||||
c = arpa[sIdx+2]
|
||||
hi := fromHexByte(c)
|
||||
if hi == 0xff {
|
||||
return nil, &RuneError{
|
||||
Kind: AddrKindARPA,
|
||||
Rune: rune(c),
|
||||
}
|
||||
}
|
||||
|
||||
if arpa[sIdx+1] != '.' || arpa[sIdx+3] != '.' {
|
||||
return nil, ErrNotAReversedIP
|
||||
}
|
||||
|
||||
ip[net.IPv6len-i-1] = hi<<4 | lo
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// IPFromReversedAddr tries to convert a full reversed ARPA address to a normal
|
||||
// IP address. arpa can be domain name or an FQDN.
|
||||
//
|
||||
// Any error returned will have the underlying type of *AddrError.
|
||||
func IPFromReversedAddr(arpa string) (ip net.IP, err error) {
|
||||
arpa = strings.TrimSuffix(arpa, ".")
|
||||
err = ValidateDomainName(arpa)
|
||||
if err != nil {
|
||||
replaceKind(err, AddrKindARPA)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer makeAddrError(&err, arpa, AddrKindARPA)
|
||||
|
||||
// TODO(a.garipov): Add stringutil.HasSuffixFold and remove this.
|
||||
arpa = strings.ToLower(arpa)
|
||||
|
||||
if strings.HasSuffix(arpa, arpaV4Suffix) {
|
||||
ipStr := arpa[:len(arpa)-len(arpaV4Suffix)]
|
||||
ip, err = ParseIPv4(ipStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reverseIP(ip)
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
if strings.HasSuffix(arpa, arpaV6Suffix) {
|
||||
if l := len(arpa); l != arpaV6MaxLen {
|
||||
return nil, &LengthError{
|
||||
Kind: AddrKindARPA,
|
||||
Allowed: []int{arpaV6MaxLen},
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
|
||||
ip, err = ipv6FromReversed(arpa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
return nil, ErrNotAReversedIP
|
||||
}
|
||||
|
||||
// IPToReversedAddr returns the reversed ARPA address of ip suitable for reverse
|
||||
// DNS (PTR) record lookups. This is a modified version of function ReverseAddr
|
||||
// from package github.com/miekg/dns package that accepts an IP.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*AddrError].
|
||||
func IPToReversedAddr(ip net.IP) (arpa string, err error) {
|
||||
const dot = "."
|
||||
|
||||
var l int
|
||||
var suffix string
|
||||
var writeByte func(val byte)
|
||||
b := &strings.Builder{}
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
l, suffix = arpaV4MaxLen, arpaV4Suffix[1:]
|
||||
ip = ip4
|
||||
writeByte = func(val byte) {
|
||||
stringutil.WriteToBuilder(b, strconv.Itoa(int(val)), dot)
|
||||
}
|
||||
} else if ip6 := ip.To16(); ip6 != nil {
|
||||
l, suffix = arpaV6MaxLen, arpaV6Suffix[1:]
|
||||
ip = ip6
|
||||
writeByte = func(val byte) {
|
||||
stringutil.WriteToBuilder(
|
||||
b,
|
||||
strconv.FormatUint(uint64(val&0x0f), 16),
|
||||
dot,
|
||||
strconv.FormatUint(uint64(val>>4), 16),
|
||||
dot,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return "", &AddrError{
|
||||
Kind: AddrKindIP,
|
||||
Addr: ip.String(),
|
||||
}
|
||||
}
|
||||
|
||||
b.Grow(l)
|
||||
for i := len(ip) - 1; i >= 0; i-- {
|
||||
writeByte(ip[i])
|
||||
}
|
||||
|
||||
stringutil.WriteToBuilder(b, suffix)
|
||||
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// ipv4NetFromReversed parses an IPv4 reverse network. It assumes that arpa is
|
||||
// a valid domain name and is not a domain name with a full IPv4 address.
|
||||
func ipv4NetFromReversed(arpa string) (subnet *net.IPNet, err error) {
|
||||
var octet64 uint64
|
||||
var octetIdx int
|
||||
|
||||
ip := make(net.IP, net.IPv4len)
|
||||
l := 0
|
||||
for addr := arpa; addr != ""; addr = addr[:octetIdx-1] {
|
||||
octetIdx = strings.LastIndexByte(addr, '.') + 1
|
||||
|
||||
// Don't check for out of range since the domain is validated to have no
|
||||
// empty labels.
|
||||
octet64, err = strconv.ParseUint(addr[octetIdx:], 10, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if octet64 != 0 && addr[octetIdx] == '0' {
|
||||
// Octets of an ARPA domain name shouldn't contain leading zero
|
||||
// except an octet itself equals zero.
|
||||
//
|
||||
// See RFC 1035 Section 3.5.
|
||||
return nil, &AddrError{
|
||||
Err: errors.Error("leading zero is forbidden at this position"),
|
||||
Kind: LabelKindDomain,
|
||||
Addr: addr[octetIdx:],
|
||||
}
|
||||
}
|
||||
|
||||
ip[l] = byte(octet64)
|
||||
l++
|
||||
|
||||
if octetIdx == 0 {
|
||||
// Prevent slicing with negative indices.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &net.IPNet{
|
||||
IP: ip,
|
||||
Mask: net.CIDRMask(l*8, IPv4BitLen),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ipv6NetFromReversed parses an IPv6 reverse network. It assumes that arpa is
|
||||
// a valid domain name and is not a domain name with a full IPv6 address.
|
||||
func ipv6NetFromReversed(arpa string) (subnet *net.IPNet, err error) {
|
||||
const nibbleLen = len("0.")
|
||||
|
||||
nibbleIdx := len(arpa) - len(arpaV6Suffix) + len(".") - nibbleLen
|
||||
if nibbleIdx%2 != 0 {
|
||||
return nil, ErrNotAReversedSubnet
|
||||
}
|
||||
|
||||
var b byte
|
||||
ip := make(net.IP, net.IPv6len)
|
||||
l := 0
|
||||
for ; nibbleIdx >= 0; nibbleIdx -= nibbleLen {
|
||||
if arpa[nibbleIdx+1] != '.' {
|
||||
return nil, ErrNotAReversedSubnet
|
||||
}
|
||||
|
||||
c := arpa[nibbleIdx]
|
||||
b = fromHexByte(c)
|
||||
if b == 0xff {
|
||||
return nil, &RuneError{
|
||||
Kind: AddrKindARPA,
|
||||
Rune: rune(c),
|
||||
}
|
||||
}
|
||||
|
||||
if l%2 == 0 {
|
||||
// An even digit stands for higher nibble of a byte.
|
||||
ip[l/2] |= b << 4
|
||||
} else {
|
||||
// An odd digit stands for lower nibble of a byte.
|
||||
ip[l/2] |= b
|
||||
}
|
||||
l++
|
||||
}
|
||||
|
||||
return &net.IPNet{
|
||||
IP: ip,
|
||||
Mask: net.CIDRMask(l*4, IPv6BitLen),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// subnetFromReversedV4 tries to convert arpa into IPv4 network. It expects
|
||||
// arpa being a valid domain name in a lower case.
|
||||
func subnetFromReversedV4(arpa string) (subnet *net.IPNet, err error) {
|
||||
arpa = arpa[:len(arpa)-len(arpaV4Suffix)]
|
||||
|
||||
if dots := strings.Count(arpa, "."); dots > 3 {
|
||||
return nil, ErrNotAReversedSubnet
|
||||
} else if dots == 3 {
|
||||
var ip net.IP
|
||||
ip, err = ParseIPv4(arpa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reverseIP(ip)
|
||||
|
||||
return SingleIPSubnet(ip), nil
|
||||
}
|
||||
|
||||
return ipv4NetFromReversed(arpa)
|
||||
}
|
||||
|
||||
// subnetFromReversedV6 tries to convert arpa into IPv6 network. It expects
|
||||
// arpa being a valid domain name in a lower case.
|
||||
func subnetFromReversedV6(arpa string) (subnet *net.IPNet, err error) {
|
||||
if l := len(arpa); l == arpaV6MaxLen {
|
||||
var ip net.IP
|
||||
ip, err = ipv6FromReversed(arpa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return SingleIPSubnet(ip), nil
|
||||
} else if l > arpaV6MaxLen {
|
||||
return nil, &LengthError{
|
||||
Kind: AddrKindARPA,
|
||||
Max: arpaV6MaxLen,
|
||||
Length: l,
|
||||
}
|
||||
}
|
||||
|
||||
return ipv6NetFromReversed(arpa)
|
||||
}
|
||||
|
||||
// SubnetFromReversedAddr tries to convert a reversed ARPA address to an IP
|
||||
// network. arpa can be domain name or an FQDN.
|
||||
//
|
||||
// Any error returned will have the underlying type of [*AddrError].
|
||||
func SubnetFromReversedAddr(arpa string) (subnet *net.IPNet, err error) {
|
||||
arpa = strings.TrimSuffix(arpa, ".")
|
||||
err = ValidateDomainName(arpa)
|
||||
if err != nil {
|
||||
replaceKind(err, AddrKindARPA)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer makeAddrError(&err, arpa, AddrKindARPA)
|
||||
|
||||
// TODO(a.garipov): Add stringutil.HasSuffixFold and remove this.
|
||||
arpa = strings.ToLower(arpa)
|
||||
|
||||
if strings.HasSuffix(arpa, arpaV4Suffix) {
|
||||
return subnetFromReversedV4(arpa)
|
||||
}
|
||||
|
||||
if strings.HasSuffix(arpa, arpaV6Suffix) {
|
||||
return subnetFromReversedV6(arpa)
|
||||
}
|
||||
|
||||
return nil, ErrNotAReversedSubnet
|
||||
}
|
||||
-254
@@ -1,254 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// Set Of Subnets
|
||||
|
||||
// SubnetSet contains the set of IP networks to match the IP address.
|
||||
type SubnetSet interface {
|
||||
// Contains returns true if ip is contained by any of networks the set
|
||||
// contains. ip must be only accessed for reading.
|
||||
Contains(ip net.IP) (ok bool)
|
||||
}
|
||||
|
||||
// Slice-based Subnet Set
|
||||
|
||||
// SliceSubnetSet is the SubnetSet that checks the address through a slice of
|
||||
// *net.IPNet.
|
||||
type SliceSubnetSet []*net.IPNet
|
||||
|
||||
// type check
|
||||
var _ SubnetSet = (SliceSubnetSet)(nil)
|
||||
|
||||
// Contains implements the SubnetSet interface for SliceSubnetSet.
|
||||
func (s SliceSubnetSet) Contains(ip net.IP) (ok bool) {
|
||||
for _, n := range s {
|
||||
if n.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Callback-based Subnet Set
|
||||
|
||||
// SubnetSetFunc is a function determining if ip belongs to the set of subnets.
|
||||
type SubnetSetFunc func(ip net.IP) (ok bool)
|
||||
|
||||
// type check
|
||||
var _ SubnetSet = SubnetSetFunc(nil)
|
||||
|
||||
// Contains implements the SubnetSet interface for SubnetSetFunc. The ip is not
|
||||
// required to be valid or non-nil so that f is responsible for the validation.
|
||||
func (f SubnetSetFunc) Contains(ip net.IP) (ok bool) { return f(ip) }
|
||||
|
||||
// Optimized Implementations Of Some Commonly Used Sets Of Networks
|
||||
|
||||
// IsLocallyServed checks if ip belongs to any network defined by [RFC 6303]:
|
||||
//
|
||||
// 10.0.0.0/8
|
||||
// 127.0.0.0/8
|
||||
// 169.254.0.0/16
|
||||
// 172.16.0.0/12
|
||||
// 192.0.2.0/24
|
||||
// 192.168.0.0/16
|
||||
// 198.51.100.0/24
|
||||
// 203.0.113.0/24
|
||||
// 255.255.255.255/32
|
||||
//
|
||||
// ::/128
|
||||
// ::1/128
|
||||
// 2001:db8::/32
|
||||
// fd00::/8
|
||||
// fe80::/10
|
||||
//
|
||||
// It may also be used as a [SubnetSetFunc].
|
||||
//
|
||||
// [RFC 6303]: https://datatracker.ietf.org/doc/html/rfc6303
|
||||
func IsLocallyServed(ip net.IP) (ok bool) {
|
||||
if ip == nil {
|
||||
return false
|
||||
} else if ip4 := ip.To4(); ip4 == nil {
|
||||
if len(ip) != net.IPv6len {
|
||||
return false
|
||||
}
|
||||
|
||||
return isLocallyServedV6(ip)
|
||||
} else {
|
||||
return isLocallyServedV4(ip4)
|
||||
}
|
||||
}
|
||||
|
||||
// IsLocallyServedAddr is like [IsLocallyServed] but for [netip.Addr]. Since
|
||||
// the argument's type is different, it cannot be used as a [SubnetSetFunc].
|
||||
func IsLocallyServedAddr(ip netip.Addr) (ok bool) {
|
||||
if ip.Is4() {
|
||||
return isLocallyServedV4(ip.AsSlice())
|
||||
}
|
||||
|
||||
return isLocallyServedV6(ip.AsSlice())
|
||||
}
|
||||
|
||||
// isLocallyServedV6 returns true if ip belongs to at least one of networks
|
||||
// listed in [RFC 6303]. The ip is expected to be a valid IPv6.
|
||||
//
|
||||
// See also [IsLocallyServed].
|
||||
//
|
||||
// [RFC 6303]: https://datatracker.ietf.org/doc/html/rfc6303
|
||||
func isLocallyServedV6(ip net.IP) (ok bool) {
|
||||
switch ip[0] {
|
||||
case 0x00:
|
||||
return string(ip[1:15]) == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" &&
|
||||
ip[15]&0xFE == 0x00
|
||||
case 0x20:
|
||||
return string(ip[1:4]) == "\x01\x0D\xB8"
|
||||
case 0xFE:
|
||||
return ip[1]&0xC0 == 0x80
|
||||
default:
|
||||
return ip[0] == 0xFD
|
||||
}
|
||||
}
|
||||
|
||||
// isLocallyServedV4 returns true if ip belongs to at least one of networks
|
||||
// listed in [RFC 6303]. The ip is expected to be a valid IPv4.
|
||||
//
|
||||
// See also [IsLocallyServed].
|
||||
//
|
||||
// [RFC 6303]: https://datatracker.ietf.org/doc/html/rfc6303
|
||||
func isLocallyServedV4(ip net.IP) (ok bool) {
|
||||
switch ip[0] {
|
||||
case 10, 127:
|
||||
return true
|
||||
case 169:
|
||||
return ip[1] == 254
|
||||
case 172:
|
||||
return ip[1]&0xF0 == 16
|
||||
case 192:
|
||||
return ip[1] == 168 || string(ip[1:3]) == "\x00\x02"
|
||||
case 198:
|
||||
return string(ip[1:3]) == "\x33\x64"
|
||||
case 203:
|
||||
return string(ip[1:3]) == "\x00\x71"
|
||||
default:
|
||||
return string(ip) == "\xFF\xFF\xFF\xFF"
|
||||
}
|
||||
}
|
||||
|
||||
// IsSpecialPurpose checks if ip belongs to any network defined by IANA
|
||||
// Special-Purpose Address Registry:
|
||||
//
|
||||
// 0.0.0.0/8 "This host on this network".
|
||||
// 10.0.0.0/8 Private-Use.
|
||||
// 100.64.0.0/10 Shared Address Space.
|
||||
// 127.0.0.0/8 Loopback.
|
||||
// 169.254.0.0/16 Link Local.
|
||||
// 172.16.0.0/12 Private-Use.
|
||||
// 192.0.0.0/24 IETF Protocol Assignments.
|
||||
// 192.0.0.0/29 DS-Lite.
|
||||
// 192.0.2.0/24 Documentation (TEST-NET-1)
|
||||
// 192.88.99.0/24 6to4 Relay Anycast.
|
||||
// 192.168.0.0/16 Private-Use.
|
||||
// 198.18.0.0/15 Benchmarking.
|
||||
// 198.51.100.0/24 Documentation (TEST-NET-2).
|
||||
// 203.0.113.0/24 Documentation (TEST-NET-3).
|
||||
// 240.0.0.0/4 Reserved.
|
||||
// 255.255.255.255/32 Limited Broadcast.
|
||||
//
|
||||
// ::/128 Unspecified Address.
|
||||
// ::1/128 Loopback Address.
|
||||
// 64:ff9b::/96 IPv4-IPv6 Translation Address.
|
||||
// 64:ff9b:1::/48 IPv4-IPv6 Translation Address.
|
||||
// 100::/64 Discard-Only Address Block.
|
||||
// 2001::/23 IETF Protocol Assignments.
|
||||
// 2001::/32 TEREDO.
|
||||
// 2001:1::1/128 Port Control Protocol Anycast.
|
||||
// 2001:1::2/128 Traversal Using Relays around NAT Anycast.
|
||||
// 2001:2::/48 Benchmarking.
|
||||
// 2001:3::/32 AMT.
|
||||
// 2001:4:112::/48 AS112-v6.
|
||||
// 2001:10::/28 ORCHID.
|
||||
// 2001:20::/28 ORCHIDv2.
|
||||
// 2001:db8::/32 Documentation.
|
||||
// 2002::/16 6to4.
|
||||
// 2620:4f:8000::/48 Direct Delegation AS112 Service.
|
||||
// fc00::/7 Unique-Local.
|
||||
// fe80::/10 Linked-Scoped Unicast.
|
||||
//
|
||||
// See https://www.iana.org/assignments/iana-ipv4-special-registry and
|
||||
// https://www.iana.org/assignments/iana-ipv6-special-registry.
|
||||
//
|
||||
// It may also be used as a [SubnetSetFunc].
|
||||
func IsSpecialPurpose(ip net.IP) (ok bool) {
|
||||
if ip == nil {
|
||||
return false
|
||||
} else if ip4 := ip.To4(); ip4 == nil {
|
||||
if len(ip) != net.IPv6len {
|
||||
return false
|
||||
}
|
||||
|
||||
return isSpecialPurposeV6(ip)
|
||||
} else {
|
||||
return isSpecialPurposeV4(ip4)
|
||||
}
|
||||
}
|
||||
|
||||
// IsSpecialPurposeAddr is like [IsSpecialPurpose] but for [netip.Addr]. Since
|
||||
// the argument's type is different, it cannot be used as a [SubnetSetFunc].
|
||||
func IsSpecialPurposeAddr(ip netip.Addr) (ok bool) {
|
||||
if ip.Is4() {
|
||||
return isSpecialPurposeV4(ip.AsSlice())
|
||||
}
|
||||
|
||||
return isSpecialPurposeV6(ip.AsSlice())
|
||||
}
|
||||
|
||||
// isSpecialPurposeV6 returns true if ip belongs to at least one of networks
|
||||
// from special-purpose address registries. The ip is expected to be a valid
|
||||
// IPv6.
|
||||
//
|
||||
// See also [IsSpecialPurpose].
|
||||
func isSpecialPurposeV6(ip net.IP) (ok bool) {
|
||||
switch ip[0] {
|
||||
case 0x00:
|
||||
ok = string(ip[1:5]) == "\x64\xFF\x9B\x00" &&
|
||||
(ip[5] == 0x01 || string(ip[5:12]) == "\x00\x00\x00\x00\x00\x00\x00")
|
||||
case 0x20:
|
||||
if ip[1] == 0x01 {
|
||||
ok = ip[2]&0xFE == 0x00
|
||||
} else {
|
||||
ok = ip[1] == 0x02
|
||||
}
|
||||
default:
|
||||
ok = string(ip[:6]) == "\x26\x20\x00\x4F\x80\x00" ||
|
||||
string(ip[:6]) == "\x01\x00\x00\x00\x00\x00" ||
|
||||
ip[0] == 0xFC
|
||||
}
|
||||
|
||||
return ok || isLocallyServedV6(ip)
|
||||
}
|
||||
|
||||
// isSpecialPurposeV4 returns true if ip belongs to at least one of networks
|
||||
// from special-purpose address registries. The ip is expected to be a valid
|
||||
// IPv4.
|
||||
//
|
||||
// See also [IsSpecialPurpose].
|
||||
func isSpecialPurposeV4(ip net.IP) (ok bool) {
|
||||
switch ip[0] {
|
||||
case 0:
|
||||
return true
|
||||
case 100:
|
||||
ok = ip[1]&0xC0 == 64
|
||||
case 192:
|
||||
ok = string(ip[1:3]) == "\x00\x00" || string(ip[1:3]) == "\x58\x63"
|
||||
case 198:
|
||||
ok = ip[1]&0xFE == 18
|
||||
default:
|
||||
ok = ip[0] >= 0xF0
|
||||
}
|
||||
|
||||
return ok || isLocallyServedV4(ip)
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
package stringutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// unit is a convenient alias for struct{}
|
||||
type unit = struct{}
|
||||
|
||||
// Set is a set of strings.
|
||||
type Set struct {
|
||||
m map[string]unit
|
||||
}
|
||||
|
||||
// NewSet returns a new string set containing strs.
|
||||
func NewSet(strs ...string) (set *Set) {
|
||||
set = &Set{
|
||||
m: make(map[string]unit, len(strs)),
|
||||
}
|
||||
|
||||
for _, s := range strs {
|
||||
set.Add(s)
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// Add adds s to the set. Add panics if the set is a nil set, just like a nil
|
||||
// map does.
|
||||
func (set *Set) Add(s string) {
|
||||
set.m[s] = unit{}
|
||||
}
|
||||
|
||||
// Clone returns a deep clone of set. If set is nil, clone is nil.
|
||||
func (set *Set) Clone() (clone *Set) {
|
||||
if set == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Set{
|
||||
m: maps.Clone(set.m),
|
||||
}
|
||||
}
|
||||
|
||||
// Del deletes s from the set. Calling Del on a nil set has no effect, just
|
||||
// like delete on an empty map doesn't.
|
||||
func (set *Set) Del(s string) {
|
||||
if set != nil {
|
||||
delete(set.m, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Equal returns true if set is equal to other.
|
||||
func (set *Set) Equal(other *Set) (ok bool) {
|
||||
if set == nil || other == nil {
|
||||
return set == other
|
||||
} else if set.Len() != other.Len() {
|
||||
return false
|
||||
}
|
||||
|
||||
for s := range set.m {
|
||||
if _, ok = other.m[s]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Has returns true if s is in the set. Calling Has on a nil set returns false,
|
||||
// just like indexing on an empty map does.
|
||||
func (set *Set) Has(s string) (ok bool) {
|
||||
if set != nil {
|
||||
_, ok = set.m[s]
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// Len returns the length of the set. A nil set has a length of zero, just like
|
||||
// an empty map.
|
||||
func (set *Set) Len() (n int) {
|
||||
if set == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return len(set.m)
|
||||
}
|
||||
|
||||
// Range calls f with each value of the set in an undefined order. If cont is
|
||||
// false, Range stops the iteration. Calling Range on a nil *Set has no effect.
|
||||
func (set *Set) Range(f func(s string) (cont bool)) {
|
||||
if set == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for s := range set.m {
|
||||
if !f(s) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface for *Set.
|
||||
func (set *Set) String() (s string) {
|
||||
return fmt.Sprintf("%q", set.Values())
|
||||
}
|
||||
|
||||
// Values returns all values in the set. The order of the values is undefined.
|
||||
// Values returns nil if the set is nil.
|
||||
func (set *Set) Values() (strs []string) {
|
||||
if set == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
strs = make([]string, 0, len(set.m))
|
||||
for s := range set.m {
|
||||
strs = append(strs, s)
|
||||
}
|
||||
|
||||
return strs
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
// Package stringutil contains utilities for dealing with strings.
|
||||
package stringutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// AllUnique returns true if all items of strs are unique.
|
||||
func AllUnique(strs []string) (ok bool) {
|
||||
set := NewSet()
|
||||
for _, s := range strs {
|
||||
if set.Has(s) {
|
||||
return false
|
||||
}
|
||||
|
||||
set.Add(s)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CloneSliceOrEmpty returns the copy of strs or empty strings slice if strs is
|
||||
// a nil slice.
|
||||
func CloneSliceOrEmpty(strs []string) (clone []string) {
|
||||
return append([]string{}, strs...)
|
||||
}
|
||||
|
||||
// CloneSlice returns the exact copy of strs.
|
||||
func CloneSlice(strs []string) (clone []string) {
|
||||
if strs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return CloneSliceOrEmpty(strs)
|
||||
}
|
||||
|
||||
// Coalesce returns the first non-empty string. It is named after the function
|
||||
// COALESCE in SQL except that since strings in Go are non-nullable, it uses an
|
||||
// empty string as a NULL value. If strs or all it's elements are empty, it
|
||||
// returns an empty string.
|
||||
func Coalesce(strs ...string) (res string) {
|
||||
for _, s := range strs {
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// ContainsFold reports whether s contains, ignoring letter case, substr.
|
||||
func ContainsFold(s, substr string) (ok bool) {
|
||||
sLen, substrLen := len(s), len(substr)
|
||||
if sLen < substrLen {
|
||||
return false
|
||||
}
|
||||
|
||||
if sLen == substrLen {
|
||||
return strings.EqualFold(s, substr)
|
||||
}
|
||||
|
||||
first, _ := utf8.DecodeRuneInString(substr)
|
||||
firstFolded := unicode.SimpleFold(first)
|
||||
|
||||
for i := 0; i != -1 && len(s) >= len(substr); {
|
||||
if strings.EqualFold(s[:substrLen], substr) {
|
||||
return true
|
||||
}
|
||||
|
||||
i = strings.IndexFunc(s[1:], func(r rune) (eq bool) {
|
||||
return r == first || r == firstFolded
|
||||
})
|
||||
|
||||
s = s[1+i:]
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// FilterOut returns a copy of strs with all strings for which f returned true
|
||||
// removed.
|
||||
func FilterOut(strs []string, f func(s string) (ok bool)) (filtered []string) {
|
||||
for _, s := range strs {
|
||||
if !f(s) {
|
||||
filtered = append(filtered, s)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// InSlice checks if strs contains str.
|
||||
func InSlice(strs []string, str string) (ok bool) {
|
||||
for _, s := range strs {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SplitTrimmed slices str into all substrings separated by sep and returns
|
||||
// a slice of the trimmed substrings between those separators with empty strings
|
||||
// skipped. If str has no such substrings, strs is an empty slice.
|
||||
func SplitTrimmed(str, sep string) (strs []string) {
|
||||
str = strings.TrimSpace(str)
|
||||
if str == "" {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
split := strings.Split(str, sep)
|
||||
|
||||
// Use the same underlying storage to reduce allocations.
|
||||
strs = split[:0]
|
||||
for _, s := range split {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
strs = append(strs, s)
|
||||
}
|
||||
|
||||
// Reset the remaining elements of the original slice so that the
|
||||
// garbage is collected.
|
||||
for i := len(strs); i < len(split); i++ {
|
||||
split[i] = ""
|
||||
}
|
||||
|
||||
return strs
|
||||
}
|
||||
|
||||
// WriteToBuilder is a convenient wrapper for strings.(*Builder).WriteString
|
||||
// that deals with multiple strings and ignores errors, since they are
|
||||
// guaranteed to be nil.
|
||||
//
|
||||
// b must not be nil.
|
||||
func WriteToBuilder(b *strings.Builder, strs ...string) {
|
||||
for _, s := range strs {
|
||||
_, _ = b.WriteString(s)
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// DiscardLogOutput runs tests with discarded logger output.
|
||||
//
|
||||
// TODO(a.garipov): Refactor project that use this to not use a global logger.
|
||||
func DiscardLogOutput(m *testing.M) {
|
||||
log.SetOutput(io.Discard)
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// PanicT can be used with the helpers from package require in cases when
|
||||
// testing.T and similar standard test helpers aren't safe for use, e.g. stub
|
||||
// HTTP handlers and goroutines.
|
||||
//
|
||||
// While this type also implements assert.TestingT, prefer to use require helper
|
||||
// functions, since this helper panics, which immediately fails the test.
|
||||
type PanicT struct{}
|
||||
|
||||
// type check
|
||||
var _ require.TestingT = PanicT{}
|
||||
|
||||
// Errorf implements the require.TestingT interface for PanicT. It panics with
|
||||
// an error with the given format.
|
||||
func (PanicT) Errorf(format string, args ...any) {
|
||||
panic(fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// FailNow implements the require.TestingT interface for PanicT. It is assumed
|
||||
// that it will never actually be called, since Errorf panics.
|
||||
func (PanicT) FailNow() {
|
||||
panic("test failed")
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// RequireSend waits until v is sent to ch or until the timeout is exceeded. If
|
||||
// the timeout is exceeded, the test is failed.
|
||||
func RequireSend[T any](t require.TestingT, ch chan<- T, v T, timeout time.Duration) {
|
||||
if h, ok := t.(interface{ Helper() }); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case ch <- v:
|
||||
// Go on.
|
||||
case <-timer.C:
|
||||
t.Errorf("did not send after %s", timeout)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireReceive waits until res is received from ch or until the timeout is
|
||||
// exceeded. If the timeout is exceeded, the test is failed.
|
||||
func RequireReceive[T any](t require.TestingT, ch <-chan T, timeout time.Duration) (res T, ok bool) {
|
||||
if h, isHelper := t.(interface{ Helper() }); isHelper {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case res, ok = <-ch:
|
||||
return res, ok
|
||||
case <-timer.C:
|
||||
t.Errorf("did not receive after %s", timeout)
|
||||
t.FailNow()
|
||||
|
||||
}
|
||||
|
||||
// Generally unreachable.
|
||||
return res, ok
|
||||
}
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
// Package testutil contains utilities for common testing patterns.
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// AssertErrorMsg asserts that the error is not nil and that its message is
|
||||
// equal to msg. If msg is an empty string, AssertErrorMsg asserts that the
|
||||
// error is nil instead.
|
||||
func AssertErrorMsg(t testing.TB, msg string, err error) (ok bool) {
|
||||
t.Helper()
|
||||
|
||||
if msg == "" {
|
||||
return assert.NoError(t, err)
|
||||
}
|
||||
|
||||
if !assert.Error(t, err) {
|
||||
return false
|
||||
}
|
||||
|
||||
return assert.Equal(t, msg, err.Error())
|
||||
}
|
||||
|
||||
// stringCodecChecker is used in AssertMarshalText and AssertUnmarshalText to
|
||||
// check against encoding and decoding corner cases.
|
||||
type stringCodecChecker struct {
|
||||
PtrMap map[string]*string `json:"ptr_map"`
|
||||
Map map[string]string `json:"map"`
|
||||
|
||||
PtrValue *string `json:"ptr_value"`
|
||||
Value string `json:"value"`
|
||||
|
||||
PtrArray [1]*string `json:"ptr_array"`
|
||||
Array [1]string `json:"array"`
|
||||
|
||||
PtrSlice []*string `json:"ptr_slice"`
|
||||
Slice []string `json:"slice"`
|
||||
}
|
||||
|
||||
// newStringCodecChecker returns a codecChecker instance where all fields are
|
||||
// set to s or pointer to s.
|
||||
func newStringCodecChecker(s string) (c *stringCodecChecker) {
|
||||
return &stringCodecChecker{
|
||||
PtrMap: map[string]*string{"1": &s},
|
||||
Map: map[string]string{"1": s},
|
||||
|
||||
PtrValue: &s,
|
||||
Value: s,
|
||||
|
||||
PtrArray: [1]*string{&s},
|
||||
Array: [1]string{s},
|
||||
|
||||
PtrSlice: []*string{&s},
|
||||
Slice: []string{s},
|
||||
}
|
||||
}
|
||||
|
||||
// newGenericCodecChecker constructs a pointer to value of a type similar to the
|
||||
// following:
|
||||
//
|
||||
// type checker struct {
|
||||
// PtrMap map[string]*T `json:"ptr_map"`
|
||||
// Map map[string]T `json:"map"`
|
||||
//
|
||||
// PtrValue *T `json:"ptr_value"`
|
||||
// Value T `json:"value"`
|
||||
//
|
||||
// PtrArray [1]*T `json:"ptr_array"`
|
||||
// Array [1]T `json:"array"`
|
||||
//
|
||||
// PtrSlice []*T `json:"ptr_slice"`
|
||||
// Slice []T `json:"slice"`
|
||||
// }
|
||||
//
|
||||
// where T is the type v points to. The slice and pointer fields are properly
|
||||
// initialized.
|
||||
//
|
||||
// TODO(a.garipov): Redo this with type parameters in Go 1.18.
|
||||
func newGenericCodecChecker(v any) (checkerVal reflect.Value) {
|
||||
strTyp := reflect.TypeOf("")
|
||||
|
||||
ptrTyp := reflect.TypeOf(v)
|
||||
ptrMapTyp := reflect.MapOf(strTyp, ptrTyp)
|
||||
ptrSliceTyp := reflect.SliceOf(ptrTyp)
|
||||
ptrArrayTyp := reflect.ArrayOf(1, ptrTyp)
|
||||
|
||||
typ := ptrTyp.Elem()
|
||||
mapTyp := reflect.MapOf(strTyp, typ)
|
||||
sliceTyp := reflect.SliceOf(typ)
|
||||
arrayTyp := reflect.ArrayOf(1, typ)
|
||||
|
||||
checkerTyp := reflect.StructOf([]reflect.StructField{{
|
||||
Name: "PtrMap",
|
||||
Type: ptrMapTyp,
|
||||
Tag: reflect.StructTag(`json:"ptr_map"`),
|
||||
}, {
|
||||
Name: "Map",
|
||||
Type: mapTyp,
|
||||
Tag: reflect.StructTag(`json:"map"`),
|
||||
}, {
|
||||
Name: "PtrValue",
|
||||
Type: ptrTyp,
|
||||
Tag: reflect.StructTag(`json:"ptr_value"`),
|
||||
}, {
|
||||
Name: "Value",
|
||||
Type: typ,
|
||||
Tag: reflect.StructTag(`json:"value"`),
|
||||
}, {
|
||||
Name: "PtrArray",
|
||||
Type: ptrArrayTyp,
|
||||
Tag: reflect.StructTag(`json:"ptr_array"`),
|
||||
}, {
|
||||
Name: "Array",
|
||||
Type: arrayTyp,
|
||||
Tag: reflect.StructTag(`json:"array"`),
|
||||
}, {
|
||||
Name: "PtrSlice",
|
||||
Type: ptrSliceTyp,
|
||||
Tag: reflect.StructTag(`json:"ptr_slice"`),
|
||||
}, {
|
||||
Name: "Slice",
|
||||
Type: sliceTyp,
|
||||
Tag: reflect.StructTag(`json:"slice"`),
|
||||
}})
|
||||
|
||||
checkerVal = reflect.New(checkerTyp)
|
||||
|
||||
checkerVal.Elem().Field(0).Set(reflect.MakeMap(ptrMapTyp))
|
||||
checkerVal.Elem().Field(1).Set(reflect.MakeMap(mapTyp))
|
||||
|
||||
checkerVal.Elem().Field(6).Set(reflect.MakeSlice(ptrSliceTyp, 1, 1))
|
||||
checkerVal.Elem().Field(7).Set(reflect.MakeSlice(sliceTyp, 1, 1))
|
||||
|
||||
return checkerVal
|
||||
}
|
||||
|
||||
// assignGenericCodecChecker assigns all fields to v or the value v points to.
|
||||
func assignGenericCodecChecker(checkerVal reflect.Value, v any) {
|
||||
keyVal := reflect.ValueOf("1")
|
||||
valPtr := reflect.ValueOf(v)
|
||||
val := valPtr.Elem()
|
||||
|
||||
checkerVal.Elem().Field(0).SetMapIndex(keyVal, valPtr)
|
||||
checkerVal.Elem().Field(1).SetMapIndex(keyVal, val)
|
||||
|
||||
checkerVal.Elem().Field(2).Set(valPtr)
|
||||
checkerVal.Elem().Field(3).Set(val)
|
||||
|
||||
checkerVal.Elem().Field(4).Index(0).Set(valPtr)
|
||||
checkerVal.Elem().Field(5).Index(0).Set(val)
|
||||
|
||||
checkerVal.Elem().Field(6).Index(0).Set(valPtr)
|
||||
checkerVal.Elem().Field(7).Index(0).Set(val)
|
||||
}
|
||||
|
||||
// AssertMarshalText checks that the implementation of v's MarshalText works in
|
||||
// all situations and results in the string s. v must be a pointer.
|
||||
//
|
||||
// See https://github.com/dominikh/go-tools/issues/911.
|
||||
func AssertMarshalText(t testing.TB, s string, v encoding.TextMarshaler) (ok bool) {
|
||||
t.Helper()
|
||||
|
||||
// Create a checker value.
|
||||
checkerVal := newGenericCodecChecker(v)
|
||||
assignGenericCodecChecker(checkerVal, v)
|
||||
|
||||
// Get the expected value.
|
||||
want, err := json.Marshal(newStringCodecChecker(s))
|
||||
require.NoErrorf(t, err, "marshaling expected value")
|
||||
|
||||
// Marshal and check against the expected value.
|
||||
checker := checkerVal.Interface()
|
||||
b, err := json.Marshal(checker)
|
||||
require.NoErrorf(t, err, "marshaling checker value")
|
||||
|
||||
return assert.Equal(t, string(want), string(b))
|
||||
}
|
||||
|
||||
// AssertUnmarshalText checks that the implementation of v's UnmarshalText works
|
||||
// in all situations and results in a value deeply equal to want.
|
||||
func AssertUnmarshalText(t testing.TB, s string, v encoding.TextUnmarshaler) (ok bool) {
|
||||
t.Helper()
|
||||
|
||||
// Create the expected value.
|
||||
want := newGenericCodecChecker(v)
|
||||
assignGenericCodecChecker(want, v)
|
||||
|
||||
// Create the checker value.
|
||||
got := newGenericCodecChecker(v)
|
||||
|
||||
// Marshal the expected data.
|
||||
strChecker := newStringCodecChecker(s)
|
||||
b, err := json.Marshal(strChecker)
|
||||
require.NoErrorf(t, err, "marshaling checker value")
|
||||
|
||||
// Unmarshal into the checker value and compare.
|
||||
err = json.Unmarshal(b, got.Interface())
|
||||
require.NoErrorf(t, err, "unmarshaling value")
|
||||
|
||||
return assert.Equal(t, want.Interface(), got.Interface())
|
||||
}
|
||||
|
||||
// CleanupAndRequireSuccess sets a cleanup function which checks the error
|
||||
// returned by f and fails the test using t if there is one.
|
||||
func CleanupAndRequireSuccess(t testing.TB, f func() (err error)) {
|
||||
t.Helper()
|
||||
|
||||
t.Cleanup(func() {
|
||||
err := f()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
// RequireTypeAssert is a helper that first requires the desired type and then,
|
||||
// if the type is correct, converts and returns the value.
|
||||
func RequireTypeAssert[T any](t testing.TB, v any) (res T) {
|
||||
t.Helper()
|
||||
|
||||
require.IsType(t, res, v)
|
||||
|
||||
return v.(T)
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
// Package timeutil contains types and utilities for dealing with time and
|
||||
// duration values.
|
||||
package timeutil
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// Day is the duration of one day.
|
||||
const Day time.Duration = 24 * time.Hour
|
||||
|
||||
// Duration is a wrapper for time.Duration providing functionality for encoding.
|
||||
type Duration struct {
|
||||
// time.Duration is embedded here to avoid implementing all the methods.
|
||||
time.Duration
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface for Duration. It wraps
|
||||
// time.Duration.String method and additionally cuts off non-leading zero values
|
||||
// of minutes and seconds. Some values which are differ between the
|
||||
// implementations:
|
||||
//
|
||||
// Duration: "1m", time.Duration: "1m0s"
|
||||
// Duration: "1h", time.Duration: "1h0m0s"
|
||||
// Duration: "1h1m", time.Duration: "1h1m0s"
|
||||
func (d Duration) String() (str string) {
|
||||
str = d.Duration.String()
|
||||
|
||||
const (
|
||||
tailMin = len(`0s`)
|
||||
tailMinSec = len(`0m0s`)
|
||||
)
|
||||
|
||||
const (
|
||||
secsInHour = time.Hour / time.Second
|
||||
minsInHour = time.Hour / time.Minute
|
||||
)
|
||||
|
||||
switch rounded := d.Duration / time.Second; {
|
||||
case
|
||||
rounded == 0,
|
||||
rounded*time.Second != d.Duration,
|
||||
rounded%60 != 0:
|
||||
// Return the uncut value if it's either equal to zero or has
|
||||
// fractions of a second or even whole seconds in it.
|
||||
return str
|
||||
case (rounded%secsInHour)/minsInHour != 0:
|
||||
return str[:len(str)-tailMin]
|
||||
default:
|
||||
return str[:len(str)-tailMinSec]
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface for Duration.
|
||||
func (d Duration) MarshalText() (text []byte, err error) {
|
||||
return []byte(d.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface for
|
||||
// *Duration.
|
||||
//
|
||||
// TODO(e.burkov): Make it able to parse larger units like days.
|
||||
func (d *Duration) UnmarshalText(b []byte) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "unmarshaling duration: %w") }()
|
||||
|
||||
d.Duration, err = time.ParseDuration(string(b))
|
||||
|
||||
return err
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Andreas Auernhammer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package chacha implements some low-level functions of the
|
||||
// ChaCha cipher family.
|
||||
package chacha // import "github.com/aead/chacha20/chacha"
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
// NonceSize is the size of the ChaCha20 nonce in bytes.
|
||||
NonceSize = 8
|
||||
|
||||
// INonceSize is the size of the IETF-ChaCha20 nonce in bytes.
|
||||
INonceSize = 12
|
||||
|
||||
// XNonceSize is the size of the XChaCha20 nonce in bytes.
|
||||
XNonceSize = 24
|
||||
|
||||
// KeySize is the size of the key in bytes.
|
||||
KeySize = 32
|
||||
)
|
||||
|
||||
var (
|
||||
useSSE2 bool
|
||||
useSSSE3 bool
|
||||
useAVX bool
|
||||
useAVX2 bool
|
||||
)
|
||||
|
||||
var (
|
||||
errKeySize = errors.New("chacha20/chacha: bad key length")
|
||||
errInvalidNonce = errors.New("chacha20/chacha: bad nonce length")
|
||||
)
|
||||
|
||||
func setup(state *[64]byte, nonce, key []byte) (err error) {
|
||||
if len(key) != KeySize {
|
||||
err = errKeySize
|
||||
return
|
||||
}
|
||||
var Nonce [16]byte
|
||||
switch len(nonce) {
|
||||
case NonceSize:
|
||||
copy(Nonce[8:], nonce)
|
||||
initialize(state, key, &Nonce)
|
||||
case INonceSize:
|
||||
copy(Nonce[4:], nonce)
|
||||
initialize(state, key, &Nonce)
|
||||
case XNonceSize:
|
||||
var tmpKey [32]byte
|
||||
var hNonce [16]byte
|
||||
|
||||
copy(hNonce[:], nonce[:16])
|
||||
copy(tmpKey[:], key)
|
||||
HChaCha20(&tmpKey, &hNonce, &tmpKey)
|
||||
copy(Nonce[8:], nonce[16:])
|
||||
initialize(state, tmpKey[:], &Nonce)
|
||||
|
||||
// BUG(aead): A "good" compiler will remove this (optimizations)
|
||||
// But using the provided key instead of tmpKey,
|
||||
// will change the key (-> probably confuses users)
|
||||
for i := range tmpKey {
|
||||
tmpKey[i] = 0
|
||||
}
|
||||
default:
|
||||
err = errInvalidNonce
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
|
||||
// The length of the nonce determinds the version of ChaCha20:
|
||||
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
|
||||
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
|
||||
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
|
||||
// The rounds argument specifies the number of rounds performed for keystream
|
||||
// generation - valid values are 8, 12 or 20. The src and dst may be the same slice
|
||||
// but otherwise should not overlap. If len(dst) < len(src) this function panics.
|
||||
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
|
||||
func XORKeyStream(dst, src, nonce, key []byte, rounds int) {
|
||||
if rounds != 20 && rounds != 12 && rounds != 8 {
|
||||
panic("chacha20/chacha: bad number of rounds")
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("chacha20/chacha: dst buffer is to small")
|
||||
}
|
||||
if len(nonce) == INonceSize && uint64(len(src)) > (1<<38) {
|
||||
panic("chacha20/chacha: src is too large")
|
||||
}
|
||||
|
||||
var block, state [64]byte
|
||||
if err := setup(&state, nonce, key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
xorKeyStream(dst, src, &block, &state, rounds)
|
||||
}
|
||||
|
||||
// Cipher implements ChaCha20/r (XChaCha20/r) for a given number of rounds r.
|
||||
type Cipher struct {
|
||||
state, block [64]byte
|
||||
off int
|
||||
rounds int // 20 for ChaCha20
|
||||
noncesize int
|
||||
}
|
||||
|
||||
// NewCipher returns a new *chacha.Cipher implementing the ChaCha20/r or XChaCha20/r
|
||||
// (r = 8, 12 or 20) stream cipher. The nonce must be unique for one key for all time.
|
||||
// The length of the nonce determinds the version of ChaCha20:
|
||||
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
|
||||
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
|
||||
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
|
||||
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
|
||||
func NewCipher(nonce, key []byte, rounds int) (*Cipher, error) {
|
||||
if rounds != 20 && rounds != 12 && rounds != 8 {
|
||||
panic("chacha20/chacha: bad number of rounds")
|
||||
}
|
||||
|
||||
c := new(Cipher)
|
||||
if err := setup(&(c.state), nonce, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.rounds = rounds
|
||||
|
||||
if len(nonce) == INonceSize {
|
||||
c.noncesize = INonceSize
|
||||
} else {
|
||||
c.noncesize = NonceSize
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// XORKeyStream crypts bytes from src to dst. Src and dst may be the same slice
|
||||
// but otherwise should not overlap. If len(dst) < len(src) the function panics.
|
||||
func (c *Cipher) XORKeyStream(dst, src []byte) {
|
||||
if len(dst) < len(src) {
|
||||
panic("chacha20/chacha: dst buffer is to small")
|
||||
}
|
||||
|
||||
if c.off > 0 {
|
||||
n := len(c.block[c.off:])
|
||||
if len(src) <= n {
|
||||
for i, v := range src {
|
||||
dst[i] = v ^ c.block[c.off]
|
||||
c.off++
|
||||
}
|
||||
if c.off == 64 {
|
||||
c.off = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for i, v := range c.block[c.off:] {
|
||||
dst[i] = src[i] ^ v
|
||||
}
|
||||
src = src[n:]
|
||||
dst = dst[n:]
|
||||
c.off = 0
|
||||
}
|
||||
|
||||
// check for counter overflow
|
||||
blocksToXOR := len(src) / 64
|
||||
if len(src)%64 != 0 {
|
||||
blocksToXOR++
|
||||
}
|
||||
var overflow bool
|
||||
if c.noncesize == INonceSize {
|
||||
overflow = binary.LittleEndian.Uint32(c.state[48:]) > math.MaxUint32-uint32(blocksToXOR)
|
||||
} else {
|
||||
overflow = binary.LittleEndian.Uint64(c.state[48:]) > math.MaxUint64-uint64(blocksToXOR)
|
||||
}
|
||||
if overflow {
|
||||
panic("chacha20/chacha: counter overflow")
|
||||
}
|
||||
|
||||
c.off += xorKeyStream(dst, src, &(c.block), &(c.state), c.rounds)
|
||||
}
|
||||
|
||||
// SetCounter skips ctr * 64 byte blocks. SetCounter(0) resets the cipher.
|
||||
// This function always skips the unused keystream of the current 64 byte block.
|
||||
func (c *Cipher) SetCounter(ctr uint64) {
|
||||
if c.noncesize == INonceSize {
|
||||
binary.LittleEndian.PutUint32(c.state[48:], uint32(ctr))
|
||||
} else {
|
||||
binary.LittleEndian.PutUint64(c.state[48:], ctr)
|
||||
}
|
||||
c.off = 0
|
||||
}
|
||||
|
||||
// HChaCha20 generates 32 pseudo-random bytes from a 128 bit nonce and a 256 bit secret key.
|
||||
// It can be used as a key-derivation-function (KDF).
|
||||
func HChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { hChaCha20(out, nonce, key) }
|
||||
-406
@@ -1,406 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build amd64,!gccgo,!appengine,!nacl
|
||||
|
||||
#include "const.s"
|
||||
#include "macro.s"
|
||||
|
||||
#define TWO 0(SP)
|
||||
#define C16 32(SP)
|
||||
#define C8 64(SP)
|
||||
#define STATE_0 96(SP)
|
||||
#define STATE_1 128(SP)
|
||||
#define STATE_2 160(SP)
|
||||
#define STATE_3 192(SP)
|
||||
#define TMP_0 224(SP)
|
||||
#define TMP_1 256(SP)
|
||||
|
||||
// func xorKeyStreamAVX(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
TEXT ·xorKeyStreamAVX2(SB), 4, $320-80
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ src_base+24(FP), SI
|
||||
MOVQ block+48(FP), BX
|
||||
MOVQ state+56(FP), AX
|
||||
MOVQ rounds+64(FP), DX
|
||||
MOVQ src_len+32(FP), CX
|
||||
|
||||
MOVQ SP, R8
|
||||
ADDQ $32, SP
|
||||
ANDQ $-32, SP
|
||||
|
||||
VMOVDQU 0(AX), Y2
|
||||
VMOVDQU 32(AX), Y3
|
||||
VPERM2I128 $0x22, Y2, Y0, Y0
|
||||
VPERM2I128 $0x33, Y2, Y1, Y1
|
||||
VPERM2I128 $0x22, Y3, Y2, Y2
|
||||
VPERM2I128 $0x33, Y3, Y3, Y3
|
||||
|
||||
TESTQ CX, CX
|
||||
JZ done
|
||||
|
||||
VMOVDQU ·one_AVX2<>(SB), Y4
|
||||
VPADDD Y4, Y3, Y3
|
||||
|
||||
VMOVDQA Y0, STATE_0
|
||||
VMOVDQA Y1, STATE_1
|
||||
VMOVDQA Y2, STATE_2
|
||||
VMOVDQA Y3, STATE_3
|
||||
|
||||
VMOVDQU ·rol16_AVX2<>(SB), Y4
|
||||
VMOVDQU ·rol8_AVX2<>(SB), Y5
|
||||
VMOVDQU ·two_AVX2<>(SB), Y6
|
||||
VMOVDQA Y4, Y14
|
||||
VMOVDQA Y5, Y15
|
||||
VMOVDQA Y4, C16
|
||||
VMOVDQA Y5, C8
|
||||
VMOVDQA Y6, TWO
|
||||
|
||||
CMPQ CX, $64
|
||||
JBE between_0_and_64
|
||||
CMPQ CX, $192
|
||||
JBE between_64_and_192
|
||||
CMPQ CX, $320
|
||||
JBE between_192_and_320
|
||||
CMPQ CX, $448
|
||||
JBE between_320_and_448
|
||||
|
||||
at_least_512:
|
||||
VMOVDQA Y0, Y4
|
||||
VMOVDQA Y1, Y5
|
||||
VMOVDQA Y2, Y6
|
||||
VPADDQ TWO, Y3, Y7
|
||||
VMOVDQA Y0, Y8
|
||||
VMOVDQA Y1, Y9
|
||||
VMOVDQA Y2, Y10
|
||||
VPADDQ TWO, Y7, Y11
|
||||
VMOVDQA Y0, Y12
|
||||
VMOVDQA Y1, Y13
|
||||
VMOVDQA Y2, Y14
|
||||
VPADDQ TWO, Y11, Y15
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_512:
|
||||
VMOVDQA Y8, TMP_0
|
||||
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y8, C16, C8)
|
||||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y8, C16, C8)
|
||||
VMOVDQA TMP_0, Y8
|
||||
VMOVDQA Y0, TMP_0
|
||||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y0, C16, C8)
|
||||
CHACHA_QROUND_AVX(Y12, Y13, Y14, Y15, Y0, C16, C8)
|
||||
CHACHA_SHUFFLE_AVX(Y1, Y2, Y3)
|
||||
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7)
|
||||
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11)
|
||||
CHACHA_SHUFFLE_AVX(Y13, Y14, Y15)
|
||||
|
||||
CHACHA_QROUND_AVX(Y12, Y13, Y14, Y15, Y0, C16, C8)
|
||||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y0, C16, C8)
|
||||
VMOVDQA TMP_0, Y0
|
||||
VMOVDQA Y8, TMP_0
|
||||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y8, C16, C8)
|
||||
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y8, C16, C8)
|
||||
VMOVDQA TMP_0, Y8
|
||||
CHACHA_SHUFFLE_AVX(Y3, Y2, Y1)
|
||||
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5)
|
||||
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9)
|
||||
CHACHA_SHUFFLE_AVX(Y15, Y14, Y13)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_512
|
||||
|
||||
VMOVDQA Y12, TMP_0
|
||||
VMOVDQA Y13, TMP_1
|
||||
VPADDD STATE_0, Y0, Y0
|
||||
VPADDD STATE_1, Y1, Y1
|
||||
VPADDD STATE_2, Y2, Y2
|
||||
VPADDD STATE_3, Y3, Y3
|
||||
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13)
|
||||
VMOVDQA STATE_0, Y0
|
||||
VMOVDQA STATE_1, Y1
|
||||
VMOVDQA STATE_2, Y2
|
||||
VMOVDQA STATE_3, Y3
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD Y0, Y4, Y4
|
||||
VPADDD Y1, Y5, Y5
|
||||
VPADDD Y2, Y6, Y6
|
||||
VPADDD Y3, Y7, Y7
|
||||
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD Y0, Y8, Y8
|
||||
VPADDD Y1, Y9, Y9
|
||||
VPADDD Y2, Y10, Y10
|
||||
VPADDD Y3, Y11, Y11
|
||||
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD TMP_0, Y0, Y12
|
||||
VPADDD TMP_1, Y1, Y13
|
||||
VPADDD Y2, Y14, Y14
|
||||
VPADDD Y3, Y15, Y15
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
CMPQ CX, $512
|
||||
JB less_than_512
|
||||
|
||||
XOR_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5)
|
||||
VMOVDQA Y3, STATE_3
|
||||
ADDQ $512, SI
|
||||
ADDQ $512, DI
|
||||
SUBQ $512, CX
|
||||
CMPQ CX, $448
|
||||
JA at_least_512
|
||||
|
||||
TESTQ CX, CX
|
||||
JZ done
|
||||
|
||||
VMOVDQA C16, Y14
|
||||
VMOVDQA C8, Y15
|
||||
|
||||
CMPQ CX, $64
|
||||
JBE between_0_and_64
|
||||
CMPQ CX, $192
|
||||
JBE between_64_and_192
|
||||
CMPQ CX, $320
|
||||
JBE between_192_and_320
|
||||
JMP between_320_and_448
|
||||
|
||||
less_than_512:
|
||||
XOR_UPPER_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5)
|
||||
EXTRACT_LOWER(BX, Y12, Y13, Y14, Y15, Y4)
|
||||
ADDQ $448, SI
|
||||
ADDQ $448, DI
|
||||
SUBQ $448, CX
|
||||
JMP finalize
|
||||
|
||||
between_320_and_448:
|
||||
VMOVDQA Y0, Y4
|
||||
VMOVDQA Y1, Y5
|
||||
VMOVDQA Y2, Y6
|
||||
VPADDQ TWO, Y3, Y7
|
||||
VMOVDQA Y0, Y8
|
||||
VMOVDQA Y1, Y9
|
||||
VMOVDQA Y2, Y10
|
||||
VPADDQ TWO, Y7, Y11
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_384:
|
||||
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y13, Y14, Y15)
|
||||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE_AVX(Y1, Y2, Y3)
|
||||
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7)
|
||||
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11)
|
||||
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y13, Y14, Y15)
|
||||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE_AVX(Y3, Y2, Y1)
|
||||
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5)
|
||||
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_384
|
||||
|
||||
VPADDD STATE_0, Y0, Y0
|
||||
VPADDD STATE_1, Y1, Y1
|
||||
VPADDD STATE_2, Y2, Y2
|
||||
VPADDD STATE_3, Y3, Y3
|
||||
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13)
|
||||
VMOVDQA STATE_0, Y0
|
||||
VMOVDQA STATE_1, Y1
|
||||
VMOVDQA STATE_2, Y2
|
||||
VMOVDQA STATE_3, Y3
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD Y0, Y4, Y4
|
||||
VPADDD Y1, Y5, Y5
|
||||
VPADDD Y2, Y6, Y6
|
||||
VPADDD Y3, Y7, Y7
|
||||
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD Y0, Y8, Y8
|
||||
VPADDD Y1, Y9, Y9
|
||||
VPADDD Y2, Y10, Y10
|
||||
VPADDD Y3, Y11, Y11
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
CMPQ CX, $384
|
||||
JB less_than_384
|
||||
|
||||
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
SUBQ $384, CX
|
||||
TESTQ CX, CX
|
||||
JE done
|
||||
|
||||
ADDQ $384, SI
|
||||
ADDQ $384, DI
|
||||
JMP between_0_and_64
|
||||
|
||||
less_than_384:
|
||||
XOR_UPPER_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12)
|
||||
ADDQ $320, SI
|
||||
ADDQ $320, DI
|
||||
SUBQ $320, CX
|
||||
JMP finalize
|
||||
|
||||
between_192_and_320:
|
||||
VMOVDQA Y0, Y4
|
||||
VMOVDQA Y1, Y5
|
||||
VMOVDQA Y2, Y6
|
||||
VMOVDQA Y3, Y7
|
||||
VMOVDQA Y0, Y8
|
||||
VMOVDQA Y1, Y9
|
||||
VMOVDQA Y2, Y10
|
||||
VPADDQ TWO, Y3, Y11
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_256:
|
||||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7)
|
||||
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11)
|
||||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5)
|
||||
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_256
|
||||
|
||||
VPADDD Y0, Y4, Y4
|
||||
VPADDD Y1, Y5, Y5
|
||||
VPADDD Y2, Y6, Y6
|
||||
VPADDD Y3, Y7, Y7
|
||||
VPADDQ TWO, Y3, Y3
|
||||
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
VPADDD Y0, Y8, Y8
|
||||
VPADDD Y1, Y9, Y9
|
||||
VPADDD Y2, Y10, Y10
|
||||
VPADDD Y3, Y11, Y11
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
CMPQ CX, $256
|
||||
JB less_than_256
|
||||
|
||||
XOR_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
SUBQ $256, CX
|
||||
TESTQ CX, CX
|
||||
JE done
|
||||
|
||||
ADDQ $256, SI
|
||||
ADDQ $256, DI
|
||||
JMP between_0_and_64
|
||||
|
||||
less_than_256:
|
||||
XOR_UPPER_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12)
|
||||
ADDQ $192, SI
|
||||
ADDQ $192, DI
|
||||
SUBQ $192, CX
|
||||
JMP finalize
|
||||
|
||||
between_64_and_192:
|
||||
VMOVDQA Y0, Y4
|
||||
VMOVDQA Y1, Y5
|
||||
VMOVDQA Y2, Y6
|
||||
VMOVDQA Y3, Y7
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_128:
|
||||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7)
|
||||
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_128
|
||||
|
||||
VPADDD Y0, Y4, Y4
|
||||
VPADDD Y1, Y5, Y5
|
||||
VPADDD Y2, Y6, Y6
|
||||
VPADDD Y3, Y7, Y7
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
CMPQ CX, $128
|
||||
JB less_than_128
|
||||
|
||||
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
SUBQ $128, CX
|
||||
TESTQ CX, CX
|
||||
JE done
|
||||
|
||||
ADDQ $128, SI
|
||||
ADDQ $128, DI
|
||||
JMP between_0_and_64
|
||||
|
||||
less_than_128:
|
||||
XOR_UPPER_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
EXTRACT_LOWER(BX, Y4, Y5, Y6, Y7, Y13)
|
||||
ADDQ $64, SI
|
||||
ADDQ $64, DI
|
||||
SUBQ $64, CX
|
||||
JMP finalize
|
||||
|
||||
between_0_and_64:
|
||||
VMOVDQA X0, X4
|
||||
VMOVDQA X1, X5
|
||||
VMOVDQA X2, X6
|
||||
VMOVDQA X3, X7
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_64:
|
||||
CHACHA_QROUND_AVX(X4, X5, X6, X7, X13, X14, X15)
|
||||
CHACHA_SHUFFLE_AVX(X5, X6, X7)
|
||||
CHACHA_QROUND_AVX(X4, X5, X6, X7, X13, X14, X15)
|
||||
CHACHA_SHUFFLE_AVX(X7, X6, X5)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_64
|
||||
|
||||
VPADDD X0, X4, X4
|
||||
VPADDD X1, X5, X5
|
||||
VPADDD X2, X6, X6
|
||||
VPADDD X3, X7, X7
|
||||
VMOVDQU ·one<>(SB), X0
|
||||
VPADDQ X0, X3, X3
|
||||
|
||||
CMPQ CX, $64
|
||||
JB less_than_64
|
||||
|
||||
XOR_AVX(DI, SI, 0, X4, X5, X6, X7, X13)
|
||||
SUBQ $64, CX
|
||||
JMP done
|
||||
|
||||
less_than_64:
|
||||
VMOVDQU X4, 0(BX)
|
||||
VMOVDQU X5, 16(BX)
|
||||
VMOVDQU X6, 32(BX)
|
||||
VMOVDQU X7, 48(BX)
|
||||
|
||||
finalize:
|
||||
XORQ R11, R11
|
||||
XORQ R12, R12
|
||||
MOVQ CX, BP
|
||||
|
||||
xor_loop:
|
||||
MOVB 0(SI), R11
|
||||
MOVB 0(BX), R12
|
||||
XORQ R11, R12
|
||||
MOVB R12, 0(DI)
|
||||
INCQ SI
|
||||
INCQ BX
|
||||
INCQ DI
|
||||
DECQ BP
|
||||
JA xor_loop
|
||||
|
||||
done:
|
||||
VMOVDQU X3, 48(AX)
|
||||
VZEROUPPER
|
||||
MOVQ R8, SP
|
||||
MOVQ CX, ret+72(FP)
|
||||
RET
|
||||
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build 386,!gccgo,!appengine,!nacl
|
||||
|
||||
package chacha
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
func init() {
|
||||
useSSE2 = cpu.X86.HasSSE2
|
||||
useSSSE3 = cpu.X86.HasSSSE3
|
||||
useAVX = false
|
||||
useAVX2 = false
|
||||
}
|
||||
|
||||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) {
|
||||
binary.LittleEndian.PutUint32(state[0:], sigma[0])
|
||||
binary.LittleEndian.PutUint32(state[4:], sigma[1])
|
||||
binary.LittleEndian.PutUint32(state[8:], sigma[2])
|
||||
binary.LittleEndian.PutUint32(state[12:], sigma[3])
|
||||
copy(state[16:], key[:])
|
||||
copy(state[48:], nonce[:])
|
||||
}
|
||||
|
||||
// This function is implemented in chacha_386.s
|
||||
//go:noescape
|
||||
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
|
||||
// This function is implemented in chacha_386.s
|
||||
//go:noescape
|
||||
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
|
||||
// This function is implemented in chacha_386.s
|
||||
//go:noescape
|
||||
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
|
||||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
|
||||
switch {
|
||||
case useSSSE3:
|
||||
hChaCha20SSSE3(out, nonce, key)
|
||||
case useSSE2:
|
||||
hChaCha20SSE2(out, nonce, key)
|
||||
default:
|
||||
hChaCha20Generic(out, nonce, key)
|
||||
}
|
||||
}
|
||||
|
||||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
|
||||
if useSSE2 {
|
||||
return xorKeyStreamSSE2(dst, src, block, state, rounds)
|
||||
} else {
|
||||
return xorKeyStreamGeneric(dst, src, block, state, rounds)
|
||||
}
|
||||
}
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build 386,!gccgo,!appengine,!nacl
|
||||
|
||||
#include "const.s"
|
||||
#include "macro.s"
|
||||
|
||||
// FINALIZE xors len bytes from src and block using
|
||||
// the temp. registers t0 and t1 and writes the result
|
||||
// to dst.
|
||||
#define FINALIZE(dst, src, block, len, t0, t1) \
|
||||
XORL t0, t0; \
|
||||
XORL t1, t1; \
|
||||
FINALIZE_LOOP:; \
|
||||
MOVB 0(src), t0; \
|
||||
MOVB 0(block), t1; \
|
||||
XORL t0, t1; \
|
||||
MOVB t1, 0(dst); \
|
||||
INCL src; \
|
||||
INCL block; \
|
||||
INCL dst; \
|
||||
DECL len; \
|
||||
JG FINALIZE_LOOP \
|
||||
|
||||
#define Dst DI
|
||||
#define Nonce AX
|
||||
#define Key BX
|
||||
#define Rounds DX
|
||||
|
||||
// func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
TEXT ·hChaCha20SSE2(SB), 4, $0-12
|
||||
MOVL out+0(FP), Dst
|
||||
MOVL nonce+4(FP), Nonce
|
||||
MOVL key+8(FP), Key
|
||||
|
||||
MOVOU ·sigma<>(SB), X0
|
||||
MOVOU 0*16(Key), X1
|
||||
MOVOU 1*16(Key), X2
|
||||
MOVOU 0*16(Nonce), X3
|
||||
MOVL $20, Rounds
|
||||
|
||||
chacha_loop:
|
||||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4)
|
||||
CHACHA_SHUFFLE_SSE(X1, X2, X3)
|
||||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4)
|
||||
CHACHA_SHUFFLE_SSE(X3, X2, X1)
|
||||
SUBL $2, Rounds
|
||||
JNZ chacha_loop
|
||||
|
||||
MOVOU X0, 0*16(Dst)
|
||||
MOVOU X3, 1*16(Dst)
|
||||
RET
|
||||
|
||||
// func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
TEXT ·hChaCha20SSSE3(SB), 4, $0-12
|
||||
MOVL out+0(FP), Dst
|
||||
MOVL nonce+4(FP), Nonce
|
||||
MOVL key+8(FP), Key
|
||||
|
||||
MOVOU ·sigma<>(SB), X0
|
||||
MOVOU 0*16(Key), X1
|
||||
MOVOU 1*16(Key), X2
|
||||
MOVOU 0*16(Nonce), X3
|
||||
MOVL $20, Rounds
|
||||
|
||||
MOVOU ·rol16<>(SB), X5
|
||||
MOVOU ·rol8<>(SB), X6
|
||||
|
||||
chacha_loop:
|
||||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6)
|
||||
CHACHA_SHUFFLE_SSE(X1, X2, X3)
|
||||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6)
|
||||
CHACHA_SHUFFLE_SSE(X3, X2, X1)
|
||||
SUBL $2, Rounds
|
||||
JNZ chacha_loop
|
||||
|
||||
MOVOU X0, 0*16(Dst)
|
||||
MOVOU X3, 1*16(Dst)
|
||||
RET
|
||||
|
||||
#undef Dst
|
||||
#undef Nonce
|
||||
#undef Key
|
||||
#undef Rounds
|
||||
|
||||
#define State AX
|
||||
#define Dst DI
|
||||
#define Src SI
|
||||
#define Len DX
|
||||
#define Tmp0 BX
|
||||
#define Tmp1 BP
|
||||
|
||||
// func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
TEXT ·xorKeyStreamSSE2(SB), 4, $0-40
|
||||
MOVL dst_base+0(FP), Dst
|
||||
MOVL src_base+12(FP), Src
|
||||
MOVL state+28(FP), State
|
||||
MOVL src_len+16(FP), Len
|
||||
MOVL $0, ret+36(FP) // Number of bytes written to the keystream buffer - 0 iff len mod 64 == 0
|
||||
|
||||
MOVOU 0*16(State), X0
|
||||
MOVOU 1*16(State), X1
|
||||
MOVOU 2*16(State), X2
|
||||
MOVOU 3*16(State), X3
|
||||
TESTL Len, Len
|
||||
JZ DONE
|
||||
|
||||
GENERATE_KEYSTREAM:
|
||||
MOVO X0, X4
|
||||
MOVO X1, X5
|
||||
MOVO X2, X6
|
||||
MOVO X3, X7
|
||||
MOVL rounds+32(FP), Tmp0
|
||||
|
||||
CHACHA_LOOP:
|
||||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0)
|
||||
CHACHA_SHUFFLE_SSE(X5, X6, X7)
|
||||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0)
|
||||
CHACHA_SHUFFLE_SSE(X7, X6, X5)
|
||||
SUBL $2, Tmp0
|
||||
JA CHACHA_LOOP
|
||||
|
||||
MOVOU 0*16(State), X0 // Restore X0 from state
|
||||
PADDL X0, X4
|
||||
PADDL X1, X5
|
||||
PADDL X2, X6
|
||||
PADDL X3, X7
|
||||
MOVOU ·one<>(SB), X0
|
||||
PADDQ X0, X3
|
||||
|
||||
CMPL Len, $64
|
||||
JL BUFFER_KEYSTREAM
|
||||
|
||||
XOR_SSE(Dst, Src, 0, X4, X5, X6, X7, X0)
|
||||
MOVOU 0*16(State), X0 // Restore X0 from state
|
||||
ADDL $64, Src
|
||||
ADDL $64, Dst
|
||||
SUBL $64, Len
|
||||
JZ DONE
|
||||
JMP GENERATE_KEYSTREAM // There is at least one more plaintext byte
|
||||
|
||||
BUFFER_KEYSTREAM:
|
||||
MOVL block+24(FP), State
|
||||
MOVOU X4, 0(State)
|
||||
MOVOU X5, 16(State)
|
||||
MOVOU X6, 32(State)
|
||||
MOVOU X7, 48(State)
|
||||
MOVL Len, ret+36(FP) // Number of bytes written to the keystream buffer - 0 < Len < 64
|
||||
FINALIZE(Dst, Src, State, Len, Tmp0, Tmp1)
|
||||
|
||||
DONE:
|
||||
MOVL state+28(FP), State
|
||||
MOVOU X3, 3*16(State)
|
||||
RET
|
||||
|
||||
#undef State
|
||||
#undef Dst
|
||||
#undef Src
|
||||
#undef Len
|
||||
#undef Tmp0
|
||||
#undef Tmp1
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build go1.7,amd64,!gccgo,!appengine,!nacl
|
||||
|
||||
package chacha
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
func init() {
|
||||
useSSE2 = cpu.X86.HasSSE2
|
||||
useSSSE3 = cpu.X86.HasSSSE3
|
||||
useAVX = cpu.X86.HasAVX
|
||||
useAVX2 = cpu.X86.HasAVX2
|
||||
}
|
||||
|
||||
// This function is implemented in chacha_amd64.s
|
||||
//go:noescape
|
||||
func initialize(state *[64]byte, key []byte, nonce *[16]byte)
|
||||
|
||||
// This function is implemented in chacha_amd64.s
|
||||
//go:noescape
|
||||
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
|
||||
// This function is implemented in chacha_amd64.s
|
||||
//go:noescape
|
||||
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
|
||||
// This function is implemented in chachaAVX2_amd64.s
|
||||
//go:noescape
|
||||
func hChaCha20AVX(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
|
||||
// This function is implemented in chacha_amd64.s
|
||||
//go:noescape
|
||||
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
|
||||
// This function is implemented in chacha_amd64.s
|
||||
//go:noescape
|
||||
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
|
||||
// This function is implemented in chacha_amd64.s
|
||||
//go:noescape
|
||||
func xorKeyStreamAVX(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
|
||||
// This function is implemented in chachaAVX2_amd64.s
|
||||
//go:noescape
|
||||
func xorKeyStreamAVX2(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
|
||||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
|
||||
switch {
|
||||
case useAVX:
|
||||
hChaCha20AVX(out, nonce, key)
|
||||
case useSSSE3:
|
||||
hChaCha20SSSE3(out, nonce, key)
|
||||
case useSSE2:
|
||||
hChaCha20SSE2(out, nonce, key)
|
||||
default:
|
||||
hChaCha20Generic(out, nonce, key)
|
||||
}
|
||||
}
|
||||
|
||||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
|
||||
switch {
|
||||
case useAVX2:
|
||||
return xorKeyStreamAVX2(dst, src, block, state, rounds)
|
||||
case useAVX:
|
||||
return xorKeyStreamAVX(dst, src, block, state, rounds)
|
||||
case useSSSE3:
|
||||
return xorKeyStreamSSSE3(dst, src, block, state, rounds)
|
||||
case useSSE2:
|
||||
return xorKeyStreamSSE2(dst, src, block, state, rounds)
|
||||
default:
|
||||
return xorKeyStreamGeneric(dst, src, block, state, rounds)
|
||||
}
|
||||
}
|
||||
-1072
File diff suppressed because it is too large
Load Diff
-319
@@ -1,319 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package chacha
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
var sigma = [4]uint32{0x61707865, 0x3320646e, 0x79622d32, 0x6b206574}
|
||||
|
||||
func xorKeyStreamGeneric(dst, src []byte, block, state *[64]byte, rounds int) int {
|
||||
for len(src) >= 64 {
|
||||
chachaGeneric(block, state, rounds)
|
||||
|
||||
for i, v := range block {
|
||||
dst[i] = src[i] ^ v
|
||||
}
|
||||
src = src[64:]
|
||||
dst = dst[64:]
|
||||
}
|
||||
|
||||
n := len(src)
|
||||
if n > 0 {
|
||||
chachaGeneric(block, state, rounds)
|
||||
for i, v := range src {
|
||||
dst[i] = v ^ block[i]
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func chachaGeneric(dst *[64]byte, state *[64]byte, rounds int) {
|
||||
v00 := binary.LittleEndian.Uint32(state[0:])
|
||||
v01 := binary.LittleEndian.Uint32(state[4:])
|
||||
v02 := binary.LittleEndian.Uint32(state[8:])
|
||||
v03 := binary.LittleEndian.Uint32(state[12:])
|
||||
v04 := binary.LittleEndian.Uint32(state[16:])
|
||||
v05 := binary.LittleEndian.Uint32(state[20:])
|
||||
v06 := binary.LittleEndian.Uint32(state[24:])
|
||||
v07 := binary.LittleEndian.Uint32(state[28:])
|
||||
v08 := binary.LittleEndian.Uint32(state[32:])
|
||||
v09 := binary.LittleEndian.Uint32(state[36:])
|
||||
v10 := binary.LittleEndian.Uint32(state[40:])
|
||||
v11 := binary.LittleEndian.Uint32(state[44:])
|
||||
v12 := binary.LittleEndian.Uint32(state[48:])
|
||||
v13 := binary.LittleEndian.Uint32(state[52:])
|
||||
v14 := binary.LittleEndian.Uint32(state[56:])
|
||||
v15 := binary.LittleEndian.Uint32(state[60:])
|
||||
|
||||
s00, s01, s02, s03, s04, s05, s06, s07 := v00, v01, v02, v03, v04, v05, v06, v07
|
||||
s08, s09, s10, s11, s12, s13, s14, s15 := v08, v09, v10, v11, v12, v13, v14, v15
|
||||
|
||||
for i := 0; i < rounds; i += 2 {
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
}
|
||||
|
||||
v00 += s00
|
||||
v01 += s01
|
||||
v02 += s02
|
||||
v03 += s03
|
||||
v04 += s04
|
||||
v05 += s05
|
||||
v06 += s06
|
||||
v07 += s07
|
||||
v08 += s08
|
||||
v09 += s09
|
||||
v10 += s10
|
||||
v11 += s11
|
||||
v12 += s12
|
||||
v13 += s13
|
||||
v14 += s14
|
||||
v15 += s15
|
||||
|
||||
s12++
|
||||
binary.LittleEndian.PutUint32(state[48:], s12)
|
||||
if s12 == 0 { // indicates overflow
|
||||
s13++
|
||||
binary.LittleEndian.PutUint32(state[52:], s13)
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(dst[0:], v00)
|
||||
binary.LittleEndian.PutUint32(dst[4:], v01)
|
||||
binary.LittleEndian.PutUint32(dst[8:], v02)
|
||||
binary.LittleEndian.PutUint32(dst[12:], v03)
|
||||
binary.LittleEndian.PutUint32(dst[16:], v04)
|
||||
binary.LittleEndian.PutUint32(dst[20:], v05)
|
||||
binary.LittleEndian.PutUint32(dst[24:], v06)
|
||||
binary.LittleEndian.PutUint32(dst[28:], v07)
|
||||
binary.LittleEndian.PutUint32(dst[32:], v08)
|
||||
binary.LittleEndian.PutUint32(dst[36:], v09)
|
||||
binary.LittleEndian.PutUint32(dst[40:], v10)
|
||||
binary.LittleEndian.PutUint32(dst[44:], v11)
|
||||
binary.LittleEndian.PutUint32(dst[48:], v12)
|
||||
binary.LittleEndian.PutUint32(dst[52:], v13)
|
||||
binary.LittleEndian.PutUint32(dst[56:], v14)
|
||||
binary.LittleEndian.PutUint32(dst[60:], v15)
|
||||
}
|
||||
|
||||
func hChaCha20Generic(out *[32]byte, nonce *[16]byte, key *[32]byte) {
|
||||
v00 := sigma[0]
|
||||
v01 := sigma[1]
|
||||
v02 := sigma[2]
|
||||
v03 := sigma[3]
|
||||
v04 := binary.LittleEndian.Uint32(key[0:])
|
||||
v05 := binary.LittleEndian.Uint32(key[4:])
|
||||
v06 := binary.LittleEndian.Uint32(key[8:])
|
||||
v07 := binary.LittleEndian.Uint32(key[12:])
|
||||
v08 := binary.LittleEndian.Uint32(key[16:])
|
||||
v09 := binary.LittleEndian.Uint32(key[20:])
|
||||
v10 := binary.LittleEndian.Uint32(key[24:])
|
||||
v11 := binary.LittleEndian.Uint32(key[28:])
|
||||
v12 := binary.LittleEndian.Uint32(nonce[0:])
|
||||
v13 := binary.LittleEndian.Uint32(nonce[4:])
|
||||
v14 := binary.LittleEndian.Uint32(nonce[8:])
|
||||
v15 := binary.LittleEndian.Uint32(nonce[12:])
|
||||
|
||||
for i := 0; i < 20; i += 2 {
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:], v00)
|
||||
binary.LittleEndian.PutUint32(out[4:], v01)
|
||||
binary.LittleEndian.PutUint32(out[8:], v02)
|
||||
binary.LittleEndian.PutUint32(out[12:], v03)
|
||||
binary.LittleEndian.PutUint32(out[16:], v12)
|
||||
binary.LittleEndian.PutUint32(out[20:], v13)
|
||||
binary.LittleEndian.PutUint32(out[24:], v14)
|
||||
binary.LittleEndian.PutUint32(out[28:], v15)
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!386 gccgo appengine nacl
|
||||
|
||||
package chacha
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
func init() {
|
||||
useSSE2 = false
|
||||
useSSSE3 = false
|
||||
useAVX = false
|
||||
useAVX2 = false
|
||||
}
|
||||
|
||||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) {
|
||||
binary.LittleEndian.PutUint32(state[0:], sigma[0])
|
||||
binary.LittleEndian.PutUint32(state[4:], sigma[1])
|
||||
binary.LittleEndian.PutUint32(state[8:], sigma[2])
|
||||
binary.LittleEndian.PutUint32(state[12:], sigma[3])
|
||||
copy(state[16:], key[:])
|
||||
copy(state[48:], nonce[:])
|
||||
}
|
||||
|
||||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
|
||||
return xorKeyStreamGeneric(dst, src, block, state, rounds)
|
||||
}
|
||||
|
||||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
|
||||
hChaCha20Generic(out, nonce, key)
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
// Copyright (c) 2018 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build 386,!gccgo,!appengine,!nacl amd64,!gccgo,!appengine,!nacl
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·sigma<>+0x00(SB)/4, $0x61707865
|
||||
DATA ·sigma<>+0x04(SB)/4, $0x3320646e
|
||||
DATA ·sigma<>+0x08(SB)/4, $0x79622d32
|
||||
DATA ·sigma<>+0x0C(SB)/4, $0x6b206574
|
||||
GLOBL ·sigma<>(SB), (NOPTR+RODATA), $16 // The 4 ChaCha initialization constants
|
||||
|
||||
// SSE2/SSE3/AVX constants
|
||||
|
||||
DATA ·one<>+0x00(SB)/8, $1
|
||||
DATA ·one<>+0x08(SB)/8, $0
|
||||
GLOBL ·one<>(SB), (NOPTR+RODATA), $16 // The constant 1 as 128 bit value
|
||||
|
||||
DATA ·rol16<>+0x00(SB)/8, $0x0504070601000302
|
||||
DATA ·rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
|
||||
GLOBL ·rol16<>(SB), (NOPTR+RODATA), $16 // The PSHUFB 16 bit left rotate constant
|
||||
|
||||
DATA ·rol8<>+0x00(SB)/8, $0x0605040702010003
|
||||
DATA ·rol8<>+0x08(SB)/8, $0x0E0D0C0F0A09080B
|
||||
GLOBL ·rol8<>(SB), (NOPTR+RODATA), $16 // The PSHUFB 8 bit left rotate constant
|
||||
|
||||
// AVX2 constants
|
||||
|
||||
DATA ·one_AVX2<>+0x00(SB)/8, $0
|
||||
DATA ·one_AVX2<>+0x08(SB)/8, $0
|
||||
DATA ·one_AVX2<>+0x10(SB)/8, $1
|
||||
DATA ·one_AVX2<>+0x18(SB)/8, $0
|
||||
GLOBL ·one_AVX2<>(SB), (NOPTR+RODATA), $32 // The constant 1 as 256 bit value
|
||||
|
||||
DATA ·two_AVX2<>+0x00(SB)/8, $2
|
||||
DATA ·two_AVX2<>+0x08(SB)/8, $0
|
||||
DATA ·two_AVX2<>+0x10(SB)/8, $2
|
||||
DATA ·two_AVX2<>+0x18(SB)/8, $0
|
||||
GLOBL ·two_AVX2<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
DATA ·rol16_AVX2<>+0x00(SB)/8, $0x0504070601000302
|
||||
DATA ·rol16_AVX2<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
|
||||
DATA ·rol16_AVX2<>+0x10(SB)/8, $0x0504070601000302
|
||||
DATA ·rol16_AVX2<>+0x18(SB)/8, $0x0D0C0F0E09080B0A
|
||||
GLOBL ·rol16_AVX2<>(SB), (NOPTR+RODATA), $32 // The VPSHUFB 16 bit left rotate constant
|
||||
|
||||
DATA ·rol8_AVX2<>+0x00(SB)/8, $0x0605040702010003
|
||||
DATA ·rol8_AVX2<>+0x08(SB)/8, $0x0E0D0C0F0A09080B
|
||||
DATA ·rol8_AVX2<>+0x10(SB)/8, $0x0605040702010003
|
||||
DATA ·rol8_AVX2<>+0x18(SB)/8, $0x0E0D0C0F0A09080B
|
||||
GLOBL ·rol8_AVX2<>(SB), (NOPTR+RODATA), $32 // The VPSHUFB 8 bit left rotate constant
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
// Copyright (c) 2018 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build 386,!gccgo,!appengine,!nacl amd64,!gccgo,!appengine,!nacl
|
||||
|
||||
// ROTL_SSE rotates all 4 32 bit values of the XMM register v
|
||||
// left by n bits using SSE2 instructions (0 <= n <= 32).
|
||||
// The XMM register t is used as a temp. register.
|
||||
#define ROTL_SSE(n, t, v) \
|
||||
MOVO v, t; \
|
||||
PSLLL $n, t; \
|
||||
PSRLL $(32-n), v; \
|
||||
PXOR t, v
|
||||
|
||||
// ROTL_AVX rotates all 4/8 32 bit values of the AVX/AVX2 register v
|
||||
// left by n bits using AVX/AVX2 instructions (0 <= n <= 32).
|
||||
// The AVX/AVX2 register t is used as a temp. register.
|
||||
#define ROTL_AVX(n, t, v) \
|
||||
VPSLLD $n, v, t; \
|
||||
VPSRLD $(32-n), v, v; \
|
||||
VPXOR v, t, v
|
||||
|
||||
// CHACHA_QROUND_SSE2 performs a ChaCha quarter-round using the
|
||||
// 4 XMM registers v0, v1, v2 and v3. It uses only ROTL_SSE2 for
|
||||
// rotations. The XMM register t is used as a temp. register.
|
||||
#define CHACHA_QROUND_SSE2(v0, v1, v2, v3, t) \
|
||||
PADDL v1, v0; \
|
||||
PXOR v0, v3; \
|
||||
ROTL_SSE(16, t, v3); \
|
||||
PADDL v3, v2; \
|
||||
PXOR v2, v1; \
|
||||
ROTL_SSE(12, t, v1); \
|
||||
PADDL v1, v0; \
|
||||
PXOR v0, v3; \
|
||||
ROTL_SSE(8, t, v3); \
|
||||
PADDL v3, v2; \
|
||||
PXOR v2, v1; \
|
||||
ROTL_SSE(7, t, v1)
|
||||
|
||||
// CHACHA_QROUND_SSSE3 performs a ChaCha quarter-round using the
|
||||
// 4 XMM registers v0, v1, v2 and v3. It uses PSHUFB for 8/16 bit
|
||||
// rotations. The XMM register t is used as a temp. register.
|
||||
//
|
||||
// r16 holds the PSHUFB constant for a 16 bit left rotate.
|
||||
// r8 holds the PSHUFB constant for a 8 bit left rotate.
|
||||
#define CHACHA_QROUND_SSSE3(v0, v1, v2, v3, t, r16, r8) \
|
||||
PADDL v1, v0; \
|
||||
PXOR v0, v3; \
|
||||
PSHUFB r16, v3; \
|
||||
PADDL v3, v2; \
|
||||
PXOR v2, v1; \
|
||||
ROTL_SSE(12, t, v1); \
|
||||
PADDL v1, v0; \
|
||||
PXOR v0, v3; \
|
||||
PSHUFB r8, v3; \
|
||||
PADDL v3, v2; \
|
||||
PXOR v2, v1; \
|
||||
ROTL_SSE(7, t, v1)
|
||||
|
||||
// CHACHA_QROUND_AVX performs a ChaCha quarter-round using the
|
||||
// 4 AVX/AVX2 registers v0, v1, v2 and v3. It uses VPSHUFB for 8/16 bit
|
||||
// rotations. The AVX/AVX2 register t is used as a temp. register.
|
||||
//
|
||||
// r16 holds the VPSHUFB constant for a 16 bit left rotate.
|
||||
// r8 holds the VPSHUFB constant for a 8 bit left rotate.
|
||||
#define CHACHA_QROUND_AVX(v0, v1, v2, v3, t, r16, r8) \
|
||||
VPADDD v0, v1, v0; \
|
||||
VPXOR v3, v0, v3; \
|
||||
VPSHUFB r16, v3, v3; \
|
||||
VPADDD v2, v3, v2; \
|
||||
VPXOR v1, v2, v1; \
|
||||
ROTL_AVX(12, t, v1); \
|
||||
VPADDD v0, v1, v0; \
|
||||
VPXOR v3, v0, v3; \
|
||||
VPSHUFB r8, v3, v3; \
|
||||
VPADDD v2, v3, v2; \
|
||||
VPXOR v1, v2, v1; \
|
||||
ROTL_AVX(7, t, v1)
|
||||
|
||||
// CHACHA_SHUFFLE_SSE performs a ChaCha shuffle using the
|
||||
// 3 XMM registers v1, v2 and v3. The inverse shuffle is
|
||||
// performed by switching v1 and v3: CHACHA_SHUFFLE_SSE(v3, v2, v1).
|
||||
#define CHACHA_SHUFFLE_SSE(v1, v2, v3) \
|
||||
PSHUFL $0x39, v1, v1; \
|
||||
PSHUFL $0x4E, v2, v2; \
|
||||
PSHUFL $0x93, v3, v3
|
||||
|
||||
// CHACHA_SHUFFLE_AVX performs a ChaCha shuffle using the
|
||||
// 3 AVX/AVX2 registers v1, v2 and v3. The inverse shuffle is
|
||||
// performed by switching v1 and v3: CHACHA_SHUFFLE_AVX(v3, v2, v1).
|
||||
#define CHACHA_SHUFFLE_AVX(v1, v2, v3) \
|
||||
VPSHUFD $0x39, v1, v1; \
|
||||
VPSHUFD $0x4E, v2, v2; \
|
||||
VPSHUFD $0x93, v3, v3
|
||||
|
||||
// XOR_SSE extracts 4x16 byte vectors from src at
|
||||
// off, xors all vectors with the corresponding XMM
|
||||
// register (v0 - v3) and writes the result to dst
|
||||
// at off.
|
||||
// The XMM register t is used as a temp. register.
|
||||
#define XOR_SSE(dst, src, off, v0, v1, v2, v3, t) \
|
||||
MOVOU 0+off(src), t; \
|
||||
PXOR v0, t; \
|
||||
MOVOU t, 0+off(dst); \
|
||||
MOVOU 16+off(src), t; \
|
||||
PXOR v1, t; \
|
||||
MOVOU t, 16+off(dst); \
|
||||
MOVOU 32+off(src), t; \
|
||||
PXOR v2, t; \
|
||||
MOVOU t, 32+off(dst); \
|
||||
MOVOU 48+off(src), t; \
|
||||
PXOR v3, t; \
|
||||
MOVOU t, 48+off(dst)
|
||||
|
||||
// XOR_AVX extracts 4x16 byte vectors from src at
|
||||
// off, xors all vectors with the corresponding AVX
|
||||
// register (v0 - v3) and writes the result to dst
|
||||
// at off.
|
||||
// The XMM register t is used as a temp. register.
|
||||
#define XOR_AVX(dst, src, off, v0, v1, v2, v3, t) \
|
||||
VPXOR 0+off(src), v0, t; \
|
||||
VMOVDQU t, 0+off(dst); \
|
||||
VPXOR 16+off(src), v1, t; \
|
||||
VMOVDQU t, 16+off(dst); \
|
||||
VPXOR 32+off(src), v2, t; \
|
||||
VMOVDQU t, 32+off(dst); \
|
||||
VPXOR 48+off(src), v3, t; \
|
||||
VMOVDQU t, 48+off(dst)
|
||||
|
||||
#define XOR_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \
|
||||
VMOVDQU (0+off)(src), t0; \
|
||||
VPERM2I128 $32, v1, v0, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (0+off)(dst); \
|
||||
VMOVDQU (32+off)(src), t0; \
|
||||
VPERM2I128 $32, v3, v2, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (32+off)(dst); \
|
||||
VMOVDQU (64+off)(src), t0; \
|
||||
VPERM2I128 $49, v1, v0, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (64+off)(dst); \
|
||||
VMOVDQU (96+off)(src), t0; \
|
||||
VPERM2I128 $49, v3, v2, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (96+off)(dst)
|
||||
|
||||
#define XOR_UPPER_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \
|
||||
VMOVDQU (0+off)(src), t0; \
|
||||
VPERM2I128 $32, v1, v0, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (0+off)(dst); \
|
||||
VMOVDQU (32+off)(src), t0; \
|
||||
VPERM2I128 $32, v3, v2, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (32+off)(dst); \
|
||||
|
||||
#define EXTRACT_LOWER(dst, v0, v1, v2, v3, t0) \
|
||||
VPERM2I128 $49, v1, v0, t0; \
|
||||
VMOVDQU t0, 0(dst); \
|
||||
VPERM2I128 $49, v3, v2, t0; \
|
||||
VMOVDQU t0, 32(dst)
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
.vscode
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.8.x"
|
||||
- "1.9.x"
|
||||
- "1.10.x"
|
||||
|
||||
env:
|
||||
- TRAVIS_GOARCH=amd64
|
||||
- TRAVIS_GOARCH=386
|
||||
|
||||
before_install:
|
||||
- export GOARCH=$TRAVIS_GOARCH
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
script:
|
||||
- diff -au <(gofmt -d .) <(printf "")
|
||||
- go test -v ./...
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Andreas Auernhammer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
[](https://godoc.org/github.com/aead/poly1305)
|
||||
|
||||
## The poly1305 message authentication code
|
||||
|
||||
Poly1305 is a fast, one-time authentication function created by Daniel J. Bernstein.
|
||||
It is infeasible for an attacker to generate an authenticator for a message without the key.
|
||||
However, a key must only be used for a single message. Authenticating two different messages
|
||||
with the same key allows an attacker to forge authenticators for other messages with the same key.
|
||||
|
||||
### Installation
|
||||
Install in your GOPATH: `go get -u github.com/aead/poly1305`
|
||||
|
||||
### Requirements
|
||||
All Go versions >= 1.7 are supported.
|
||||
|
||||
### Performance
|
||||
|
||||
#### AMD64
|
||||
Hardware: Intel i7-6500U 2.50GHz x 2
|
||||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic
|
||||
Go version: 1.8.0
|
||||
|
||||
**AVX2**
|
||||
```
|
||||
name speed cpb
|
||||
Sum_64-4 1.60GB/s ± 0% 1.39
|
||||
Sum_256-4 2.32GB/s ± 1% 1.00
|
||||
Sum_1K-4 3.61GB/s ± 1% 0.65
|
||||
Sum_8K-4 4.20GB/s ± 1% 0.55
|
||||
Write_64-4 2.04GB/s ± 0% 1.14
|
||||
Write_256-4 3.50GB/s ± 2% 0.67
|
||||
Write_1K-4 4.08GB/s ± 2% 0.57
|
||||
Write_8K-4 4.25GB/s ± 2% 0.55
|
||||
```
|
||||
|
||||
**x64**
|
||||
|
||||
```
|
||||
name speed cpb
|
||||
Sum_64-4 1.60GB/s ± 1% 1.46
|
||||
Sum_256-4 2.11GB/s ± 3% 1.10
|
||||
Sum_1K-4 2.35GB/s ±13% 0.99
|
||||
Sum_8K-4 2.47GB/s ±13% 0.94
|
||||
Write_64-4 1.81GB/s ± 5% 1.29
|
||||
Write_256-4 2.24GB/s ± 4% 1.04
|
||||
Write_1K-4 2.55GB/s ± 0% 0.91
|
||||
Write_8K-4 2.63GB/s ± 0% 0.88
|
||||
```
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package poly1305 implements Poly1305 one-time message authentication code
|
||||
// defined in RFC 7539..
|
||||
//
|
||||
// Poly1305 is a fast, one-time authentication function. It is infeasible for an
|
||||
// attacker to generate an authenticator for a message without the key.
|
||||
// However, a key must only be used for a single message. Authenticating two
|
||||
// different messages with the same key allows an attacker to forge
|
||||
// authenticators for other messages with the same key.
|
||||
package poly1305 // import "github.com/aead/poly1305"
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// TagSize is the size of the poly1305 authentication tag in bytes.
|
||||
const TagSize = 16
|
||||
|
||||
var errWriteAfterSum = errors.New("checksum already computed - adding more data is not allowed")
|
||||
|
||||
// Verify returns true if and only if the mac is a valid authenticator
|
||||
// for msg with the given key.
|
||||
func Verify(mac *[TagSize]byte, msg []byte, key [32]byte) bool {
|
||||
sum := Sum(msg, key)
|
||||
return subtle.ConstantTimeCompare(sum[:], mac[:]) == 1
|
||||
}
|
||||
-871
@@ -1,871 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This code is inspired by the poly1305 AVX2 implementation by Shay Gueron, and Vlad Krasnov.
|
||||
|
||||
// +build amd64, !gccgo, !appengine
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA addMaskAVX2<>+0x00(SB)/8, $0x3FFFFFF
|
||||
DATA addMaskAVX2<>+0x08(SB)/8, $0x3FFFFFF
|
||||
DATA addMaskAVX2<>+0x10(SB)/8, $0x3FFFFFF
|
||||
DATA addMaskAVX2<>+0x18(SB)/8, $0x3FFFFFF
|
||||
GLOBL addMaskAVX2<>(SB), RODATA, $32
|
||||
|
||||
DATA poly1305MaskAVX2<>+0x00(SB)/8, $0xFFFFFFC0FFFFFFF
|
||||
DATA poly1305MaskAVX2<>+0x08(SB)/8, $0xFFFFFFC0FFFFFFF
|
||||
DATA poly1305MaskAVX2<>+0x10(SB)/8, $0xFFFFFFC0FFFFFFF
|
||||
DATA poly1305MaskAVX2<>+0x18(SB)/8, $0xFFFFFFC0FFFFFFF
|
||||
DATA poly1305MaskAVX2<>+0x20(SB)/8, $0xFFFFFFC0FFFFFFC
|
||||
DATA poly1305MaskAVX2<>+0x28(SB)/8, $0xFFFFFFC0FFFFFFC
|
||||
DATA poly1305MaskAVX2<>+0x30(SB)/8, $0xFFFFFFC0FFFFFFC
|
||||
DATA poly1305MaskAVX2<>+0x38(SB)/8, $0xFFFFFFC0FFFFFFC
|
||||
GLOBL poly1305MaskAVX2<>(SB), RODATA, $64
|
||||
|
||||
DATA oneBit<>+0x00(SB)/8, $0x1000000
|
||||
DATA oneBit<>+0x08(SB)/8, $0x1000000
|
||||
DATA oneBit<>+0x10(SB)/8, $0x1000000
|
||||
DATA oneBit<>+0x18(SB)/8, $0x1000000
|
||||
GLOBL oneBit<>(SB), RODATA, $32
|
||||
|
||||
DATA fixPermutation<>+0x00(SB)/4, $6
|
||||
DATA fixPermutation<>+0x04(SB)/4, $7
|
||||
DATA fixPermutation<>+0x08(SB)/4, $6
|
||||
DATA fixPermutation<>+0x0c(SB)/4, $7
|
||||
DATA fixPermutation<>+0x10(SB)/4, $6
|
||||
DATA fixPermutation<>+0x14(SB)/4, $7
|
||||
DATA fixPermutation<>+0x18(SB)/4, $6
|
||||
DATA fixPermutation<>+0x1c(SB)/4, $7
|
||||
DATA fixPermutation<>+0x20(SB)/4, $4
|
||||
DATA fixPermutation<>+0x24(SB)/4, $5
|
||||
DATA fixPermutation<>+0x28(SB)/4, $6
|
||||
DATA fixPermutation<>+0x2c(SB)/4, $7
|
||||
DATA fixPermutation<>+0x30(SB)/4, $6
|
||||
DATA fixPermutation<>+0x34(SB)/4, $7
|
||||
DATA fixPermutation<>+0x38(SB)/4, $6
|
||||
DATA fixPermutation<>+0x3c(SB)/4, $7
|
||||
DATA fixPermutation<>+0x40(SB)/4, $2
|
||||
DATA fixPermutation<>+0x44(SB)/4, $3
|
||||
DATA fixPermutation<>+0x48(SB)/4, $6
|
||||
DATA fixPermutation<>+0x4c(SB)/4, $7
|
||||
DATA fixPermutation<>+0x50(SB)/4, $4
|
||||
DATA fixPermutation<>+0x54(SB)/4, $5
|
||||
DATA fixPermutation<>+0x58(SB)/4, $6
|
||||
DATA fixPermutation<>+0x5c(SB)/4, $7
|
||||
DATA fixPermutation<>+0x60(SB)/4, $0
|
||||
DATA fixPermutation<>+0x64(SB)/4, $1
|
||||
DATA fixPermutation<>+0x68(SB)/4, $4
|
||||
DATA fixPermutation<>+0x6c(SB)/4, $5
|
||||
DATA fixPermutation<>+0x70(SB)/4, $2
|
||||
DATA fixPermutation<>+0x74(SB)/4, $3
|
||||
DATA fixPermutation<>+0x78(SB)/4, $6
|
||||
DATA fixPermutation<>+0x7c(SB)/4, $7
|
||||
GLOBL fixPermutation<>(SB), RODATA, $128
|
||||
|
||||
TEXT ·initializeAVX2(SB), $0-16
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ key+8(FP), SI
|
||||
|
||||
MOVQ $addMaskAVX2<>(SB), R8
|
||||
|
||||
MOVOU 16*1(SI), X10
|
||||
MOVOU X10, 288(DI)
|
||||
PXOR X10, X10
|
||||
MOVOU X10, 304(DI)
|
||||
|
||||
MOVD X10, 320(DI)
|
||||
MOVQ 8*0(SI), X5
|
||||
MOVQ 8*1(SI), X10
|
||||
|
||||
VZEROUPPER
|
||||
|
||||
MOVQ $poly1305MaskAVX2<>(SB), R9
|
||||
VPAND (R9), X5, X5
|
||||
VPAND 32(R9), X10, X10
|
||||
|
||||
VMOVDQU 0(R8), X0
|
||||
VPSRLQ $26, X5, X6
|
||||
VPAND X0, X5, X5
|
||||
VPSRLQ $26, X6, X7
|
||||
VPAND X0, X6, X6
|
||||
VPSLLQ $12, X10, X11
|
||||
VPXOR X11, X7, X7
|
||||
VPSRLQ $26, X7, X8
|
||||
VPSRLQ $40, X10, X9
|
||||
VPAND X0, X7, X7
|
||||
VPAND X0, X8, X8
|
||||
|
||||
BYTE $0xc5; BYTE $0xd1; BYTE $0xf4; BYTE $0xc5 // VPMULUDQ X5, X5, X0
|
||||
BYTE $0xc5; BYTE $0xd1; BYTE $0xf4; BYTE $0xce // VPMULUDQ X6, X5, X1
|
||||
BYTE $0xc5; BYTE $0xd1; BYTE $0xf4; BYTE $0xd7 // VPMULUDQ X7, X5, X2
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x51; BYTE $0xf4; BYTE $0xd8 // VPMULUDQ X8, X5, X3
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x51; BYTE $0xf4; BYTE $0xe1 // VPMULUDQ X9, X5, X4
|
||||
|
||||
VPSLLQ $1, X1, X1
|
||||
VPSLLQ $1, X2, X2
|
||||
BYTE $0xc5; BYTE $0x49; BYTE $0xf4; BYTE $0xd6 // VPMULUDQ X6, X6, X10
|
||||
VPADDQ X10, X2, X2
|
||||
BYTE $0xc5; BYTE $0x49; BYTE $0xf4; BYTE $0xd7 // VPMULUDQ X7, X6, X10
|
||||
VPADDQ X10, X3, X3
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x49; BYTE $0xf4; BYTE $0xd0 // VPMULUDQ X8, X6, X10
|
||||
VPADDQ X10, X4, X4
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x49; BYTE $0xf4; BYTE $0xe1 // VPMULUDQ X9, X6, X12
|
||||
VPSLLQ $1, X3, X3
|
||||
VPSLLQ $1, X4, X4
|
||||
BYTE $0xc5; BYTE $0x41; BYTE $0xf4; BYTE $0xd7 // VPMULUDQ X7, X7, X10
|
||||
VPADDQ X10, X4, X4
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x41; BYTE $0xf4; BYTE $0xd0 // VPMULUDQ X8, X7, X10
|
||||
VPADDQ X10, X12, X12
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x41; BYTE $0xf4; BYTE $0xe9 // VPMULUDQ X9, X7, X13
|
||||
VPSLLQ $1, X12, X12
|
||||
VPSLLQ $1, X13, X13
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x39; BYTE $0xf4; BYTE $0xd0 // VPMULUDQ X8, X8, X10
|
||||
VPADDQ X10, X13, X13
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x39; BYTE $0xf4; BYTE $0xf1 // VPMULUDQ X9, X8, X14
|
||||
VPSLLQ $1, X14, X14
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x31; BYTE $0xf4; BYTE $0xf9 // VPMULUDQ X9, X9, X15
|
||||
|
||||
VPSRLQ $26, X4, X10
|
||||
VPAND 0(R8), X4, X4
|
||||
VPADDQ X10, X12, X12
|
||||
|
||||
VPSLLQ $2, X12, X10
|
||||
VPADDQ X10, X12, X12
|
||||
VPSLLQ $2, X13, X10
|
||||
VPADDQ X10, X13, X13
|
||||
VPSLLQ $2, X14, X10
|
||||
VPADDQ X10, X14, X14
|
||||
VPSLLQ $2, X15, X10
|
||||
VPADDQ X10, X15, X15
|
||||
|
||||
VPADDQ X12, X0, X0
|
||||
VPADDQ X13, X1, X1
|
||||
VPADDQ X14, X2, X2
|
||||
VPADDQ X15, X3, X3
|
||||
|
||||
VPSRLQ $26, X0, X10
|
||||
VPAND 0(R8), X0, X0
|
||||
VPADDQ X10, X1, X1
|
||||
VPSRLQ $26, X1, X10
|
||||
VPAND 0(R8), X1, X1
|
||||
VPADDQ X10, X2, X2
|
||||
VPSRLQ $26, X2, X10
|
||||
VPAND 0(R8), X2, X2
|
||||
VPADDQ X10, X3, X3
|
||||
VPSRLQ $26, X3, X10
|
||||
VPAND 0(R8), X3, X3
|
||||
VPADDQ X10, X4, X4
|
||||
|
||||
BYTE $0xc5; BYTE $0xf9; BYTE $0x6c; BYTE $0xed // VPUNPCKLQDQ X5, X0, X5
|
||||
BYTE $0xc5; BYTE $0xf1; BYTE $0x6c; BYTE $0xf6 // VPUNPCKLQDQ X6, X1, X6
|
||||
BYTE $0xc5; BYTE $0xe9; BYTE $0x6c; BYTE $0xff // VPUNPCKLQDQ X7, X2, X7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x61; BYTE $0x6c; BYTE $0xc0 // VPUNPCKLQDQ X8, X3, X8
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x59; BYTE $0x6c; BYTE $0xc9 // VPUNPCKLQDQ X9, X4, X9
|
||||
|
||||
VMOVDQU X5, 0+16(DI)
|
||||
VMOVDQU X6, 32+16(DI)
|
||||
VMOVDQU X7, 64+16(DI)
|
||||
VMOVDQU X8, 96+16(DI)
|
||||
VMOVDQU X9, 128+16(DI)
|
||||
|
||||
VPSLLQ $2, X6, X1
|
||||
VPSLLQ $2, X7, X2
|
||||
VPSLLQ $2, X8, X3
|
||||
VPSLLQ $2, X9, X4
|
||||
|
||||
VPADDQ X1, X6, X1
|
||||
VPADDQ X2, X7, X2
|
||||
VPADDQ X3, X8, X3
|
||||
VPADDQ X4, X9, X4
|
||||
|
||||
VMOVDQU X1, 160+16(DI)
|
||||
VMOVDQU X2, 192+16(DI)
|
||||
VMOVDQU X3, 224+16(DI)
|
||||
VMOVDQU X4, 256+16(DI)
|
||||
|
||||
VPSHUFD $68, X5, X0
|
||||
VPSHUFD $68, X6, X1
|
||||
VPSHUFD $68, X7, X2
|
||||
VPSHUFD $68, X8, X3
|
||||
VPSHUFD $68, X9, X4
|
||||
|
||||
VMOVDQU 0+16(DI), X10
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x79; BYTE $0xf4; BYTE $0xea // VPMULUDQ X10, X0, X5
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x71; BYTE $0xf4; BYTE $0xf2 // VPMULUDQ X10, X1, X6
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x69; BYTE $0xf4; BYTE $0xfa // VPMULUDQ X10, X2, X7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x61; BYTE $0xf4; BYTE $0xc2 // VPMULUDQ X10, X3, X8
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x59; BYTE $0xf4; BYTE $0xca // VPMULUDQ X10, X4, X9
|
||||
|
||||
VMOVDQU 160+16(DI), X10
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x59; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X4, X11
|
||||
VPADDQ X11, X5, X5
|
||||
|
||||
VMOVDQU 32+16(DI), X10
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x79; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X0, X11
|
||||
VPADDQ X11, X6, X6
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x71; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X1, X11
|
||||
VPADDQ X11, X7, X7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x69; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X2, X11
|
||||
VPADDQ X11, X8, X8
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x61; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X3, X11
|
||||
VPADDQ X11, X9, X9
|
||||
|
||||
VMOVDQU 192+16(DI), X10
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x61; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X3, X11
|
||||
VPADDQ X11, X5, X5
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x59; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X4, X11
|
||||
VPADDQ X11, X6, X6
|
||||
|
||||
VMOVDQU 64+16(DI), X10
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x79; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X0, X11
|
||||
VPADDQ X11, X7, X7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x71; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X1, X11
|
||||
VPADDQ X11, X8, X8
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x69; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10 ,X2, X11
|
||||
VPADDQ X11, X9, X9
|
||||
|
||||
VMOVDQU 224+16(DI), X10
|
||||
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x69; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X2, X11
|
||||
VPADDQ X11, X5, X5
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x61; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X3, X11
|
||||
VPADDQ X11, X6, X6
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x59; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X4, X11
|
||||
VPADDQ X11, X7, X7
|
||||
|
||||
VMOVDQU 96+16(DI), X10
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x79; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X0, X11
|
||||
VPADDQ X11, X8, X8
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x71; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X1, X11
|
||||
VPADDQ X11, X9, X9
|
||||
|
||||
VMOVDQU 256+16(DI), X10
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x71; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X1, X11
|
||||
VPADDQ X11, X5, X5
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x69; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X2, X11
|
||||
VPADDQ X11, X6, X6
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x61; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X3, X11
|
||||
VPADDQ X11, X7, X7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x59; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X4, X11
|
||||
VPADDQ X11, X8, X8
|
||||
|
||||
VMOVDQU 128+16(DI), X10
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x79; BYTE $0xf4; BYTE $0xda // VPMULUDQ X10, X0, X11
|
||||
VPADDQ X11, X9, X9
|
||||
|
||||
VMOVDQU 0(R8), X12
|
||||
|
||||
VPSRLQ $26, X8, X10
|
||||
VPADDQ X10, X9, X9
|
||||
VPAND X12, X8, X8
|
||||
VPSRLQ $26, X9, X10
|
||||
VPSLLQ $2, X10, X11
|
||||
VPADDQ X11, X10, X10
|
||||
VPADDQ X10, X5, X5
|
||||
VPAND X12, X9, X9
|
||||
VPSRLQ $26, X5, X10
|
||||
VPAND X12, X5, X5
|
||||
VPADDQ X10, X6, X6
|
||||
VPSRLQ $26, X6, X10
|
||||
VPAND X12, X6, X6
|
||||
VPADDQ X10, X7, X7
|
||||
VPSRLQ $26, X7, X10
|
||||
VPAND X12, X7, X7
|
||||
VPADDQ X10, X8, X8
|
||||
VPSRLQ $26, X8, X10
|
||||
VPAND X12, X8, X8
|
||||
VPADDQ X10, X9, X9
|
||||
|
||||
VMOVDQU X5, 0(DI)
|
||||
VMOVDQU X6, 32(DI)
|
||||
VMOVDQU X7, 64(DI)
|
||||
VMOVDQU X8, 96(DI)
|
||||
VMOVDQU X9, 128(DI)
|
||||
|
||||
VPSLLQ $2, X6, X1
|
||||
VPSLLQ $2, X7, X2
|
||||
VPSLLQ $2, X8, X3
|
||||
VPSLLQ $2, X9, X4
|
||||
|
||||
VPADDQ X1, X6, X1
|
||||
VPADDQ X2, X7, X2
|
||||
VPADDQ X3, X8, X3
|
||||
VPADDQ X4, X9, X4
|
||||
|
||||
VMOVDQU X1, 160(DI)
|
||||
VMOVDQU X2, 192(DI)
|
||||
VMOVDQU X3, 224(DI)
|
||||
VMOVDQU X4, 256(DI)
|
||||
|
||||
RET
|
||||
|
||||
TEXT ·updateAVX2(SB), $0-24
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ msg+8(FP), SI
|
||||
MOVQ msg_len+16(FP), DX
|
||||
|
||||
MOVD 304(DI), X0
|
||||
MOVD 308(DI), X1
|
||||
MOVD 312(DI), X2
|
||||
MOVD 316(DI), X3
|
||||
MOVD 320(DI), X4
|
||||
|
||||
MOVQ $addMaskAVX2<>(SB), R12
|
||||
MOVQ $oneBit<>(SB), R13
|
||||
MOVQ $fixPermutation<>(SB), R15
|
||||
VZEROUPPER
|
||||
|
||||
VMOVDQA (R12), Y12
|
||||
|
||||
CMPQ DX, $128
|
||||
JB BETWEEN_0_AND_128
|
||||
|
||||
AT_LEAST_128:
|
||||
VMOVDQU 32*0(SI), Y9
|
||||
VMOVDQU 32*1(SI), Y10
|
||||
ADDQ $64, SI
|
||||
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x35; BYTE $0x6c; BYTE $0xfa // VPUNPCKLQDQ Y10,Y9,Y7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x35; BYTE $0x6d; BYTE $0xc2 // VPUNPCKHQDQ Y10,Y9,Y8
|
||||
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xff; BYTE $0xd8 // VPERMQ $216,Y7,Y7
|
||||
BYTE $0xc4; BYTE $0x43; BYTE $0xfd; BYTE $0x00; BYTE $0xc0; BYTE $0xd8 // VPERMQ $216,Y8,Y8
|
||||
|
||||
VPSRLQ $26, Y7, Y9
|
||||
VPAND Y12, Y7, Y7
|
||||
VPADDQ Y7, Y0, Y0
|
||||
|
||||
VPSRLQ $26, Y9, Y7
|
||||
VPAND Y12, Y9, Y9
|
||||
VPADDQ Y9, Y1, Y1
|
||||
|
||||
VPSLLQ $12, Y8, Y9
|
||||
VPXOR Y9, Y7, Y7
|
||||
VPAND Y12, Y7, Y7
|
||||
VPADDQ Y7, Y2, Y2
|
||||
|
||||
VPSRLQ $26, Y9, Y7
|
||||
VPSRLQ $40, Y8, Y9
|
||||
VPAND Y12, Y7, Y7
|
||||
VPXOR (R13), Y9, Y9
|
||||
VPADDQ Y7, Y3, Y3
|
||||
VPADDQ Y9, Y4, Y4
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0x2f // VPBROADCASTQ 0(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xfd // VPMULUDQ Y5, Y0, Y7
|
||||
BYTE $0xc5; BYTE $0x75; BYTE $0xf4; BYTE $0xc5 // VPMULUDQ Y5, Y1, Y8
|
||||
BYTE $0xc5; BYTE $0x6d; BYTE $0xf4; BYTE $0xcd // VPMULUDQ Y5, Y2, Y9
|
||||
BYTE $0xc5; BYTE $0x65; BYTE $0xf4; BYTE $0xd5 // VPMULUDQ Y5, Y3, Y10
|
||||
BYTE $0xc5; BYTE $0x5d; BYTE $0xf4; BYTE $0xdd // VPMULUDQ Y5, Y4, Y11
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0xaf; BYTE $0xa0; BYTE $0x00; BYTE $0x00; BYTE $0x00 // VPBROADCASTQ 160(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0x6f; BYTE $0x20 // VPBROADCASTQ 32(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0xaf; BYTE $0xc0; BYTE $0x00; BYTE $0x00; BYTE $0x00 // VPBROADCASTQ 192(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0x6f; BYTE $0x40 // VPBROADCASTQ 64(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0xaf; BYTE $0xe0; BYTE $0x00; BYTE $0x00; BYTE $0x00 // VPBROADCASTQ 224(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0x6f; BYTE $0x60 // VPBROADCASTQ 96(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0xaf; BYTE $0x00; BYTE $0x01; BYTE $0x00; BYTE $0x00 // VPBROADCASTQ 256(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2,Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x7d; BYTE $0x59; BYTE $0xaf; BYTE $0x80; BYTE $0x00; BYTE $0x00; BYTE $0x00 // VPBROADCASTQ 128(DI),Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VPSRLQ $26, Y10, Y5
|
||||
VPADDQ Y5, Y11, Y11
|
||||
VPAND Y12, Y10, Y10
|
||||
|
||||
VPSRLQ $26, Y11, Y5
|
||||
VPSLLQ $2, Y5, Y6
|
||||
VPADDQ Y6, Y5, Y5
|
||||
VPADDQ Y5, Y7, Y7
|
||||
VPAND Y12, Y11, Y11
|
||||
|
||||
VPSRLQ $26, Y7, Y5
|
||||
VPAND Y12, Y7, Y0
|
||||
VPADDQ Y5, Y8, Y8
|
||||
VPSRLQ $26, Y8, Y5
|
||||
VPAND Y12, Y8, Y1
|
||||
VPADDQ Y5, Y9, Y9
|
||||
VPSRLQ $26, Y9, Y5
|
||||
VPAND Y12, Y9, Y2
|
||||
VPADDQ Y5, Y10, Y10
|
||||
VPSRLQ $26, Y10, Y5
|
||||
VPAND Y12, Y10, Y3
|
||||
VPADDQ Y5, Y11, Y4
|
||||
|
||||
SUBQ $64, DX
|
||||
CMPQ DX, $128
|
||||
JAE AT_LEAST_128
|
||||
|
||||
BETWEEN_0_AND_128:
|
||||
CMPQ DX, $64
|
||||
JB BETWEEN_0_AND_64
|
||||
|
||||
VMOVDQU 32*0(SI), Y9
|
||||
VMOVDQU 32*1(SI), Y10
|
||||
ADDQ $64, SI
|
||||
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x35; BYTE $0x6c; BYTE $0xfa // VPUNPCKLQDQ Y10, Y9, Y7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x35; BYTE $0x6d; BYTE $0xc2 // VPUNPCKHQDQ Y10, Y9, Y8
|
||||
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xff; BYTE $0xd8 // VPERMQ $216, Y7, Y7
|
||||
BYTE $0xc4; BYTE $0x43; BYTE $0xfd; BYTE $0x00; BYTE $0xc0; BYTE $0xd8 // VPERMQ $216, Y8, Y8
|
||||
|
||||
VPSRLQ $26, Y7, Y9
|
||||
VPAND Y12, Y7, Y7
|
||||
VPADDQ Y7, Y0, Y0
|
||||
|
||||
VPSRLQ $26, Y9, Y7
|
||||
VPAND Y12, Y9, Y9
|
||||
VPADDQ Y9, Y1, Y1
|
||||
|
||||
VPSLLQ $12, Y8, Y9
|
||||
VPXOR Y9, Y7, Y7
|
||||
VPAND Y12, Y7, Y7
|
||||
VPADDQ Y7, Y2, Y2
|
||||
|
||||
VPSRLQ $26, Y9, Y7
|
||||
VPSRLQ $40, Y8, Y9
|
||||
VPAND Y12, Y7, Y7
|
||||
VPXOR (R13), Y9, Y9
|
||||
VPADDQ Y7, Y3, Y3
|
||||
VPADDQ Y9, Y4, Y4
|
||||
|
||||
VMOVDQU 0(DI), Y5
|
||||
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xfd // VPMULUDQ Y5, Y0, Y7
|
||||
BYTE $0xc5; BYTE $0x75; BYTE $0xf4; BYTE $0xc5 // VPMULUDQ Y5, Y1, Y8
|
||||
BYTE $0xc5; BYTE $0x6d; BYTE $0xf4; BYTE $0xcd // VPMULUDQ Y5, Y2, Y9
|
||||
BYTE $0xc5; BYTE $0x65; BYTE $0xf4; BYTE $0xd5 // VPMULUDQ Y5, Y3, Y10
|
||||
BYTE $0xc5; BYTE $0x5d; BYTE $0xf4; BYTE $0xdd // VPMULUDQ Y5, Y4, Y11
|
||||
|
||||
VMOVDQU 160(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
VMOVDQU 32(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VMOVDQU 192(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
|
||||
VMOVDQU 64(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VMOVDQU 224(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
|
||||
VMOVDQU 96(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VMOVDQU 256(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
|
||||
VMOVDQU 128(DI), Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VPSRLQ $26, Y10, Y5
|
||||
VPADDQ Y5, Y11, Y11
|
||||
VPAND Y12, Y10, Y10
|
||||
VPSRLQ $26, Y11, Y5
|
||||
VPSLLQ $2, Y5, Y6
|
||||
VPADDQ Y6, Y5, Y5
|
||||
VPADDQ Y5, Y7, Y7
|
||||
VPAND Y12, Y11, Y11
|
||||
VPSRLQ $26, Y7, Y5
|
||||
VPAND Y12, Y7, Y0
|
||||
VPADDQ Y5, Y8, Y8
|
||||
VPSRLQ $26, Y8, Y5
|
||||
VPAND Y12, Y8, Y1
|
||||
VPADDQ Y5, Y9, Y9
|
||||
VPSRLQ $26, Y9, Y5
|
||||
VPAND Y12, Y9, Y2
|
||||
VPADDQ Y5, Y10, Y10
|
||||
VPSRLQ $26, Y10, Y5
|
||||
VPAND Y12, Y10, Y3
|
||||
VPADDQ Y5, Y11, Y4
|
||||
|
||||
VPSRLDQ $8, Y0, Y7
|
||||
VPSRLDQ $8, Y1, Y8
|
||||
VPSRLDQ $8, Y2, Y9
|
||||
VPSRLDQ $8, Y3, Y10
|
||||
VPSRLDQ $8, Y4, Y11
|
||||
|
||||
VPADDQ Y7, Y0, Y0
|
||||
VPADDQ Y8, Y1, Y1
|
||||
VPADDQ Y9, Y2, Y2
|
||||
VPADDQ Y10, Y3, Y3
|
||||
VPADDQ Y11, Y4, Y4
|
||||
|
||||
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xf8; BYTE $0xaa // VPERMQ $170, Y0, Y7
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0xfd; BYTE $0x00; BYTE $0xc1; BYTE $0xaa // VPERMQ $170, Y1, Y8
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0xfd; BYTE $0x00; BYTE $0xca; BYTE $0xaa // VPERMQ $170, Y2, Y9
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0xfd; BYTE $0x00; BYTE $0xd3; BYTE $0xaa // VPERMQ $170, Y3, Y10
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0xfd; BYTE $0x00; BYTE $0xdc; BYTE $0xaa // VPERMQ $170, Y4, Y11
|
||||
|
||||
VPADDQ Y7, Y0, Y0
|
||||
VPADDQ Y8, Y1, Y1
|
||||
VPADDQ Y9, Y2, Y2
|
||||
VPADDQ Y10, Y3, Y3
|
||||
VPADDQ Y11, Y4, Y4
|
||||
SUBQ $64, DX
|
||||
|
||||
BETWEEN_0_AND_64:
|
||||
TESTQ DX, DX
|
||||
JZ DONE
|
||||
|
||||
BYTE $0xc5; BYTE $0xfa; BYTE $0x7e; BYTE $0xc0 // VMOVQ X0, X0
|
||||
BYTE $0xc5; BYTE $0xfa; BYTE $0x7e; BYTE $0xc9 // VMOVQ X1, X1
|
||||
BYTE $0xc5; BYTE $0xfa; BYTE $0x7e; BYTE $0xd2 // VMOVQ X2, X2
|
||||
BYTE $0xc5; BYTE $0xfa; BYTE $0x7e; BYTE $0xdb // VMOVQ X3, X3
|
||||
BYTE $0xc5; BYTE $0xfa; BYTE $0x7e; BYTE $0xe4 // VMOVQ X4, X4
|
||||
|
||||
MOVQ (R13), BX
|
||||
MOVQ SP, AX
|
||||
TESTQ $15, DX
|
||||
JZ FULL_BLOCKS
|
||||
|
||||
SUBQ $64, SP
|
||||
VPXOR Y7, Y7, Y7
|
||||
VMOVDQU Y7, (SP)
|
||||
VMOVDQU Y7, 32(SP)
|
||||
|
||||
XORQ BX, BX
|
||||
|
||||
FLUSH_BUFFER:
|
||||
MOVB (SI)(BX*1), CX
|
||||
MOVB CX, (SP)(BX*1)
|
||||
INCQ BX
|
||||
CMPQ DX, BX
|
||||
JNE FLUSH_BUFFER
|
||||
|
||||
MOVB $1, (SP)(BX*1)
|
||||
XORQ BX, BX
|
||||
MOVQ SP, SI
|
||||
|
||||
FULL_BLOCKS:
|
||||
CMPQ DX, $16
|
||||
JA AT_LEAST_16
|
||||
|
||||
BYTE $0xc5; BYTE $0xfa; BYTE $0x7e; BYTE $0x3e // VMOVQ 8*0(SI), X7
|
||||
BYTE $0xc5; BYTE $0x7a; BYTE $0x7e; BYTE $0x46; BYTE $0x08 // VMOVQ 8*1(SI), X8
|
||||
BYTE $0xc4; BYTE $0x61; BYTE $0xf9; BYTE $0x6e; BYTE $0xf3 // VMOVQ BX ,X14
|
||||
VMOVDQA (R15), Y13
|
||||
JMP MULTIPLY
|
||||
|
||||
AT_LEAST_16:
|
||||
CMPQ DX, $32
|
||||
JA AT_LEAST_32
|
||||
VMOVDQU 16*0(SI), X9
|
||||
VMOVDQU 16*1(SI), X10
|
||||
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x7a; BYTE $0x7e; BYTE $0x75; BYTE $0x00 // VMOVQ (R13), X14
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0xf3; BYTE $0x01 // VPINSRQ $1,BX, X14, X14
|
||||
VMOVDQA 32(R15), Y13
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x35; BYTE $0x6c; BYTE $0xfa // VPUNPCKLQDQ Y10, Y9, Y7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x35; BYTE $0x6d; BYTE $0xc2 // VPUNPCKHQDQ Y10, Y9, Y8
|
||||
JMP MULTIPLY
|
||||
|
||||
AT_LEAST_32:
|
||||
CMPQ DX, $48
|
||||
JA AT_LEAST_48
|
||||
VMOVDQU 32*0(SI), Y9
|
||||
VMOVDQU 32*1(SI), X10
|
||||
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x7a; BYTE $0x7e; BYTE $0x75; BYTE $0x00 // VMOVQ 0(R13), X14
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0xf3; BYTE $0x01 // VPINSRQ $1, BX, X14, X14
|
||||
BYTE $0xc4; BYTE $0x43; BYTE $0xfd; BYTE $0x00; BYTE $0xf6; BYTE $0xc4 // VPERMQ $196, Y14, Y14
|
||||
VMOVDQA 64(R15), Y13
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x35; BYTE $0x6c; BYTE $0xfa // VPUNPCKLQDQ Y10, Y9, Y7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x35; BYTE $0x6d; BYTE $0xc2 // VPUNPCKHQDQ Y10, Y9, Y8
|
||||
JMP MULTIPLY
|
||||
|
||||
AT_LEAST_48:
|
||||
VMOVDQU 32*0(SI), Y9
|
||||
VMOVDQU 32*1(SI), Y10
|
||||
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x7a; BYTE $0x7e; BYTE $0x75; BYTE $0x00 // VMOVQ (R13),X14
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0xf3; BYTE $0x01 // VPINSRQ $1,BX,X14,X14
|
||||
BYTE $0xc4; BYTE $0x43; BYTE $0xfd; BYTE $0x00; BYTE $0xf6; BYTE $0x40 // VPERMQ $64,Y14,Y14
|
||||
VMOVDQA 96(R15), Y13
|
||||
BYTE $0xc4; BYTE $0xc1; BYTE $0x35; BYTE $0x6c; BYTE $0xfa // VPUNPCKLQDQ Y10, Y9, Y7
|
||||
BYTE $0xc4; BYTE $0x41; BYTE $0x35; BYTE $0x6d; BYTE $0xc2 // VPUNPCKHQDQ Y10, Y9, Y8
|
||||
|
||||
MULTIPLY:
|
||||
MOVQ AX, SP
|
||||
|
||||
VPSRLQ $26, Y7, Y9
|
||||
VPAND Y12, Y7, Y7
|
||||
VPADDQ Y7, Y0, Y0
|
||||
|
||||
VPSRLQ $26, Y9, Y7
|
||||
VPAND Y12, Y9, Y9
|
||||
VPADDQ Y9, Y1, Y1
|
||||
|
||||
VPSLLQ $12, Y8, Y9
|
||||
VPXOR Y9, Y7, Y7
|
||||
VPAND Y12, Y7, Y7
|
||||
VPADDQ Y7, Y2, Y2
|
||||
|
||||
VPSRLQ $26, Y9, Y7
|
||||
VPSRLQ $40, Y8, Y9
|
||||
VPAND Y12, Y7, Y7
|
||||
VPXOR Y14, Y9, Y9
|
||||
VPADDQ Y7, Y3, Y3
|
||||
VPADDQ Y9, Y4, Y4
|
||||
|
||||
VMOVDQU 0(DI), Y5
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xfd // VPMULUDQ Y5, Y0, Y7
|
||||
BYTE $0xc5; BYTE $0x75; BYTE $0xf4; BYTE $0xc5 // VPMULUDQ Y5, Y1, Y8
|
||||
BYTE $0xc5; BYTE $0x6d; BYTE $0xf4; BYTE $0xcd // VPMULUDQ Y5, Y2, Y9
|
||||
BYTE $0xc5; BYTE $0x65; BYTE $0xf4; BYTE $0xd5 // VPMULUDQ Y5, Y3, Y10
|
||||
BYTE $0xc5; BYTE $0x5d; BYTE $0xf4; BYTE $0xdd // VPMULUDQ Y5, Y4, Y11
|
||||
|
||||
VMOVDQU 160(DI), Y5
|
||||
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
VMOVDQU 32(DI), Y5
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VMOVDQU 192(DI), Y5
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
|
||||
VMOVDQU 64(DI), Y5
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VMOVDQU 224(DI), Y5
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
|
||||
VMOVDQU 96(DI), Y5
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VMOVDQU 256(DI), Y5
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xf5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y1,Y6
|
||||
VPADDQ Y6, Y7, Y7
|
||||
BYTE $0xc5; BYTE $0xed; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y2, Y6
|
||||
VPADDQ Y6, Y8, Y8
|
||||
BYTE $0xc5; BYTE $0xe5; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y3, Y6
|
||||
VPADDQ Y6, Y9, Y9
|
||||
BYTE $0xc5; BYTE $0xdd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y4, Y6
|
||||
VPADDQ Y6, Y10, Y10
|
||||
|
||||
VMOVDQU 128(DI), Y5
|
||||
BYTE $0xc4; BYTE $0xe2; BYTE $0x15; BYTE $0x36; BYTE $0xed // VPERMD Y5, Y13, Y5
|
||||
BYTE $0xc5; BYTE $0xfd; BYTE $0xf4; BYTE $0xf5 // VPMULUDQ Y5, Y0, Y6
|
||||
VPADDQ Y6, Y11, Y11
|
||||
|
||||
VPSRLQ $26, Y10, Y5
|
||||
VPADDQ Y5, Y11, Y11
|
||||
VPAND Y12, Y10, Y10
|
||||
VPSRLQ $26, Y11, Y5
|
||||
VPSLLQ $2, Y5, Y6
|
||||
VPADDQ Y6, Y5, Y5
|
||||
VPADDQ Y5, Y7, Y7
|
||||
VPAND Y12, Y11, Y11
|
||||
VPSRLQ $26, Y7, Y5
|
||||
VPAND Y12, Y7, Y0
|
||||
VPADDQ Y5, Y8, Y8
|
||||
VPSRLQ $26, Y8, Y5
|
||||
VPAND Y12, Y8, Y1
|
||||
VPADDQ Y5, Y9, Y9
|
||||
VPSRLQ $26, Y9, Y5
|
||||
VPAND Y12, Y9, Y2
|
||||
VPADDQ Y5, Y10, Y10
|
||||
VPSRLQ $26, Y10, Y5
|
||||
VPAND Y12, Y10, Y3
|
||||
VPADDQ Y5, Y11, Y4
|
||||
|
||||
VPSRLDQ $8, Y0, Y7
|
||||
VPSRLDQ $8, Y1, Y8
|
||||
VPSRLDQ $8, Y2, Y9
|
||||
VPSRLDQ $8, Y3, Y10
|
||||
VPSRLDQ $8, Y4, Y11
|
||||
|
||||
VPADDQ Y7, Y0, Y0
|
||||
VPADDQ Y8, Y1, Y1
|
||||
VPADDQ Y9, Y2, Y2
|
||||
VPADDQ Y10, Y3, Y3
|
||||
VPADDQ Y11, Y4, Y4
|
||||
|
||||
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xf8; BYTE $0xaa // VPERMQ $170, Y0, Y7
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0xfd; BYTE $0x00; BYTE $0xc1; BYTE $0xaa // VPERMQ $170, Y1, Y8
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0xfd; BYTE $0x00; BYTE $0xca; BYTE $0xaa // VPERMQ $170, Y2, Y9
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0xfd; BYTE $0x00; BYTE $0xd3; BYTE $0xaa // VPERMQ $170, Y3, Y10
|
||||
BYTE $0xc4; BYTE $0x63; BYTE $0xfd; BYTE $0x00; BYTE $0xdc; BYTE $0xaa // VPERMQ $170, Y4, Y11
|
||||
|
||||
VPADDQ Y7, Y0, Y0
|
||||
VPADDQ Y8, Y1, Y1
|
||||
VPADDQ Y9, Y2, Y2
|
||||
VPADDQ Y10, Y3, Y3
|
||||
VPADDQ Y11, Y4, Y4
|
||||
|
||||
DONE:
|
||||
VZEROUPPER
|
||||
MOVD X0, 304(DI)
|
||||
MOVD X1, 308(DI)
|
||||
MOVD X2, 312(DI)
|
||||
MOVD X3, 316(DI)
|
||||
MOVD X4, 320(DI)
|
||||
RET
|
||||
|
||||
TEXT ·finalizeAVX2(SB), $0-16
|
||||
MOVQ out+0(FP), SI
|
||||
MOVQ state+8(FP), DI
|
||||
|
||||
VZEROUPPER
|
||||
|
||||
BYTE $0xc5; BYTE $0xf9; BYTE $0x6e; BYTE $0x87; BYTE $0x30; BYTE $0x01; BYTE $0x00; BYTE $0x00 // VMOVD 304(DI), X0
|
||||
BYTE $0xc5; BYTE $0xf9; BYTE $0x6e; BYTE $0x8f; BYTE $0x34; BYTE $0x01; BYTE $0x00; BYTE $0x00 // VMOVD 308(DI), X1
|
||||
BYTE $0xc5; BYTE $0xf9; BYTE $0x6e; BYTE $0x97; BYTE $0x38; BYTE $0x01; BYTE $0x00; BYTE $0x00 // VMOVD 312(DI), X2
|
||||
BYTE $0xc5; BYTE $0xf9; BYTE $0x6e; BYTE $0x9f; BYTE $0x3c; BYTE $0x01; BYTE $0x00; BYTE $0x00 // VMOVD 316(DI), X3
|
||||
BYTE $0xc5; BYTE $0xf9; BYTE $0x6e; BYTE $0xa7; BYTE $0x40; BYTE $0x01; BYTE $0x00; BYTE $0x00 // VMOVD 320(DI), X4
|
||||
|
||||
VMOVDQU addMaskAVX2<>(SB), X7
|
||||
|
||||
VPSRLQ $26, X4, X5
|
||||
VPSLLQ $2, X5, X6
|
||||
VPADDQ X6, X5, X5
|
||||
VPADDQ X5, X0, X0
|
||||
VPAND X7, X4, X4
|
||||
|
||||
VPSRLQ $26, X0, X5
|
||||
VPAND X7, X0, X0
|
||||
VPADDQ X5, X1, X1
|
||||
VPSRLQ $26, X1, X5
|
||||
VPAND X7, X1, X1
|
||||
VPADDQ X5, X2, X2
|
||||
VPSRLQ $26, X2, X5
|
||||
VPAND X7, X2, X2
|
||||
VPADDQ X5, X3, X3
|
||||
VPSRLQ $26, X3, X5
|
||||
VPAND X7, X3, X3
|
||||
VPADDQ X5, X4, X4
|
||||
|
||||
VPSLLQ $26, X1, X5
|
||||
VPXOR X5, X0, X0
|
||||
VPSLLQ $52, X2, X5
|
||||
VPXOR X5, X0, X0
|
||||
VPSRLQ $12, X2, X1
|
||||
VPSLLQ $14, X3, X5
|
||||
VPXOR X5, X1, X1
|
||||
VPSLLQ $40, X4, X5
|
||||
VPXOR X5, X1, X1
|
||||
|
||||
VZEROUPPER
|
||||
|
||||
MOVQ X0, AX
|
||||
MOVQ X1, BX
|
||||
|
||||
ADDQ 288(DI), AX
|
||||
ADCQ 288+8(DI), BX
|
||||
MOVQ AX, (SI)
|
||||
MOVQ BX, 8(SI)
|
||||
|
||||
RET
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build amd64, !gccgo, !appengine
|
||||
|
||||
package poly1305
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/cpu"
|
||||
"io"
|
||||
)
|
||||
|
||||
var useAVX2 = cpu.X86.HasAVX2
|
||||
|
||||
//go:noescape
|
||||
func initialize(state *[7]uint64, key *[32]byte)
|
||||
|
||||
//go:noescape
|
||||
func initializeAVX2(state *[512]byte, key *[32]byte)
|
||||
|
||||
//go:noescape
|
||||
func update(state *[7]uint64, msg []byte)
|
||||
|
||||
//go:noescape
|
||||
func updateAVX2(state *[512]byte, msg []byte)
|
||||
|
||||
//go:noescape
|
||||
func finalize(tag *[TagSize]byte, state *[7]uint64)
|
||||
|
||||
//go:noescape
|
||||
func finalizeAVX2(tag *[TagSize]byte, state *[512]byte)
|
||||
|
||||
// compiler asserts - check that poly1305Hash and poly1305HashAVX2 implements the hash interface
|
||||
var (
|
||||
_ (hash) = &poly1305Hash{}
|
||||
_ (hash) = &poly1305HashAVX2{}
|
||||
)
|
||||
|
||||
type hash interface {
|
||||
io.Writer
|
||||
|
||||
Sum(b []byte) []byte
|
||||
}
|
||||
|
||||
// Sum generates an authenticator for msg using a one-time key and returns the
|
||||
// 16-byte result. Authenticating two different messages with the same key allows
|
||||
// an attacker to forge messages at will.
|
||||
func Sum(msg []byte, key [32]byte) [TagSize]byte {
|
||||
if len(msg) == 0 {
|
||||
msg = []byte{}
|
||||
}
|
||||
var out [TagSize]byte
|
||||
if useAVX2 && len(msg) > 8*TagSize {
|
||||
var state [512]byte
|
||||
initializeAVX2(&state, &key)
|
||||
updateAVX2(&state, msg)
|
||||
finalizeAVX2(&out, &state)
|
||||
} else {
|
||||
var state [7]uint64 // := uint64{ h0, h1, h2, r0, r1, pad0, pad1 }
|
||||
initialize(&state, &key)
|
||||
update(&state, msg)
|
||||
finalize(&out, &state)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// New returns a Hash computing the poly1305 sum.
|
||||
// Notice that Poly1305 is insecure if one key is used twice.
|
||||
func New(key [32]byte) *Hash {
|
||||
if useAVX2 {
|
||||
h := new(poly1305HashAVX2)
|
||||
initializeAVX2(&(h.state), &key)
|
||||
return &Hash{h, false}
|
||||
}
|
||||
h := new(poly1305Hash)
|
||||
initialize(&(h.state), &key)
|
||||
return &Hash{h, false}
|
||||
}
|
||||
|
||||
// Hash implements the poly1305 authenticator.
|
||||
// Poly1305 cannot be used like common hash.Hash implementations,
|
||||
// because using a poly1305 key twice breaks its security.
|
||||
type Hash struct {
|
||||
hash
|
||||
|
||||
done bool
|
||||
}
|
||||
|
||||
// Size returns the number of bytes Sum will append.
|
||||
func (h *Hash) Size() int { return TagSize }
|
||||
|
||||
// Write adds more data to the running Poly1305 hash.
|
||||
// This function should return a non-nil error if a call
|
||||
// to Write happens after a call to Sum. So it is not possible
|
||||
// to compute the checksum and than add more data.
|
||||
func (h *Hash) Write(msg []byte) (int, error) {
|
||||
if h.done {
|
||||
return 0, errWriteAfterSum
|
||||
}
|
||||
return h.hash.Write(msg)
|
||||
}
|
||||
|
||||
// Sum appends the Poly1305 hash of the previously
|
||||
// processed data to b and returns the resulting slice.
|
||||
// It is safe to call this function multiple times.
|
||||
func (h *Hash) Sum(b []byte) []byte {
|
||||
b = h.hash.Sum(b)
|
||||
h.done = true
|
||||
return b
|
||||
}
|
||||
|
||||
type poly1305Hash struct {
|
||||
state [7]uint64 // := uint64{ h0, h1, h2, r0, r1, pad0, pad1 }
|
||||
|
||||
buf [TagSize]byte
|
||||
off int
|
||||
}
|
||||
|
||||
func (h *poly1305Hash) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
if h.off > 0 {
|
||||
dif := TagSize - h.off
|
||||
if n <= dif {
|
||||
h.off += copy(h.buf[h.off:], p)
|
||||
return n, nil
|
||||
}
|
||||
copy(h.buf[h.off:], p[:dif])
|
||||
update(&(h.state), h.buf[:])
|
||||
p = p[dif:]
|
||||
h.off = 0
|
||||
}
|
||||
// process full 16-byte blocks
|
||||
if nn := len(p) & (^(TagSize - 1)); nn > 0 {
|
||||
update(&(h.state), p[:nn])
|
||||
p = p[nn:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.off += copy(h.buf[h.off:], p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *poly1305Hash) Sum(b []byte) []byte {
|
||||
var out [TagSize]byte
|
||||
state := h.state
|
||||
if h.off > 0 {
|
||||
update(&state, h.buf[:h.off])
|
||||
}
|
||||
finalize(&out, &state)
|
||||
return append(b, out[:]...)
|
||||
}
|
||||
|
||||
type poly1305HashAVX2 struct {
|
||||
// r[0] | r^2[0] | r[1] | r^2[1] | r[2] | r^2[2] | r[3] | r^2[3] | r[4] | r^2[4] | r[1]*5 | r^2[1]*5 | r[2]*5 | r^2[2]*5 r[3]*5 | r^2[3]*5 r[4]*5 | r^2[4]*5
|
||||
state [512]byte
|
||||
|
||||
buffer [8 * TagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (h *poly1305HashAVX2) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
if h.offset > 0 {
|
||||
remaining := 8*TagSize - h.offset
|
||||
if n <= remaining {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
return n, nil
|
||||
}
|
||||
copy(h.buffer[h.offset:], p[:remaining])
|
||||
updateAVX2(&h.state, h.buffer[:])
|
||||
p = p[remaining:]
|
||||
h.offset = 0
|
||||
}
|
||||
// process full 8*16-byte blocks
|
||||
if nn := len(p) & (^(8*TagSize - 1)); nn > 0 {
|
||||
updateAVX2(&h.state, p[:nn])
|
||||
p = p[nn:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.offset += copy(h.buffer[:], p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *poly1305HashAVX2) Sum(b []byte) []byte {
|
||||
var out [TagSize]byte
|
||||
state := h.state
|
||||
|
||||
if h.offset > 0 {
|
||||
updateAVX2(&state, h.buffer[:h.offset])
|
||||
}
|
||||
finalizeAVX2(&out, &state)
|
||||
return append(b, out[:]...)
|
||||
}
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build amd64, !gccgo, !appengine
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
|
||||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
|
||||
GLOBL ·poly1305Mask<>(SB), RODATA, $16
|
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2) \
|
||||
ADDQ 0(msg), h0; \
|
||||
ADCQ 8(msg), h1; \
|
||||
ADCQ $1, h2; \
|
||||
LEAQ 16(msg), msg
|
||||
|
||||
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
|
||||
MOVQ r0, AX; \
|
||||
MULQ h0; \
|
||||
MOVQ AX, t0; \
|
||||
MOVQ DX, t1; \
|
||||
MOVQ r0, AX; \
|
||||
MULQ h1; \
|
||||
ADDQ AX, t1; \
|
||||
ADCQ $0, DX; \
|
||||
MOVQ r0, t2; \
|
||||
IMULQ h2, t2; \
|
||||
ADDQ DX, t2; \
|
||||
\
|
||||
MOVQ r1, AX; \
|
||||
MULQ h0; \
|
||||
ADDQ AX, t1; \
|
||||
ADCQ $0, DX; \
|
||||
MOVQ DX, h0; \
|
||||
MOVQ r1, t3; \
|
||||
IMULQ h2, t3; \
|
||||
MOVQ r1, AX; \
|
||||
MULQ h1; \
|
||||
ADDQ AX, t2; \
|
||||
ADCQ DX, t3; \
|
||||
ADDQ h0, t2; \
|
||||
ADCQ $0, t3; \
|
||||
\
|
||||
MOVQ t0, h0; \
|
||||
MOVQ t1, h1; \
|
||||
MOVQ t2, h2; \
|
||||
ANDQ $3, h2; \
|
||||
MOVQ t2, t0; \
|
||||
ANDQ $0XFFFFFFFFFFFFFFFC, t0; \
|
||||
ADDQ t0, h0; \
|
||||
ADCQ t3, h1; \
|
||||
ADCQ $0, h2; \
|
||||
SHRQ $2, t3, t2; \
|
||||
SHRQ $2, t3; \
|
||||
ADDQ t2, h0; \
|
||||
ADCQ t3, h1; \
|
||||
ADCQ $0, h2
|
||||
|
||||
// func update(state *[7]uint64, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ msg_base+8(FP), SI
|
||||
MOVQ msg_len+16(FP), R15
|
||||
|
||||
MOVQ 0(DI), R8 // h0
|
||||
MOVQ 8(DI), R9 // h1
|
||||
MOVQ 16(DI), R10 // h2
|
||||
MOVQ 24(DI), R11 // r0
|
||||
MOVQ 32(DI), R12 // h1
|
||||
|
||||
CMPQ R15, $16
|
||||
JB BYTES_BETWEEN_0_AND_15
|
||||
|
||||
LOOP:
|
||||
POLY1305_ADD(SI, R8, R9, R10)
|
||||
|
||||
MULTIPLY:
|
||||
POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14)
|
||||
SUBQ $16, R15
|
||||
CMPQ R15, $16
|
||||
JAE LOOP
|
||||
|
||||
BYTES_BETWEEN_0_AND_15:
|
||||
TESTQ R15, R15
|
||||
JZ DONE
|
||||
MOVQ $1, BX
|
||||
XORQ CX, CX
|
||||
XORQ R13, R13
|
||||
ADDQ R15, SI
|
||||
|
||||
FLUSH_BUFFER:
|
||||
SHLQ $8, BX, CX
|
||||
SHLQ $8, BX
|
||||
MOVB -1(SI), R13
|
||||
XORQ R13, BX
|
||||
DECQ SI
|
||||
DECQ R15
|
||||
JNZ FLUSH_BUFFER
|
||||
|
||||
ADDQ BX, R8
|
||||
ADCQ CX, R9
|
||||
ADCQ $0, R10
|
||||
MOVQ $16, R15
|
||||
JMP MULTIPLY
|
||||
|
||||
DONE:
|
||||
MOVQ R8, 0(DI)
|
||||
MOVQ R9, 8(DI)
|
||||
MOVQ R10, 16(DI)
|
||||
RET
|
||||
|
||||
// func initialize(state *[7]uint64, key *[32]byte)
|
||||
TEXT ·initialize(SB), $0-16
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ key+8(FP), SI
|
||||
|
||||
// state[0...7] is initialized with zero
|
||||
MOVOU 0(SI), X0
|
||||
MOVOU 16(SI), X1
|
||||
MOVOU ·poly1305Mask<>(SB), X2
|
||||
PAND X2, X0
|
||||
MOVOU X0, 24(DI)
|
||||
MOVOU X1, 40(DI)
|
||||
RET
|
||||
|
||||
// func finalize(tag *[TagSize]byte, state *[7]uint64)
|
||||
TEXT ·finalize(SB), $0-16
|
||||
MOVQ tag+0(FP), DI
|
||||
MOVQ state+8(FP), SI
|
||||
|
||||
MOVQ 0(SI), AX
|
||||
MOVQ 8(SI), BX
|
||||
MOVQ 16(SI), CX
|
||||
MOVQ AX, R8
|
||||
MOVQ BX, R9
|
||||
SUBQ $0XFFFFFFFFFFFFFFFB, AX
|
||||
SBBQ $0XFFFFFFFFFFFFFFFF, BX
|
||||
SBBQ $3, CX
|
||||
CMOVQCS R8, AX
|
||||
CMOVQCS R9, BX
|
||||
ADDQ 40(SI), AX
|
||||
ADCQ 48(SI), BX
|
||||
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ BX, 8(DI)
|
||||
RET
|
||||
|
||||
|
||||
// func supportsAVX2() bool
|
||||
TEXT ·supportsAVX2(SB), 4, $0-1
|
||||
MOVQ runtime·support_avx2(SB), AX
|
||||
MOVB AX, ret+0(FP)
|
||||
RET
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build !amd64 gccgo appengine nacl
|
||||
|
||||
package poly1305
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
msgBlock = uint32(1 << 24)
|
||||
finalBlock = uint32(0)
|
||||
)
|
||||
|
||||
// Sum generates an authenticator for msg using a one-time key and returns the
|
||||
// 16-byte result. Authenticating two different messages with the same key allows
|
||||
// an attacker to forge messages at will.
|
||||
func Sum(msg []byte, key [32]byte) [TagSize]byte {
|
||||
var (
|
||||
h, r [5]uint32
|
||||
s [4]uint32
|
||||
)
|
||||
var out [TagSize]byte
|
||||
|
||||
initialize(&r, &s, &key)
|
||||
|
||||
// process full 16-byte blocks
|
||||
n := len(msg) & (^(TagSize - 1))
|
||||
if n > 0 {
|
||||
update(msg[:n], msgBlock, &h, &r)
|
||||
msg = msg[n:]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
var block [TagSize]byte
|
||||
off := copy(block[:], msg)
|
||||
block[off] = 1
|
||||
update(block[:], finalBlock, &h, &r)
|
||||
}
|
||||
finalize(&out, &h, &s)
|
||||
return out
|
||||
}
|
||||
|
||||
// New returns a hash.Hash computing the poly1305 sum.
|
||||
// Notice that Poly1305 is insecure if one key is used twice.
|
||||
func New(key [32]byte) *Hash {
|
||||
p := new(Hash)
|
||||
initialize(&(p.r), &(p.s), &key)
|
||||
return p
|
||||
}
|
||||
|
||||
// Hash implements a Poly1305 writer interface.
|
||||
// Poly1305 cannot be used like common hash.Hash implementations,
|
||||
// because using a poly1305 key twice breaks its security.
|
||||
// So poly1305.Hash does not support some kind of reset.
|
||||
type Hash struct {
|
||||
h, r [5]uint32
|
||||
s [4]uint32
|
||||
|
||||
buf [TagSize]byte
|
||||
off int
|
||||
done bool
|
||||
}
|
||||
|
||||
// Size returns the number of bytes Sum will append.
|
||||
func (p *Hash) Size() int { return TagSize }
|
||||
|
||||
// Write adds more data to the running Poly1305 hash.
|
||||
// This function should return a non-nil error if a call
|
||||
// to Write happens after a call to Sum. So it is not possible
|
||||
// to compute the checksum and than add more data.
|
||||
func (p *Hash) Write(msg []byte) (int, error) {
|
||||
if p.done {
|
||||
return 0, errWriteAfterSum
|
||||
}
|
||||
n := len(msg)
|
||||
|
||||
if p.off > 0 {
|
||||
dif := TagSize - p.off
|
||||
if n <= dif {
|
||||
p.off += copy(p.buf[p.off:], msg)
|
||||
return n, nil
|
||||
}
|
||||
copy(p.buf[p.off:], msg[:dif])
|
||||
msg = msg[dif:]
|
||||
update(p.buf[:], msgBlock, &(p.h), &(p.r))
|
||||
p.off = 0
|
||||
}
|
||||
|
||||
// process full 16-byte blocks
|
||||
if nn := len(msg) & (^(TagSize - 1)); nn > 0 {
|
||||
update(msg[:nn], msgBlock, &(p.h), &(p.r))
|
||||
msg = msg[nn:]
|
||||
}
|
||||
|
||||
if len(msg) > 0 {
|
||||
p.off += copy(p.buf[p.off:], msg)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Sum appends the Pol1305 hash of the previously
|
||||
// processed data to b and returns the resulting slice.
|
||||
// It is safe to call this function multiple times.
|
||||
func (p *Hash) Sum(b []byte) []byte {
|
||||
var out [TagSize]byte
|
||||
h := p.h
|
||||
|
||||
if p.off > 0 {
|
||||
var buf [TagSize]byte
|
||||
copy(buf[:], p.buf[:p.off])
|
||||
buf[p.off] = 1 // invariant: p.off < TagSize
|
||||
|
||||
update(buf[:], finalBlock, &h, &(p.r))
|
||||
}
|
||||
|
||||
finalize(&out, &h, &(p.s))
|
||||
p.done = true
|
||||
return append(b, out[:]...)
|
||||
}
|
||||
|
||||
func initialize(r *[5]uint32, s *[4]uint32, key *[32]byte) {
|
||||
r[0] = binary.LittleEndian.Uint32(key[0:]) & 0x3ffffff
|
||||
r[1] = (binary.LittleEndian.Uint32(key[3:]) >> 2) & 0x3ffff03
|
||||
r[2] = (binary.LittleEndian.Uint32(key[6:]) >> 4) & 0x3ffc0ff
|
||||
r[3] = (binary.LittleEndian.Uint32(key[9:]) >> 6) & 0x3f03fff
|
||||
r[4] = (binary.LittleEndian.Uint32(key[12:]) >> 8) & 0x00fffff
|
||||
|
||||
s[0] = binary.LittleEndian.Uint32(key[16:])
|
||||
s[1] = binary.LittleEndian.Uint32(key[20:])
|
||||
s[2] = binary.LittleEndian.Uint32(key[24:])
|
||||
s[3] = binary.LittleEndian.Uint32(key[28:])
|
||||
}
|
||||
|
||||
func update(msg []byte, flag uint32, h, r *[5]uint32) {
|
||||
h0, h1, h2, h3, h4 := h[0], h[1], h[2], h[3], h[4]
|
||||
r0, r1, r2, r3, r4 := uint64(r[0]), uint64(r[1]), uint64(r[2]), uint64(r[3]), uint64(r[4])
|
||||
R1, R2, R3, R4 := r1*5, r2*5, r3*5, r4*5
|
||||
|
||||
for len(msg) >= TagSize {
|
||||
// h += msg
|
||||
h0 += binary.LittleEndian.Uint32(msg[0:]) & 0x3ffffff
|
||||
h1 += (binary.LittleEndian.Uint32(msg[3:]) >> 2) & 0x3ffffff
|
||||
h2 += (binary.LittleEndian.Uint32(msg[6:]) >> 4) & 0x3ffffff
|
||||
h3 += (binary.LittleEndian.Uint32(msg[9:]) >> 6) & 0x3ffffff
|
||||
h4 += (binary.LittleEndian.Uint32(msg[12:]) >> 8) | flag
|
||||
|
||||
// h *= r
|
||||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1)
|
||||
d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2)
|
||||
d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3)
|
||||
d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4)
|
||||
d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0)
|
||||
|
||||
// h %= p
|
||||
h0 = uint32(d0) & 0x3ffffff
|
||||
h1 = uint32(d1) & 0x3ffffff
|
||||
h2 = uint32(d2) & 0x3ffffff
|
||||
h3 = uint32(d3) & 0x3ffffff
|
||||
h4 = uint32(d4) & 0x3ffffff
|
||||
|
||||
h0 += uint32(d4>>26) * 5
|
||||
h1 += h0 >> 26
|
||||
h0 = h0 & 0x3ffffff
|
||||
|
||||
msg = msg[TagSize:]
|
||||
}
|
||||
|
||||
h[0], h[1], h[2], h[3], h[4] = h0, h1, h2, h3, h4
|
||||
}
|
||||
|
||||
func finalize(out *[TagSize]byte, h *[5]uint32, s *[4]uint32) {
|
||||
h0, h1, h2, h3, h4 := h[0], h[1], h[2], h[3], h[4]
|
||||
|
||||
// h %= p reduction
|
||||
h2 += h1 >> 26
|
||||
h1 &= 0x3ffffff
|
||||
h3 += h2 >> 26
|
||||
h2 &= 0x3ffffff
|
||||
h4 += h3 >> 26
|
||||
h3 &= 0x3ffffff
|
||||
h0 += 5 * (h4 >> 26)
|
||||
h4 &= 0x3ffffff
|
||||
h1 += h0 >> 26
|
||||
h0 &= 0x3ffffff
|
||||
|
||||
// h - p
|
||||
t0 := h0 + 5
|
||||
t1 := h1 + (t0 >> 26)
|
||||
t2 := h2 + (t1 >> 26)
|
||||
t3 := h3 + (t2 >> 26)
|
||||
t4 := h4 + (t3 >> 26) - (1 << 26)
|
||||
t0 &= 0x3ffffff
|
||||
t1 &= 0x3ffffff
|
||||
t2 &= 0x3ffffff
|
||||
t3 &= 0x3ffffff
|
||||
|
||||
// select h if h < p else h - p
|
||||
t_mask := (t4 >> 31) - 1
|
||||
h_mask := ^t_mask
|
||||
h0 = (h0 & h_mask) | (t0 & t_mask)
|
||||
h1 = (h1 & h_mask) | (t1 & t_mask)
|
||||
h2 = (h2 & h_mask) | (t2 & t_mask)
|
||||
h3 = (h3 & h_mask) | (t3 & t_mask)
|
||||
h4 = (h4 & h_mask) | (t4 & t_mask)
|
||||
|
||||
// h %= 2^128
|
||||
h0 |= h1 << 26
|
||||
h1 = ((h1 >> 6) | (h2 << 20))
|
||||
h2 = ((h2 >> 12) | (h3 << 14))
|
||||
h3 = ((h3 >> 18) | (h4 << 8))
|
||||
|
||||
// s: the s part of the key
|
||||
// tag = (h + s) % (2^128)
|
||||
t := uint64(h0) + uint64(s[0])
|
||||
h0 = uint32(t)
|
||||
t = uint64(h1) + uint64(s[1]) + (t >> 32)
|
||||
h1 = uint32(t)
|
||||
t = uint64(h2) + uint64(s[2]) + (t >> 32)
|
||||
h2 = uint32(t)
|
||||
t = uint64(h3) + uint64(s[3]) + (t >> 32)
|
||||
h3 = uint32(t)
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:], h0)
|
||||
binary.LittleEndian.PutUint32(out[4:], h1)
|
||||
binary.LittleEndian.PutUint32(out[8:], h2)
|
||||
binary.LittleEndian.PutUint32(out[12:], h3)
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 60%
|
||||
threshold: null
|
||||
patch: false
|
||||
changes: false
|
||||
@@ -1,5 +0,0 @@
|
||||
.idea
|
||||
.vscode
|
||||
coverage.txt
|
||||
build
|
||||
dnscrypt
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
# options for analysis running
|
||||
run:
|
||||
# default concurrency is a available CPU number
|
||||
concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
deadline: 2m
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
skip-files:
|
||||
- ".*generated.*"
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
gocyclo:
|
||||
min-complexity: 20
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- errcheck
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
- depguard
|
||||
- dupl
|
||||
- gocyclo
|
||||
- goimports
|
||||
- revive
|
||||
- gosec
|
||||
- misspell
|
||||
- stylecheck
|
||||
- unconvert
|
||||
disable-all: true
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently of this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
exclude:
|
||||
# gosec: Potential file inclusion via variable
|
||||
# Exclude as it is required it in the command-line tool.
|
||||
- G304
|
||||
# gosec: Use of weak random number generator
|
||||
# Used in tests.
|
||||
- G404
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS 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.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
NAME=dnscrypt
|
||||
BASE_BUILDDIR=build
|
||||
BUILDNAME=$(GOOS)-$(GOARCH)$(GOARM)
|
||||
BUILDDIR=$(BASE_BUILDDIR)/$(BUILDNAME)
|
||||
VERSION?=dev
|
||||
|
||||
ifeq ($(GOOS),windows)
|
||||
ext=.exe
|
||||
archiveCmd=zip -9 -r $(NAME)-$(BUILDNAME)-$(VERSION).zip $(BUILDNAME)
|
||||
else
|
||||
ext=
|
||||
archiveCmd=tar czpvf $(NAME)-$(BUILDNAME)-$(VERSION).tar.gz $(BUILDNAME)
|
||||
endif
|
||||
|
||||
.PHONY: default
|
||||
default: build
|
||||
|
||||
build: clean test
|
||||
go build -ldflags "-X main.VersionString=$(VERSION)" -o $(NAME)$(ext) ./cmd
|
||||
|
||||
release: check-env-release
|
||||
mkdir -p $(BUILDDIR)
|
||||
cp LICENSE $(BUILDDIR)/
|
||||
cp README.md $(BUILDDIR)/
|
||||
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags "-X main.VersionString=$(VERSION)" -o $(BUILDDIR)/$(NAME)$(ext) ./cmd/
|
||||
cd $(BASE_BUILDDIR) ; $(archiveCmd)
|
||||
|
||||
test:
|
||||
go test -race -v -bench=. ./...
|
||||
|
||||
clean:
|
||||
go clean
|
||||
rm -rf $(BASE_BUILDDIR)
|
||||
|
||||
check-env-release:
|
||||
@ if [ "$(GOOS)" = "" ]; then \
|
||||
echo "Environment variable GOOS not set"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@ if [ "$(GOARCH)" = "" ]; then \
|
||||
echo "Environment variable GOOS not set"; \
|
||||
exit 1; \
|
||||
fi
|
||||
-209
@@ -1,209 +0,0 @@
|
||||
[](https://codecov.io/github/ameshkov/dnscrypt?branch=master)
|
||||
[](https://goreportcard.com/report/ameshkov/dnscrypt)
|
||||
[](https://godoc.org/github.com/ameshkov/dnscrypt)
|
||||
|
||||
# DNSCrypt Go
|
||||
|
||||
Golang-implementation of the [DNSCrypt v2 protocol](https://dnscrypt.info/protocol).
|
||||
|
||||
This repo includes everything you need to work with DNSCrypt. You can run your own resolver, make DNS lookups to other DNSCrypt resolvers, and you can use it as a library in your own projects.
|
||||
|
||||
* [Command-line tool](#commandline)
|
||||
* [How to install](#install)
|
||||
* [How to configure](#configure)
|
||||
* [Converting dnscrypt-wrapper configuration](#convertfromwrapper)
|
||||
* [Running a server](#runningserver)
|
||||
* [Making lookups](#lookup)
|
||||
* [Programming interface](#api)
|
||||
* [Client](#client)
|
||||
* [Server](#server)
|
||||
|
||||
## <a id="commandline"></a> Command-line tool
|
||||
|
||||
`dnscrypt` is a helper tool that can work as a DNSCrypt client or server.
|
||||
|
||||
Please note, that even though this tool can work as a server, it's purpose is merely testing. Use [dnsproxy](https://github.com/AdguardTeam/dnsproxy) or [AdGuard Home](https://github.com/AdguardTeam/AdGuardHome) for real-life purposes.
|
||||
|
||||
|
||||
### <a id="install"></a> How to install
|
||||
|
||||
Download and unpack an archive for your platform from the [latest release](https://github.com/ameshkov/dnscrypt/releases).
|
||||
|
||||
Homebrew:
|
||||
```
|
||||
brew install ameshkov/tap/dnscrypt
|
||||
```
|
||||
|
||||
### <a id="configure"></a> How to configure
|
||||
|
||||
Generate a configuration file for running a DNSCrypt server:
|
||||
|
||||
```
|
||||
./dnscrypt generate
|
||||
|
||||
[generate command options]
|
||||
-p, --provider-name= DNSCrypt provider name. Param is required.
|
||||
-o, --out= Path to the resulting config file. Param is required.
|
||||
-k, --private-key= Private key (hex-encoded)
|
||||
-t, --ttl= Certificate time-to-live (seconds)
|
||||
```
|
||||
|
||||
It will generate a configuration file that looks like this:
|
||||
|
||||
```yaml
|
||||
provider_name: 2.dnscrypt-cert.example.org
|
||||
public_key: F11DDBCC4817E543845FDDD4CB881849B64226F3DE397625669D87B919BC4FB0
|
||||
private_key: 5752095FFA56D963569951AFE70FE1690F378D13D8AD6F8054DFAA100907F8B6F11DDBCC4817E543845FDDD4CB881849B64226F3DE397625669D87B919BC4FB0
|
||||
resolver_secret: 9E46E79FEB3AB3D45F4EB3EA957DEAF5D9639A0179F1850AFABA7E58F87C74C4
|
||||
resolver_public: 9327C5E64783E19C339BD6B680A56DB85521CC6E4E0CA5DF5274E2D3CE026C6B
|
||||
es_version: 1
|
||||
certificate_ttl: 0s
|
||||
```
|
||||
|
||||
* `provider_name` - DNSCrypt resolver name.
|
||||
* `public_key`, `private_key` - keypair that is used by the DNSCrypt resolver to sign the certificate.
|
||||
* `resolver_secret`, `resolver_public` - keypair that is used by the DNSCrypt resolver to encrypt and decrypt messages.
|
||||
* `es_version` - crypto to use. Can be `1` (XSalsa20Poly1305) or `2` (XChacha20Poly1305).
|
||||
* `certificate_ttl` - certificate time-to-live. By default it's set to `0` and in this case 1-year cert is generated. The certificate is generated on `dnscrypt` start-up and it will only be valid for the specified amount of time. You should periodically restart `dnscrypt` to rotate the cert.
|
||||
|
||||
#### <a id="convertfromwrapper"></a> Converting [dnscrypt-wrapper](https://github.com/cofyc/dnscrypt-wrapper) configuration
|
||||
|
||||
Also, to create a configuration, you can use the keys generated using [dnscrypt-wrapper](https://github.com/cofyc/dnscrypt-wrapper) by running the command:
|
||||
|
||||
```
|
||||
./dnscrypt convert-dnscrypt-wrapper
|
||||
|
||||
[convert-dnscrypt-wrapper command options]
|
||||
-p, --private-key= Path to the DNSCrypt resolver private key file that is used for signing certificates. Param is required.
|
||||
-r, --resolver-secret= Path to the Short-term privacy key file for encrypting/decrypting DNS queries. If not specified, resolver_secret and resolver_public will be randomly generated.
|
||||
-n, --provider-name= DNSCrypt provider name. Param is required.
|
||||
-o, --out= Path to the resulting config file. Param is required.
|
||||
-t, --ttl= Certificate time-to-live (seconds)
|
||||
```
|
||||
|
||||
|
||||
### <a id="runningserver"></a> Running a server
|
||||
|
||||
This configuration file can be used to run a DNSCrypt forwarding server:
|
||||
|
||||
```
|
||||
./dnscrypt server
|
||||
|
||||
[server command options]
|
||||
-c, --config= Path to the DNSCrypt configuration file. Param is required.
|
||||
-f, --forward= Forwards DNS queries to the specified address (default: 94.140.14.140:53)
|
||||
-l, --listen= Listening addresses (default: 0.0.0.0)
|
||||
-p, --port= Listening ports (default: 443)
|
||||
```
|
||||
|
||||
Now you can go to https://dnscrypt.info/stamps and use `provider_name` and `public_key` from this configuration to generate a DNS stamp. Here's how it looks like for a server running on `127.0.0.1:443`:
|
||||
|
||||
```
|
||||
sdns://AQcAAAAAAAAADTEyNy4wLjAuMTo0NDMg8R3bzEgX5UOEX93Uy4gYSbZCJvPeOXYlZp2HuRm8T7AbMi5kbnNjcnlwdC1jZXJ0LmV4YW1wbGUub3Jn
|
||||
```
|
||||
|
||||
### <a id="lookup"></a> Making lookups
|
||||
|
||||
You can use that stamp to send a DNSCrypt request to your server:
|
||||
|
||||
```
|
||||
./dnscrypt lookup-stamp
|
||||
|
||||
[lookup-stamp command options]
|
||||
-n, --network= network type (tcp/udp) (default: udp)
|
||||
-s, --stamp= DNSCrypt resolver stamp. Param is required.
|
||||
-d, --domain= Domain to resolve. Param is required.
|
||||
-t, --type= DNS query type (default: A)
|
||||
```
|
||||
|
||||
You can also send a DNSCrypt request using a command that does not require stamps:
|
||||
|
||||
```
|
||||
./dnscrypt lookup \
|
||||
|
||||
[lookup command options]
|
||||
-n, --network= network type (tcp/udp) (default: udp)
|
||||
-p, --provider-name= DNSCrypt resolver provider name. Param is required.
|
||||
-k, --public-key= DNSCrypt resolver public key. Param is required.
|
||||
-a, --addr= Resolver address (IP[:port]). By default, the port is 443. Param is required.
|
||||
-d, --domain= Domain to resolve. Param is required.
|
||||
-t, --type= DNS query type (default: A)
|
||||
```
|
||||
|
||||
## <a id="api"></a> Programming interface
|
||||
|
||||
### <a id="client"></a> Client
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
)
|
||||
|
||||
// AdGuard DNS stamp
|
||||
stampStr := "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"
|
||||
|
||||
// Initializing the DNSCrypt client
|
||||
c := dnscrypt.Client{Net: "udp", Timeout: 10 * time.Second}
|
||||
|
||||
// Fetching and validating the server certificate
|
||||
resolverInfo, err := c.Dial(stampStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a DNS request
|
||||
req := dns.Msg{}
|
||||
req.Id = dns.Id()
|
||||
req.RecursionDesired = true
|
||||
req.Question = []dns.Question{
|
||||
{
|
||||
Name: "google-public-dns-a.google.com.",
|
||||
Qtype: dns.TypeA,
|
||||
Qclass: dns.ClassINET,
|
||||
},
|
||||
}
|
||||
|
||||
// Get the DNS response
|
||||
reply, err := c.Exchange(&req, resolverInfo)
|
||||
```
|
||||
|
||||
## <a id="server"></a> Server
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
)
|
||||
|
||||
// Prepare the test DNSCrypt server config
|
||||
rc, err := dnscrypt.GenerateResolverConfig("example.org", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cert, err := rc.CreateCert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := &dnscrypt.Server{
|
||||
ProviderName: rc.ProviderName,
|
||||
ResolverCert: cert,
|
||||
Handler: dnscrypt.DefaultHandler,
|
||||
}
|
||||
|
||||
// Prepare TCP listener
|
||||
tcpConn, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.IPv4zero, Port: 443})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare UDP listener
|
||||
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 443})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start the server
|
||||
go s.ServeUDP(udpConn)
|
||||
go s.ServeTCP(tcpConn)
|
||||
```
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Cert is a DNSCrypt server certificate
|
||||
// See ResolverConfig for more info on how to create one
|
||||
type Cert struct {
|
||||
// Serial is a 4 byte serial number in big-endian format. If more than
|
||||
// one certificates are valid, the client must prefer the certificate
|
||||
// with a higher serial number.
|
||||
Serial uint32
|
||||
|
||||
// <es-version> ::= the cryptographic construction to use with this
|
||||
// certificate.
|
||||
// For X25519-XSalsa20Poly1305, <es-version> must be 0x00 0x01.
|
||||
// For X25519-XChacha20Poly1305, <es-version> must be 0x00 0x02.
|
||||
EsVersion CryptoConstruction
|
||||
|
||||
// Signature is a 64-byte signature of (<resolver-pk> <client-magic>
|
||||
// <serial> <ts-start> <ts-end> <extensions>) using the Ed25519 algorithm and the
|
||||
// provider secret key. Ed25519 must be used in this version of the
|
||||
// protocol.
|
||||
Signature [ed25519.SignatureSize]byte
|
||||
|
||||
// ResolverPk is the resolver's short-term public key, which is 32 bytes when using X25519.
|
||||
// This key is used to encrypt/decrypt DNS queries
|
||||
ResolverPk [keySize]byte
|
||||
|
||||
// ResolverSk is the resolver's short-term private key, which is 32 bytes when using X25519.
|
||||
// Note that it's only used in the server implementation and never serialized/deserialized.
|
||||
// This key is used to encrypt/decrypt DNS queries
|
||||
ResolverSk [keySize]byte
|
||||
|
||||
// ClientMagic is the first 8 bytes of a client query that is to be built
|
||||
// using the information from this certificate. It may be a truncated
|
||||
// public key. Two valid certificates cannot share the same <client-magic>.
|
||||
ClientMagic [clientMagicSize]byte
|
||||
|
||||
// NotAfter is the date the certificate is valid from, as a big-endian
|
||||
// 4-byte unsigned Unix timestamp.
|
||||
NotBefore uint32
|
||||
|
||||
// NotAfter is the date the certificate is valid until (inclusive), as a
|
||||
// big-endian 4-byte unsigned Unix timestamp.
|
||||
NotAfter uint32
|
||||
}
|
||||
|
||||
// Serialize serializes the cert to bytes
|
||||
// <cert> ::= <cert-magic> <es-version> <protocol-minor-version> <signature>
|
||||
// <resolver-pk> <client-magic> <serial> <ts-start> <ts-end>
|
||||
// <extensions>
|
||||
// Certificates made of these information, without extensions, are 116 bytes
|
||||
// long. With the addition of the cert-magic, es-version and
|
||||
// protocol-minor-version, the record is 124 bytes long.
|
||||
func (c *Cert) Serialize() ([]byte, error) {
|
||||
// validate
|
||||
if c.EsVersion == UndefinedConstruction {
|
||||
return nil, ErrEsVersion
|
||||
}
|
||||
|
||||
if !c.VerifyDate() {
|
||||
return nil, ErrInvalidDate
|
||||
}
|
||||
|
||||
// start serializing
|
||||
b := make([]byte, 124)
|
||||
|
||||
// <cert-magic>
|
||||
copy(b[:4], certMagic[:])
|
||||
// <es-version>
|
||||
binary.BigEndian.PutUint16(b[4:6], uint16(c.EsVersion))
|
||||
// <protocol-minor-version> - always 0x00 0x00
|
||||
copy(b[6:8], []byte{0, 0})
|
||||
// <signature>
|
||||
copy(b[8:72], c.Signature[:ed25519.SignatureSize])
|
||||
// signed: (<resolver-pk> <client-magic> <serial> <ts-start> <ts-end> <extensions>)
|
||||
c.writeSigned(b[72:])
|
||||
|
||||
// done
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Deserialize deserializes certificate from a byte array
|
||||
// <cert> ::= <cert-magic> <es-version> <protocol-minor-version> <signature>
|
||||
// <resolver-pk> <client-magic> <serial> <ts-start> <ts-end>
|
||||
// <extensions>
|
||||
func (c *Cert) Deserialize(b []byte) error {
|
||||
if len(b) < 124 {
|
||||
return ErrCertTooShort
|
||||
}
|
||||
|
||||
// <cert-magic>
|
||||
if !bytes.Equal(b[:4], certMagic[:4]) {
|
||||
return ErrCertMagic
|
||||
}
|
||||
|
||||
// <es-version>
|
||||
switch esVersion := binary.BigEndian.Uint16(b[4:6]); esVersion {
|
||||
case uint16(XSalsa20Poly1305):
|
||||
c.EsVersion = XSalsa20Poly1305
|
||||
case uint16(XChacha20Poly1305):
|
||||
c.EsVersion = XChacha20Poly1305
|
||||
default:
|
||||
return ErrEsVersion
|
||||
}
|
||||
|
||||
// Ignore 6:8, <protocol-minor-version>
|
||||
// <signature>
|
||||
copy(c.Signature[:], b[8:72])
|
||||
// <resolver-pk>
|
||||
copy(c.ResolverPk[:], b[72:104])
|
||||
// <client-magic>
|
||||
copy(c.ClientMagic[:], b[104:112])
|
||||
// <serial>
|
||||
c.Serial = binary.BigEndian.Uint32(b[112:116])
|
||||
// <ts-start> <ts-end>
|
||||
c.NotBefore = binary.BigEndian.Uint32(b[116:120])
|
||||
c.NotAfter = binary.BigEndian.Uint32(b[120:124])
|
||||
|
||||
// Deserialized with no issues
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyDate checks that the cert is valid at this moment
|
||||
func (c *Cert) VerifyDate() bool {
|
||||
if c.NotBefore >= c.NotAfter {
|
||||
return false
|
||||
}
|
||||
now := uint32(time.Now().Unix())
|
||||
if now > c.NotAfter || now < c.NotBefore {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// VerifySignature checks if the cert is properly signed with the specified signature
|
||||
func (c *Cert) VerifySignature(publicKey ed25519.PublicKey) bool {
|
||||
b := make([]byte, 52)
|
||||
c.writeSigned(b)
|
||||
return ed25519.Verify(publicKey, b, c.Signature[:])
|
||||
}
|
||||
|
||||
// Sign creates cert.Signature
|
||||
func (c *Cert) Sign(privateKey ed25519.PrivateKey) {
|
||||
b := make([]byte, 52)
|
||||
c.writeSigned(b)
|
||||
signature := ed25519.Sign(privateKey, b)
|
||||
copy(c.Signature[:64], signature[:64])
|
||||
}
|
||||
|
||||
// String Cert's string representation
|
||||
func (c *Cert) String() string {
|
||||
return fmt.Sprintf("Certificate Serial=%d NotBefore=%s NotAfter=%s EsVersion=%s",
|
||||
c.Serial, time.Unix(int64(c.NotBefore), 0).String(),
|
||||
time.Unix(int64(c.NotAfter), 0).String(), c.EsVersion.String())
|
||||
}
|
||||
|
||||
// writeSigned writes (<resolver-pk> <client-magic> <serial> <ts-start> <ts-end> <extensions>)
|
||||
func (c *Cert) writeSigned(dst []byte) {
|
||||
// <resolver-pk>
|
||||
copy(dst[:32], c.ResolverPk[:keySize])
|
||||
// <client-magic>
|
||||
copy(dst[32:40], c.ClientMagic[:clientMagicSize])
|
||||
// <serial>
|
||||
binary.BigEndian.PutUint32(dst[40:44], c.Serial)
|
||||
// <ts-start>
|
||||
binary.BigEndian.PutUint32(dst[44:48], c.NotBefore)
|
||||
// <ts-end>
|
||||
binary.BigEndian.PutUint32(dst[48:52], c.NotAfter)
|
||||
}
|
||||
-336
@@ -1,336 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/ameshkov/dnsstamps"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Client is a DNSCrypt resolver client
|
||||
type Client struct {
|
||||
Net string // protocol (can be "udp" or "tcp", by default - "udp")
|
||||
Timeout time.Duration // read/write timeout
|
||||
|
||||
// UDPSize is the maximum size of a DNS response (or query) this client can
|
||||
// sent or receive. If not set, we use dns.MinMsgSize by default.
|
||||
UDPSize int
|
||||
}
|
||||
|
||||
// ResolverInfo contains DNSCrypt resolver information necessary for decryption/encryption
|
||||
type ResolverInfo struct {
|
||||
SecretKey [keySize]byte // Client short-term secret key
|
||||
PublicKey [keySize]byte // Client short-term public key
|
||||
|
||||
ServerPublicKey ed25519.PublicKey // Resolver public key (this key is used to validate cert signature)
|
||||
ServerAddress string // Server IP address
|
||||
ProviderName string // Provider name
|
||||
|
||||
ResolverCert *Cert // Certificate info (obtained with the first unencrypted DNS request)
|
||||
SharedKey [keySize]byte // Shared key that is to be used to encrypt/decrypt messages
|
||||
}
|
||||
|
||||
// Dial fetches and validates DNSCrypt certificate from the given server
|
||||
// Data received during this call is then used for DNS requests encryption/decryption
|
||||
// stampStr is an sdns:// address which is parsed using go-dnsstamps package
|
||||
func (c *Client) Dial(stampStr string) (*ResolverInfo, error) {
|
||||
stamp, err := dnsstamps.NewServerStampFromString(stampStr)
|
||||
if err != nil {
|
||||
// Invalid SDNS stamp
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stamp.Proto != dnsstamps.StampProtoTypeDNSCrypt {
|
||||
return nil, ErrInvalidDNSStamp
|
||||
}
|
||||
|
||||
return c.DialStamp(stamp)
|
||||
}
|
||||
|
||||
// DialStamp fetches and validates DNSCrypt certificate from the given server
|
||||
// Data received during this call is then used for DNS requests encryption/decryption
|
||||
func (c *Client) DialStamp(stamp dnsstamps.ServerStamp) (*ResolverInfo, error) {
|
||||
resolverInfo := &ResolverInfo{}
|
||||
|
||||
// Generate the secret/public pair
|
||||
resolverInfo.SecretKey, resolverInfo.PublicKey = generateRandomKeyPair()
|
||||
|
||||
// Set the provider properties
|
||||
resolverInfo.ServerPublicKey = stamp.ServerPk
|
||||
resolverInfo.ServerAddress = stamp.ServerAddrStr
|
||||
resolverInfo.ProviderName = stamp.ProviderName
|
||||
|
||||
cert, err := c.fetchCert(stamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resolverInfo.ResolverCert = cert
|
||||
|
||||
// Compute shared key that we'll use to encrypt/decrypt messages
|
||||
sharedKey, err := computeSharedKey(cert.EsVersion, &resolverInfo.SecretKey, &cert.ResolverPk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolverInfo.SharedKey = sharedKey
|
||||
return resolverInfo, nil
|
||||
}
|
||||
|
||||
// Exchange performs a synchronous DNS query to the specified DNSCrypt server and returns a DNS response.
|
||||
// This method creates a new network connection for every call so avoid using it for TCP.
|
||||
// DNSCrypt cert needs to be fetched and validated prior to this call using the c.DialStamp method.
|
||||
func (c *Client) Exchange(m *dns.Msg, resolverInfo *ResolverInfo) (resp *dns.Msg, err error) {
|
||||
network := "udp"
|
||||
if c.Net == "tcp" {
|
||||
network = "tcp"
|
||||
}
|
||||
|
||||
conn, err := net.Dial(network, resolverInfo.ServerAddress)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dialing: %w", err)
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, conn.Close()) }()
|
||||
|
||||
resp, err = c.ExchangeConn(conn, m, resolverInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("exchanging: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ExchangeConn performs a synchronous DNS query to the specified DNSCrypt server and returns a DNS response.
|
||||
// DNSCrypt server information needs to be fetched and validated prior to this call using the c.DialStamp method
|
||||
func (c *Client) ExchangeConn(conn net.Conn, m *dns.Msg, resolverInfo *ResolverInfo) (*dns.Msg, error) {
|
||||
query, err := c.encrypt(m, resolverInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.writeQuery(conn, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := c.readResponse(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := c.decrypt(b, resolverInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// writeQuery writes query to the network connection
|
||||
// depending on the protocol we may write a 2-byte prefix or not
|
||||
func (c *Client) writeQuery(conn net.Conn, query []byte) error {
|
||||
var err error
|
||||
|
||||
if c.Timeout > 0 {
|
||||
_ = conn.SetWriteDeadline(time.Now().Add(c.Timeout))
|
||||
}
|
||||
|
||||
// Write to the connection
|
||||
if _, ok := conn.(*net.TCPConn); ok {
|
||||
l := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(l, uint16(len(query)))
|
||||
_, err = (&net.Buffers{l, query}).WriteTo(conn)
|
||||
} else {
|
||||
_, err = conn.Write(query)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// readResponse reads response from the network connection
|
||||
// depending on the protocol, we may read a 2-byte prefix or not
|
||||
func (c *Client) readResponse(conn net.Conn) ([]byte, error) {
|
||||
if c.Timeout > 0 {
|
||||
_ = conn.SetReadDeadline(time.Now().Add(c.Timeout))
|
||||
}
|
||||
|
||||
proto := "udp"
|
||||
if _, ok := conn.(*net.TCPConn); ok {
|
||||
proto = "tcp"
|
||||
}
|
||||
|
||||
if proto == "udp" {
|
||||
bufSize := c.UDPSize
|
||||
if bufSize == 0 {
|
||||
bufSize = dns.MinMsgSize
|
||||
}
|
||||
response := make([]byte, bufSize)
|
||||
n, err := conn.Read(response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response[:n], nil
|
||||
}
|
||||
|
||||
// If we got here, this is a TCP connection
|
||||
// so we should read a 2-byte prefix first
|
||||
return readPrefixed(conn)
|
||||
}
|
||||
|
||||
// encrypt encrypts a DNS message using shared key from the resolver info
|
||||
func (c *Client) encrypt(m *dns.Msg, resolverInfo *ResolverInfo) ([]byte, error) {
|
||||
q := EncryptedQuery{
|
||||
EsVersion: resolverInfo.ResolverCert.EsVersion,
|
||||
ClientMagic: resolverInfo.ResolverCert.ClientMagic,
|
||||
ClientPk: resolverInfo.PublicKey,
|
||||
}
|
||||
query, err := m.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := q.Encrypt(query, resolverInfo.SharedKey)
|
||||
if len(b) > c.maxQuerySize() {
|
||||
return nil, ErrQueryTooLarge
|
||||
}
|
||||
|
||||
return b, err
|
||||
}
|
||||
|
||||
// decrypts decrypts a DNS message using a shared key from the resolver info
|
||||
func (c *Client) decrypt(b []byte, resolverInfo *ResolverInfo) (*dns.Msg, error) {
|
||||
dr := EncryptedResponse{
|
||||
EsVersion: resolverInfo.ResolverCert.EsVersion,
|
||||
}
|
||||
msg, err := dr.Decrypt(b, resolverInfo.SharedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := new(dns.Msg)
|
||||
err = res.Unpack(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// fetchCert loads DNSCrypt cert from the specified server
|
||||
func (c *Client) fetchCert(stamp dnsstamps.ServerStamp) (cert *Cert, err error) {
|
||||
providerName := stamp.ProviderName
|
||||
if !strings.HasSuffix(providerName, ".") {
|
||||
providerName = providerName + "."
|
||||
}
|
||||
|
||||
query := new(dns.Msg)
|
||||
query.SetQuestion(providerName, dns.TypeTXT)
|
||||
// use 1252 as a UDPSize for this client to make sure the buffer is not too small
|
||||
client := dns.Client{Net: c.Net, UDPSize: uint16(1252), Timeout: c.Timeout}
|
||||
r, _, err := client.Exchange(query, stamp.ServerAddrStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return nil, ErrFailedToFetchCert
|
||||
}
|
||||
|
||||
currentCert := &Cert{}
|
||||
foundValid := false
|
||||
for _, rr := range r.Answer {
|
||||
txt, ok := rr.(*dns.TXT)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err = parseCert(stamp, currentCert, providerName, strings.Join(txt.Txt, ""))
|
||||
if err != nil {
|
||||
log.Debug("[%s] bad cert: %s", providerName, err)
|
||||
} else if cert == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
currentCert = cert
|
||||
foundValid = true
|
||||
}
|
||||
|
||||
if foundValid {
|
||||
return currentCert, nil
|
||||
} else if err == nil {
|
||||
err = errors.Error("no valid txt records")
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// parseCert parses a certificate from its string form and returns it if it has
|
||||
// priority over currentCert.
|
||||
func parseCert(
|
||||
stamp dnsstamps.ServerStamp,
|
||||
currentCert *Cert,
|
||||
providerName string,
|
||||
certStr string,
|
||||
) (cert *Cert, err error) {
|
||||
certBytes, err := unpackTxtString(certStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unpacking txt record: %w", err)
|
||||
}
|
||||
|
||||
cert = &Cert{}
|
||||
err = cert.Deserialize(certBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("deserializing cert for: %w", err)
|
||||
}
|
||||
|
||||
log.Debug("[%s] fetched certificate %d", providerName, cert.Serial)
|
||||
|
||||
if !cert.VerifyDate() {
|
||||
return nil, ErrInvalidDate
|
||||
}
|
||||
|
||||
if !cert.VerifySignature(stamp.ServerPk) {
|
||||
return nil, ErrInvalidCertSignature
|
||||
}
|
||||
|
||||
if cert.Serial < currentCert.Serial {
|
||||
log.Debug("[%v] cert %d superseded by a previous certificate", providerName, cert.Serial)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if cert.Serial > currentCert.Serial {
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
if cert.EsVersion <= currentCert.EsVersion {
|
||||
log.Debug("[%v] keeping the previous, preferred crypto construction", providerName)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
log.Debug(
|
||||
"[%v] upgrading the construction from %v to %v",
|
||||
providerName,
|
||||
currentCert.EsVersion,
|
||||
cert.EsVersion,
|
||||
)
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
func (c *Client) maxQuerySize() int {
|
||||
if c.Net == "tcp" {
|
||||
return dns.MaxMsgSize
|
||||
}
|
||||
|
||||
if c.UDPSize > 0 {
|
||||
return c.UDPSize
|
||||
}
|
||||
|
||||
return dns.MinMsgSize
|
||||
}
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
// Error represents a dnscrypt error.
|
||||
type Error string
|
||||
|
||||
func (e Error) Error() string { return "dnscrypt: " + string(e) }
|
||||
|
||||
const (
|
||||
// ErrTooShort means that the DNS query is shorter than possible
|
||||
ErrTooShort = Error("message is too short")
|
||||
|
||||
// ErrQueryTooLarge means that the DNS query is larger than max allowed size
|
||||
ErrQueryTooLarge = Error("DNSCrypt query is too large")
|
||||
|
||||
// ErrEsVersion means that the cert contains unsupported es-version
|
||||
ErrEsVersion = Error("unsupported es-version")
|
||||
|
||||
// ErrInvalidDate means that the cert is not valid for the current time
|
||||
ErrInvalidDate = Error("cert has invalid ts-start or ts-end")
|
||||
|
||||
// ErrInvalidCertSignature means that the cert has invalid signature
|
||||
ErrInvalidCertSignature = Error("cert has invalid signature")
|
||||
|
||||
// ErrInvalidQuery means that it failed to decrypt a DNSCrypt query
|
||||
ErrInvalidQuery = Error("DNSCrypt query is invalid and cannot be decrypted")
|
||||
|
||||
// ErrInvalidClientMagic means that client-magic does not match
|
||||
ErrInvalidClientMagic = Error("DNSCrypt query contains invalid client magic")
|
||||
|
||||
// ErrInvalidResolverMagic means that server-magic does not match
|
||||
ErrInvalidResolverMagic = Error("DNSCrypt response contains invalid resolver magic")
|
||||
|
||||
// ErrInvalidResponse means that it failed to decrypt a DNSCrypt response
|
||||
ErrInvalidResponse = Error("DNSCrypt response is invalid and cannot be decrypted")
|
||||
|
||||
// ErrInvalidPadding means that it failed to unpad a query
|
||||
ErrInvalidPadding = Error("invalid padding")
|
||||
|
||||
// ErrInvalidDNSStamp means an invalid DNS stamp
|
||||
ErrInvalidDNSStamp = Error("invalid DNS stamp")
|
||||
|
||||
// ErrFailedToFetchCert means that it failed to fetch DNSCrypt certificate
|
||||
ErrFailedToFetchCert = Error("failed to fetch DNSCrypt certificate")
|
||||
|
||||
// ErrCertTooShort means that it failed to deserialize cert, too short
|
||||
ErrCertTooShort = Error("cert is too short")
|
||||
|
||||
// ErrCertMagic means an invalid cert magic
|
||||
ErrCertMagic = Error("invalid cert magic")
|
||||
|
||||
// ErrServerConfig means that it failed to start the DNSCrypt server - invalid configuration
|
||||
ErrServerConfig = Error("invalid server configuration")
|
||||
|
||||
// ErrServerNotStarted is returned if there's nothing to shutdown
|
||||
ErrServerNotStarted = Error("server is not started")
|
||||
)
|
||||
|
||||
const (
|
||||
// <min-query-len> is a variable length, initially set to 256 bytes, and
|
||||
// must be a multiple of 64 bytes. (see https://dnscrypt.info/protocol)
|
||||
// Some servers do not work if padded length is less than 256. Example: Quad9
|
||||
minUDPQuestionSize = 256
|
||||
|
||||
// Minimum possible DNS packet size
|
||||
minDNSPacketSize = 12 + 5
|
||||
|
||||
// See 11. Authenticated encryption and key exchange algorithm
|
||||
// The public and secret keys are 32 bytes long in storage
|
||||
keySize = 32
|
||||
|
||||
// size of the shared key used to encrypt/decrypt messages
|
||||
sharedKeySize = 32
|
||||
|
||||
// ClientMagic is the first 8 bytes of a client query that is to be built
|
||||
// using the information from this certificate. It may be a truncated
|
||||
// public key. Two valid certificates cannot share the same <client-magic>.
|
||||
clientMagicSize = 8
|
||||
|
||||
// When using X25519-XSalsa20Poly1305, this construction requires a 24 bytes
|
||||
// nonce, that must not be reused for a given shared secret.
|
||||
nonceSize = 24
|
||||
|
||||
// the first 8 bytes of every dnscrypt response. must match resolverMagic.
|
||||
resolverMagicSize = 8
|
||||
)
|
||||
|
||||
var (
|
||||
// certMagic is a bytes sequence that must be in the beginning of the serialized cert
|
||||
certMagic = [4]byte{0x44, 0x4e, 0x53, 0x43}
|
||||
|
||||
// resolverMagic is a byte sequence that must be in the beginning of every response
|
||||
resolverMagic = []byte{0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38}
|
||||
)
|
||||
|
||||
// CryptoConstruction represents the encryption algorithm (either XSalsa20Poly1305 or XChacha20Poly1305)
|
||||
type CryptoConstruction uint16
|
||||
|
||||
const (
|
||||
// UndefinedConstruction is the default value for empty CertInfo only
|
||||
UndefinedConstruction CryptoConstruction = iota
|
||||
// XSalsa20Poly1305 encryption
|
||||
XSalsa20Poly1305 CryptoConstruction = 0x0001
|
||||
// XChacha20Poly1305 encryption
|
||||
XChacha20Poly1305 CryptoConstruction = 0x0002
|
||||
)
|
||||
|
||||
func (c CryptoConstruction) String() string {
|
||||
switch c {
|
||||
case XChacha20Poly1305:
|
||||
return "XChacha20Poly1305"
|
||||
case XSalsa20Poly1305:
|
||||
return "XSalsa20Poly1305"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
Package dnscrypt includes everything you need to work with DNSCrypt. You can run your own resolver, make DNS lookups to other DNSCrypt resolvers, and you can use it as a library in your own projects.
|
||||
|
||||
Here's how to create a simple DNSCrypt client:
|
||||
|
||||
// AdGuard DNS stamp
|
||||
stampStr := "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"
|
||||
|
||||
// Initializing the DNSCrypt client
|
||||
c := dnscrypt.Client{Net: "udp", Timeout: 10 * time.Second}
|
||||
|
||||
// Fetching and validating the server certificate
|
||||
resolverInfo, err := c.Dial(stampStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a DNS request
|
||||
req := dns.Msg{}
|
||||
req.Id = dns.Id()
|
||||
req.RecursionDesired = true
|
||||
req.Question = []dns.Question{
|
||||
{Name: "google-public-dns-a.google.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET},
|
||||
}
|
||||
|
||||
// Get the DNS response
|
||||
reply, err := c.Exchange(&req, resolverInfo)
|
||||
|
||||
Here's how to run a DNSCrypt resolver:
|
||||
|
||||
// Prepare the test DNSCrypt server config
|
||||
rc, err := dnscrypt.GenerateResolverConfig("example.org", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cert, err := rc.CreateCert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := &dnscrypt.Server{
|
||||
ProviderName: rc.ProviderName,
|
||||
ResolverCert: cert,
|
||||
Handler: dnscrypt.DefaultHandler,
|
||||
}
|
||||
|
||||
// Prepare TCP listener
|
||||
tcpConn, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.IPv4zero, Port: 443})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare UDP listener
|
||||
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 443})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start the server
|
||||
go s.ServeUDP(udpConn)
|
||||
go s.ServeTCP(tcpConn)
|
||||
*/
|
||||
package dnscrypt
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/ameshkov/dnscrypt/v2/xsecretbox"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
)
|
||||
|
||||
// EncryptedQuery is a structure for encrypting and decrypting client queries
|
||||
//
|
||||
// <dnscrypt-query> ::= <client-magic> <client-pk> <client-nonce> <encrypted-query>
|
||||
// <encrypted-query> ::= AE(<shared-key> <client-nonce> <client-nonce-pad>, <client-query> <client-query-pad>)
|
||||
type EncryptedQuery struct {
|
||||
// EsVersion is the encryption to use
|
||||
EsVersion CryptoConstruction
|
||||
|
||||
// ClientMagic is a 8 byte identifier for the resolver certificate
|
||||
// chosen by the client.
|
||||
ClientMagic [clientMagicSize]byte
|
||||
|
||||
// ClientPk is the client's public key
|
||||
ClientPk [keySize]byte
|
||||
|
||||
// With a 24 bytes nonce, a question sent by a DNSCrypt client must be
|
||||
// encrypted using the shared secret, and a nonce constructed as follows:
|
||||
// 12 bytes chosen by the client followed by 12 NUL (0) bytes.
|
||||
//
|
||||
// The client's half of the nonce can include a timestamp in addition to a
|
||||
// counter or to random bytes, so that when a response is received, the
|
||||
// client can use this timestamp to immediately discard responses to
|
||||
// queries that have been sent too long ago, or dated in the future.
|
||||
Nonce [nonceSize]byte
|
||||
}
|
||||
|
||||
// Encrypt encrypts the specified DNS query, returns encrypted data ready to be sent.
|
||||
//
|
||||
// Note that this method will generate a random nonce automatically.
|
||||
//
|
||||
// The following fields must be set before calling this method:
|
||||
// * EsVersion -- to encrypt the query
|
||||
// * ClientMagic -- to send it with the query
|
||||
// * ClientPk -- to send it with the query
|
||||
func (q *EncryptedQuery) Encrypt(packet []byte, sharedKey [sharedKeySize]byte) ([]byte, error) {
|
||||
var query []byte
|
||||
|
||||
// Step 1: generate nonce
|
||||
binary.BigEndian.PutUint64(q.Nonce[:8], uint64(time.Now().UnixNano()))
|
||||
_, _ = rand.Read(q.Nonce[8:12])
|
||||
|
||||
// Unencrypted part of the query:
|
||||
// <client-magic> <client-pk> <client-nonce>
|
||||
query = append(query, q.ClientMagic[:]...)
|
||||
query = append(query, q.ClientPk[:]...)
|
||||
query = append(query, q.Nonce[:nonceSize/2]...)
|
||||
|
||||
// <client-query> <client-query-pad>
|
||||
padded := pad(packet)
|
||||
|
||||
// <encrypted-query>
|
||||
nonce := q.Nonce
|
||||
if q.EsVersion == XChacha20Poly1305 {
|
||||
query = xsecretbox.Seal(query, nonce[:], padded, sharedKey[:])
|
||||
} else if q.EsVersion == XSalsa20Poly1305 {
|
||||
var xsalsaNonce [nonceSize]byte
|
||||
copy(xsalsaNonce[:], nonce[:])
|
||||
query = secretbox.Seal(query, padded, &xsalsaNonce, &sharedKey)
|
||||
} else {
|
||||
return nil, ErrEsVersion
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the client query, returns decrypted DNS packet.
|
||||
//
|
||||
// Please note, that before calling this method the following fields must be set:
|
||||
// * ClientMagic -- to verify the query
|
||||
// * EsVersion -- to decrypt
|
||||
func (q *EncryptedQuery) Decrypt(query []byte, serverSecretKey [keySize]byte) ([]byte, error) {
|
||||
headerLength := clientMagicSize + keySize + nonceSize/2
|
||||
if len(query) < headerLength+xsecretbox.TagSize+minDNSPacketSize {
|
||||
return nil, ErrInvalidQuery
|
||||
}
|
||||
|
||||
// read and verify <client-magic>
|
||||
clientMagic := [clientMagicSize]byte{}
|
||||
copy(clientMagic[:], query[:clientMagicSize])
|
||||
if !bytes.Equal(clientMagic[:], q.ClientMagic[:]) {
|
||||
return nil, ErrInvalidClientMagic
|
||||
}
|
||||
|
||||
// read <client-pk>
|
||||
idx := clientMagicSize
|
||||
copy(q.ClientPk[:keySize], query[idx:idx+keySize])
|
||||
|
||||
// generate server shared key
|
||||
sharedKey, err := computeSharedKey(q.EsVersion, &serverSecretKey, &q.ClientPk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// read <client-nonce>
|
||||
idx = idx + keySize
|
||||
copy(q.Nonce[:nonceSize/2], query[idx:idx+nonceSize/2])
|
||||
|
||||
// read and decrypt <encrypted-query>
|
||||
idx = idx + nonceSize/2
|
||||
encryptedQuery := query[idx:]
|
||||
var packet []byte
|
||||
if q.EsVersion == XChacha20Poly1305 {
|
||||
packet, err = xsecretbox.Open(nil, q.Nonce[:], encryptedQuery, sharedKey[:])
|
||||
if err != nil {
|
||||
return nil, ErrInvalidQuery
|
||||
}
|
||||
} else if q.EsVersion == XSalsa20Poly1305 {
|
||||
var xsalsaServerNonce [24]byte
|
||||
copy(xsalsaServerNonce[:], q.Nonce[:])
|
||||
var ok bool
|
||||
packet, ok = secretbox.Open(nil, encryptedQuery, &xsalsaServerNonce, &sharedKey)
|
||||
if !ok {
|
||||
return nil, ErrInvalidQuery
|
||||
}
|
||||
} else {
|
||||
return nil, ErrEsVersion
|
||||
}
|
||||
|
||||
packet, err = unpad(packet)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidPadding
|
||||
}
|
||||
|
||||
return packet, nil
|
||||
}
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/ameshkov/dnscrypt/v2/xsecretbox"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
)
|
||||
|
||||
// EncryptedResponse is a structure for encrypting/decrypting server responses
|
||||
//
|
||||
// <dnscrypt-response> ::= <resolver-magic> <nonce> <encrypted-response>
|
||||
// <encrypted-response> ::= AE(<shared-key>, <nonce>, <resolver-response> <resolver-response-pad>)
|
||||
type EncryptedResponse struct {
|
||||
// EsVersion is the encryption to use
|
||||
EsVersion CryptoConstruction
|
||||
|
||||
// Nonce - <nonce> ::= <client-nonce> <resolver-nonce>
|
||||
// <client-nonce> ::= the nonce sent by the client in the related query.
|
||||
Nonce [nonceSize]byte
|
||||
}
|
||||
|
||||
// Encrypt encrypts the server response
|
||||
//
|
||||
// EsVersion must be set.
|
||||
// Nonce needs to be set to "client-nonce".
|
||||
// This method will generate "resolver-nonce" and set it automatically.
|
||||
func (r *EncryptedResponse) Encrypt(packet []byte, sharedKey [sharedKeySize]byte) ([]byte, error) {
|
||||
var response []byte
|
||||
|
||||
// Step 1: generate nonce
|
||||
_, _ = rand.Read(r.Nonce[12:16])
|
||||
binary.BigEndian.PutUint64(r.Nonce[16:nonceSize], uint64(time.Now().UnixNano()))
|
||||
|
||||
// Unencrypted part of the query:
|
||||
response = append(response, resolverMagic[:]...)
|
||||
response = append(response, r.Nonce[:]...)
|
||||
|
||||
// <resolver-response> <resolver-response-pad>
|
||||
padded := pad(packet)
|
||||
|
||||
// <encrypted-response>
|
||||
nonce := r.Nonce
|
||||
if r.EsVersion == XChacha20Poly1305 {
|
||||
response = xsecretbox.Seal(response, nonce[:], padded, sharedKey[:])
|
||||
} else if r.EsVersion == XSalsa20Poly1305 {
|
||||
var xsalsaNonce [nonceSize]byte
|
||||
copy(xsalsaNonce[:], nonce[:])
|
||||
response = secretbox.Seal(response, padded, &xsalsaNonce, &sharedKey)
|
||||
} else {
|
||||
return nil, ErrEsVersion
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the server response
|
||||
//
|
||||
// EsVersion must be set.
|
||||
func (r *EncryptedResponse) Decrypt(response []byte, sharedKey [sharedKeySize]byte) ([]byte, error) {
|
||||
headerLength := len(resolverMagic) + nonceSize
|
||||
if len(response) < headerLength+xsecretbox.TagSize+minDNSPacketSize {
|
||||
return nil, ErrInvalidResponse
|
||||
}
|
||||
|
||||
// read and verify <resolver-magic>
|
||||
magic := [resolverMagicSize]byte{}
|
||||
copy(magic[:], response[:resolverMagicSize])
|
||||
if !bytes.Equal(magic[:], resolverMagic[:]) {
|
||||
return nil, ErrInvalidResolverMagic
|
||||
}
|
||||
|
||||
// read nonce
|
||||
copy(r.Nonce[:], response[resolverMagicSize:nonceSize+resolverMagicSize])
|
||||
|
||||
// read and decrypt <encrypted-response>
|
||||
encryptedResponse := response[nonceSize+resolverMagicSize:]
|
||||
var packet []byte
|
||||
var err error
|
||||
if r.EsVersion == XChacha20Poly1305 {
|
||||
packet, err = xsecretbox.Open(nil, r.Nonce[:], encryptedResponse, sharedKey[:])
|
||||
if err != nil {
|
||||
return nil, ErrInvalidResponse
|
||||
}
|
||||
} else if r.EsVersion == XSalsa20Poly1305 {
|
||||
var xsalsaServerNonce [24]byte
|
||||
copy(xsalsaServerNonce[:], r.Nonce[:])
|
||||
var ok bool
|
||||
packet, ok = secretbox.Open(nil, encryptedResponse, &xsalsaServerNonce, &sharedKey)
|
||||
if !ok {
|
||||
return nil, ErrInvalidResponse
|
||||
}
|
||||
} else {
|
||||
return nil, ErrEsVersion
|
||||
}
|
||||
|
||||
packet, err = unpad(packet)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidPadding
|
||||
}
|
||||
|
||||
return packet, nil
|
||||
}
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/ameshkov/dnsstamps"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
const dnsCryptV2Prefix = "2.dnscrypt-cert."
|
||||
|
||||
// ResolverConfig is the DNSCrypt resolver configuration
|
||||
type ResolverConfig struct {
|
||||
// DNSCrypt provider name
|
||||
ProviderName string `yaml:"provider_name"`
|
||||
|
||||
// PublicKey is the DNSCrypt resolver public key
|
||||
PublicKey string `yaml:"public_key"`
|
||||
|
||||
// PrivateKey is the DNSCrypt resolver private key
|
||||
// The main and only purpose of this key is to sign the certificate
|
||||
PrivateKey string `yaml:"private_key"`
|
||||
|
||||
// ResolverSk is a hex-encoded short-term private key.
|
||||
// This key is used to encrypt/decrypt DNS queries.
|
||||
// If not set, we'll generate a new random ResolverSk and ResolverPk.
|
||||
ResolverSk string `yaml:"resolver_secret"`
|
||||
|
||||
// ResolverPk is a hex-encoded short-term public key corresponding to ResolverSk.
|
||||
// This key is used to encrypt/decrypt DNS queries.
|
||||
ResolverPk string `yaml:"resolver_public"`
|
||||
|
||||
// EsVersion is the crypto to use in this resolver
|
||||
EsVersion CryptoConstruction `yaml:"es_version"`
|
||||
|
||||
// CertificateTTL is the time-to-live value for the certificate that is
|
||||
// generated using this ResolverConfig.
|
||||
// If not set, we'll use 1 year by default.
|
||||
CertificateTTL time.Duration `yaml:"certificate_ttl"`
|
||||
}
|
||||
|
||||
// CreateCert generates a signed Cert to be used by Server
|
||||
func (rc *ResolverConfig) CreateCert() (*Cert, error) {
|
||||
log.Printf("Creating signed DNSCrypt certificate")
|
||||
|
||||
notAfter := time.Now()
|
||||
if rc.CertificateTTL > 0 {
|
||||
notAfter = notAfter.Add(rc.CertificateTTL)
|
||||
} else {
|
||||
// Default cert validity is 1 year
|
||||
notAfter = notAfter.Add(time.Hour * 24 * 365)
|
||||
}
|
||||
|
||||
cert := &Cert{
|
||||
Serial: uint32(time.Now().Unix()),
|
||||
NotAfter: uint32(notAfter.Unix()),
|
||||
NotBefore: uint32(time.Now().Unix()),
|
||||
EsVersion: rc.EsVersion,
|
||||
}
|
||||
|
||||
// short-term public key
|
||||
resolverPk, err := HexDecodeKey(rc.ResolverPk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// short-term private key
|
||||
resolverSk, err := HexDecodeKey(rc.ResolverSk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(resolverPk) != keySize || len(resolverSk) != keySize {
|
||||
log.Printf("Short-term keys are not set, generating random ones")
|
||||
sk, pk := generateRandomKeyPair()
|
||||
resolverSk = sk[:]
|
||||
resolverPk = pk[:]
|
||||
}
|
||||
|
||||
copy(cert.ResolverPk[:], resolverPk[:])
|
||||
copy(cert.ResolverSk[:], resolverSk)
|
||||
|
||||
// private key
|
||||
privateKey, err := HexDecodeKey(rc.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// sign the data
|
||||
cert.Sign(privateKey)
|
||||
|
||||
log.Info("Signed cert: %s", cert.String())
|
||||
|
||||
// done
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// CreateStamp generates a DNS stamp for this resolver
|
||||
func (rc *ResolverConfig) CreateStamp(addr string) (dnsstamps.ServerStamp, error) {
|
||||
stamp := dnsstamps.ServerStamp{
|
||||
ProviderName: rc.ProviderName,
|
||||
Proto: dnsstamps.StampProtoTypeDNSCrypt,
|
||||
}
|
||||
|
||||
serverPk, err := HexDecodeKey(rc.PublicKey)
|
||||
if err != nil {
|
||||
return stamp, err
|
||||
}
|
||||
|
||||
stamp.ServerPk = serverPk
|
||||
stamp.ServerAddrStr = addr
|
||||
return stamp, nil
|
||||
}
|
||||
|
||||
// GenerateResolverConfig generates resolver configuration for a given provider name.
|
||||
// providerName is mandatory. If needed, "2.dnscrypt-cert." prefix is added to it.
|
||||
// privateKey is optional. If not set, it will be generated automatically.
|
||||
func GenerateResolverConfig(providerName string, privateKey ed25519.PrivateKey) (ResolverConfig, error) {
|
||||
rc := ResolverConfig{
|
||||
// Use XSalsa20Poly1305 by default
|
||||
EsVersion: XSalsa20Poly1305,
|
||||
}
|
||||
if !strings.HasPrefix(providerName, dnsCryptV2Prefix) {
|
||||
providerName = dnsCryptV2Prefix + providerName
|
||||
}
|
||||
rc.ProviderName = providerName
|
||||
|
||||
var err error
|
||||
if privateKey == nil {
|
||||
// privateKey = gene
|
||||
_, privateKey, err = ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return rc, err
|
||||
}
|
||||
}
|
||||
rc.PrivateKey = HexEncodeKey(privateKey)
|
||||
rc.PublicKey = HexEncodeKey(privateKey.Public().(ed25519.PublicKey))
|
||||
|
||||
resolverSk, resolverPk := generateRandomKeyPair()
|
||||
rc.ResolverSk = HexEncodeKey(resolverSk[:])
|
||||
rc.ResolverPk = HexEncodeKey(resolverPk[:])
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
// HexEncodeKey encodes a byte slice to a hex-encoded string.
|
||||
func HexEncodeKey(b []byte) string {
|
||||
return strings.ToUpper(hex.EncodeToString(b))
|
||||
}
|
||||
|
||||
// HexDecodeKey decodes a hex-encoded string with (optional) colons
|
||||
// to a byte array.
|
||||
func HexDecodeKey(str string) ([]byte, error) {
|
||||
return hex.DecodeString(strings.ReplaceAll(str, ":", ""))
|
||||
}
|
||||
|
||||
// generateRandomKeyPair generates a random key-pair
|
||||
func generateRandomKeyPair() (privateKey [keySize]byte, publicKey [keySize]byte) {
|
||||
privateKey = [keySize]byte{}
|
||||
publicKey = [keySize]byte{}
|
||||
|
||||
_, _ = rand.Read(privateKey[:])
|
||||
curve25519.ScalarBaseMult(&publicKey, &privateKey)
|
||||
return
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const defaultTimeout = 10 * time.Second
|
||||
|
||||
// Handler is implemented by any value that implements ServeDNS.
|
||||
type Handler interface {
|
||||
ServeDNS(rw ResponseWriter, r *dns.Msg) error
|
||||
}
|
||||
|
||||
// ResponseWriter is the interface that needs to be implemented for different protocols
|
||||
type ResponseWriter interface {
|
||||
LocalAddr() net.Addr // LocalAddr - local socket address
|
||||
RemoteAddr() net.Addr // RemoteAddr - remote client socket address
|
||||
WriteMsg(m *dns.Msg) error // WriteMsg - writes response message to the client
|
||||
}
|
||||
|
||||
// DefaultHandler is the default Handler implementation
|
||||
// that is used by Server if custom handler is not configured
|
||||
var DefaultHandler Handler = &defaultHandler{
|
||||
udpClient: &dns.Client{
|
||||
Net: "udp",
|
||||
Timeout: defaultTimeout,
|
||||
},
|
||||
tcpClient: &dns.Client{
|
||||
Net: "tcp",
|
||||
Timeout: defaultTimeout,
|
||||
},
|
||||
addr: "94.140.14.140:53",
|
||||
}
|
||||
|
||||
type defaultHandler struct {
|
||||
udpClient *dns.Client
|
||||
tcpClient *dns.Client
|
||||
addr string
|
||||
}
|
||||
|
||||
// ServeDNS implements Handler interface
|
||||
func (h *defaultHandler) ServeDNS(rw ResponseWriter, r *dns.Msg) error {
|
||||
// Google DNS
|
||||
res, _, err := h.udpClient.Exchange(r, h.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if res.Truncated {
|
||||
res, _, err = h.tcpClient.Exchange(r, h.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return rw.WriteMsg(res)
|
||||
}
|
||||
-315
@@ -1,315 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// default read timeout for all reads
|
||||
const defaultReadTimeout = 2 * time.Second
|
||||
|
||||
// in case of TCP we only use defaultReadTimeout for the first read
|
||||
// then we start using defaultTCPIdleTimeout
|
||||
const defaultTCPIdleTimeout = 8 * time.Second
|
||||
|
||||
// defaultUDPSize is the default size of the UDP read buffer. The release notes
|
||||
// for dnscrypt-proxy version 1.1.0-RC1 claim that this size was chosen as the
|
||||
// maximum one "for compatibility with some scary network setups", and making it
|
||||
// smaller seems to break things for some people.
|
||||
//
|
||||
// See also: https://github.com/AdguardTeam/AdGuardDNS/issues/188.
|
||||
const defaultUDPSize = 1252
|
||||
|
||||
// helper struct that is used in several SetReadDeadline calls
|
||||
var longTimeAgo = time.Unix(1, 0)
|
||||
|
||||
// ServerDNSCrypt is an interface for a DNSCrypt server
|
||||
type ServerDNSCrypt interface {
|
||||
// ServeTCP listens to TCP connections, queries are then processed by Server.Handler.
|
||||
// It blocks the calling goroutine and to stop it you need to close the listener
|
||||
// or call ServerDNSCrypt.Shutdown.
|
||||
ServeTCP(l net.Listener) error
|
||||
|
||||
// ServeUDP listens to UDP connections, queries are then processed by Server.Handler.
|
||||
// It blocks the calling goroutine and to stop it you need to close the listener
|
||||
// or call ServerDNSCrypt.Shutdown.
|
||||
ServeUDP(l *net.UDPConn) error
|
||||
|
||||
// Shutdown tries to gracefully shutdown the server. It waits until all
|
||||
// connections are processed and only after that it leaves the method.
|
||||
// If context deadline is specified, it will exit earlier
|
||||
// or call ServerDNSCrypt.Shutdown.
|
||||
Shutdown(ctx context.Context) error
|
||||
}
|
||||
|
||||
// Server is a simple DNSCrypt server implementation
|
||||
type Server struct {
|
||||
// ProviderName is a DNSCrypt provider name
|
||||
ProviderName string
|
||||
|
||||
// ResolverCert contains resolver certificate.
|
||||
ResolverCert *Cert
|
||||
|
||||
// UDPSize is the default buffer size to use to read incoming UDP messages.
|
||||
// If not set it defaults to defaultUDPSize (1252 B).
|
||||
UDPSize int
|
||||
|
||||
// Handler to invoke. If nil, uses DefaultHandler.
|
||||
Handler Handler
|
||||
|
||||
// make sure init is called only once
|
||||
initOnce sync.Once
|
||||
|
||||
// Shutdown handling
|
||||
// --
|
||||
lock sync.RWMutex // protects access to all the fields below
|
||||
started bool
|
||||
wg sync.WaitGroup // active workers (servers)
|
||||
tcpListeners map[net.Listener]struct{} // track active TCP listeners
|
||||
udpListeners map[*net.UDPConn]struct{} // track active UDP listeners
|
||||
tcpConns map[net.Conn]struct{} // track active connections
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ ServerDNSCrypt = &Server{}
|
||||
|
||||
// prepareShutdown - prepares the server to shutdown:
|
||||
// unblocks reads from all connections related to this server
|
||||
// marks the server as stopped
|
||||
// if the server is not started, returns ErrServerNotStarted
|
||||
func (s *Server) prepareShutdown() error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if !s.started {
|
||||
log.Info("Server is not started")
|
||||
return ErrServerNotStarted
|
||||
}
|
||||
|
||||
s.started = false
|
||||
|
||||
// These listeners were passed to us from the outside so we cannot close
|
||||
// them here - this is up to the calling code to do that. Instead of that,
|
||||
// we call Set(Read)Deadline to unblock goroutines that are currently
|
||||
// blocked on reading from those listeners.
|
||||
// For tcpConns we would like to avoid closing them to be able to process
|
||||
// queries before shutting everything down.
|
||||
|
||||
// Unblock reads for all active tcpConns
|
||||
for conn := range s.tcpConns {
|
||||
_ = conn.SetReadDeadline(longTimeAgo)
|
||||
}
|
||||
|
||||
// Unblock reads for all active TCP listeners
|
||||
for l := range s.tcpListeners {
|
||||
switch v := l.(type) {
|
||||
case *net.TCPListener:
|
||||
_ = v.SetDeadline(longTimeAgo)
|
||||
}
|
||||
}
|
||||
|
||||
// Unblock reads for all active UDP listeners
|
||||
for l := range s.udpListeners {
|
||||
_ = l.SetReadDeadline(longTimeAgo)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown tries to gracefully shutdown the server. It waits until all
|
||||
// connections are processed and only after that it leaves the method.
|
||||
// If context deadline is specified, it will exit earlier.
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
log.Info("Shutting down the DNSCrypt server")
|
||||
|
||||
err := s.prepareShutdown()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Using this channel to wait until all goroutines finish their work
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
s.wg.Wait()
|
||||
log.Info("Serve goroutines finished their work")
|
||||
close(closed)
|
||||
}()
|
||||
|
||||
// Wait for either all goroutines finish their work
|
||||
// Or for the context deadline
|
||||
select {
|
||||
case <-closed:
|
||||
log.Info("DNSCrypt server has been stopped")
|
||||
case <-ctx.Done():
|
||||
log.Info("DNSCrypt server shutdown has timed out")
|
||||
err = ctx.Err()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// init initializes (lazily) Server properties on startup
|
||||
// this method is called from Server.ServeTCP and Server.ServeUDP
|
||||
func (s *Server) init() {
|
||||
s.tcpConns = map[net.Conn]struct{}{}
|
||||
s.udpListeners = map[*net.UDPConn]struct{}{}
|
||||
s.tcpListeners = map[net.Listener]struct{}{}
|
||||
|
||||
if s.UDPSize == 0 {
|
||||
s.UDPSize = defaultUDPSize
|
||||
}
|
||||
}
|
||||
|
||||
// isStarted returns true if the server is processing queries right now
|
||||
// it means that Server.ServeTCP and/or Server.ServeUDP have been called
|
||||
func (s *Server) isStarted() bool {
|
||||
s.lock.RLock()
|
||||
started := s.started
|
||||
s.lock.RUnlock()
|
||||
return started
|
||||
}
|
||||
|
||||
// serveDNS serves a DNS response
|
||||
func (s *Server) serveDNS(rw ResponseWriter, r *dns.Msg) error {
|
||||
if r == nil || len(r.Question) != 1 || r.Response {
|
||||
return ErrInvalidQuery
|
||||
}
|
||||
|
||||
log.Tracef("Handling a DNS query: %s", r.Question[0].Name)
|
||||
|
||||
handler := s.Handler
|
||||
if handler == nil {
|
||||
handler = DefaultHandler
|
||||
}
|
||||
|
||||
err := handler.ServeDNS(rw, r)
|
||||
if err != nil {
|
||||
log.Tracef("Error while handing a DNS query: %v", err)
|
||||
|
||||
reply := &dns.Msg{}
|
||||
reply.SetRcode(r, dns.RcodeServerFailure)
|
||||
_ = rw.WriteMsg(reply)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// encrypt encrypts DNSCrypt response
|
||||
func (s *Server) encrypt(m *dns.Msg, q EncryptedQuery) ([]byte, error) {
|
||||
r := EncryptedResponse{
|
||||
EsVersion: q.EsVersion,
|
||||
Nonce: q.Nonce,
|
||||
}
|
||||
packet, err := m.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sharedKey, err := computeSharedKey(q.EsVersion, &s.ResolverCert.ResolverSk, &q.ClientPk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.Encrypt(packet, sharedKey)
|
||||
}
|
||||
|
||||
// decrypt decrypts the incoming message and returns a DNS message to process
|
||||
func (s *Server) decrypt(b []byte) (*dns.Msg, EncryptedQuery, error) {
|
||||
q := EncryptedQuery{
|
||||
EsVersion: s.ResolverCert.EsVersion,
|
||||
ClientMagic: s.ResolverCert.ClientMagic,
|
||||
}
|
||||
msg, err := q.Decrypt(b, s.ResolverCert.ResolverSk)
|
||||
if err != nil {
|
||||
// Failed to decrypt, dropping it
|
||||
return nil, q, err
|
||||
}
|
||||
|
||||
r := new(dns.Msg)
|
||||
err = r.Unpack(msg)
|
||||
if err != nil {
|
||||
// Invalid DNS message, ignore
|
||||
return nil, q, err
|
||||
}
|
||||
|
||||
return r, q, nil
|
||||
}
|
||||
|
||||
// handleHandshake handles a TXT request that requests certificate data
|
||||
func (s *Server) handleHandshake(b []byte, certTxt string) ([]byte, error) {
|
||||
m := new(dns.Msg)
|
||||
err := m.Unpack(b)
|
||||
if err != nil {
|
||||
// Not a handshake, just ignore it
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(m.Question) != 1 || m.Response {
|
||||
// Invalid query
|
||||
return nil, ErrInvalidQuery
|
||||
}
|
||||
|
||||
q := m.Question[0]
|
||||
providerName := dns.Fqdn(s.ProviderName)
|
||||
qName := strings.ToLower(q.Name) // important, may be random case
|
||||
if q.Qtype != dns.TypeTXT || qName != providerName {
|
||||
// Invalid provider name or type, doing nothing
|
||||
return nil, ErrInvalidQuery
|
||||
}
|
||||
|
||||
reply := new(dns.Msg)
|
||||
reply.SetReply(m)
|
||||
txt := &dns.TXT{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeTXT,
|
||||
Ttl: 60, // use 60 seconds by default, but it shouldn't matter
|
||||
Class: dns.ClassINET,
|
||||
},
|
||||
Txt: []string{
|
||||
certTxt,
|
||||
},
|
||||
}
|
||||
reply.Answer = append(reply.Answer, txt)
|
||||
|
||||
// These bits are important for the old dnscrypt-proxy versions
|
||||
reply.Authoritative = true
|
||||
reply.RecursionAvailable = true
|
||||
return reply.Pack()
|
||||
}
|
||||
|
||||
// validate checks if the Server config is properly set
|
||||
func (s *Server) validate() bool {
|
||||
if s.ResolverCert == nil {
|
||||
log.Error("ResolverCert must be set")
|
||||
return false
|
||||
}
|
||||
|
||||
if !s.ResolverCert.VerifyDate() {
|
||||
log.Error("ResolverCert date is not valid")
|
||||
return false
|
||||
}
|
||||
|
||||
if s.ProviderName == "" {
|
||||
log.Error("ProviderName must be set")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// getCertTXT serializes the cert TXT record that are to be sent to the client
|
||||
func (s *Server) getCertTXT() (string, error) {
|
||||
certBuf, err := s.ResolverCert.Serialize()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
certTxt := packTxtString(certBuf)
|
||||
return certTxt, nil
|
||||
}
|
||||
-219
@@ -1,219 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// TCPResponseWriter is the ResponseWriter implementation for TCP
|
||||
type TCPResponseWriter struct {
|
||||
tcpConn net.Conn
|
||||
encrypt encryptionFunc
|
||||
req *dns.Msg
|
||||
query EncryptedQuery
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ ResponseWriter = &TCPResponseWriter{}
|
||||
|
||||
// LocalAddr is the server socket local address
|
||||
func (w *TCPResponseWriter) LocalAddr() net.Addr {
|
||||
return w.tcpConn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr is the client's address
|
||||
func (w *TCPResponseWriter) RemoteAddr() net.Addr {
|
||||
return w.tcpConn.RemoteAddr()
|
||||
}
|
||||
|
||||
// WriteMsg writes DNS message to the client
|
||||
func (w *TCPResponseWriter) WriteMsg(m *dns.Msg) error {
|
||||
normalize("tcp", w.req, m)
|
||||
|
||||
res, err := w.encrypt(m, w.query)
|
||||
if err != nil {
|
||||
log.Tracef("Failed to encrypt the DNS query: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return writePrefixed(res, w.tcpConn)
|
||||
}
|
||||
|
||||
// ServeTCP listens to TCP connections, queries are then processed by Server.Handler.
|
||||
// It blocks the calling goroutine and to stop it you need to close the listener
|
||||
// or call Server.Shutdown.
|
||||
func (s *Server) ServeTCP(l net.Listener) error {
|
||||
err := s.prepareServeTCP(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Entering DNSCrypt TCP listening loop tcp://%s", l.Addr())
|
||||
|
||||
// Tracks TCP connection handling goroutines
|
||||
tcpWg := &sync.WaitGroup{}
|
||||
defer s.cleanUpTCP(tcpWg, l)
|
||||
|
||||
// Track active goroutine
|
||||
s.wg.Add(1)
|
||||
|
||||
// Serialize the cert right away and prepare it to be sent to the client
|
||||
certTxt, err := s.getCertTXT()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for s.isStarted() {
|
||||
conn, err := l.Accept()
|
||||
|
||||
// Check the error code and exit loop if necessary
|
||||
if err != nil {
|
||||
if !s.isStarted() {
|
||||
// Stopped gracefully
|
||||
break
|
||||
}
|
||||
var netErr net.Error
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
// Note that timeout errors will be here (i.e. hitting ReadDeadline)
|
||||
continue
|
||||
}
|
||||
if isConnClosed(err) {
|
||||
log.Info("udpListen.ReadFrom() returned because we're reading from a closed connection, exiting loop")
|
||||
} else {
|
||||
log.Info("got error when reading from UDP listen: %s", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// If we got here, the connection is alive
|
||||
s.lock.Lock()
|
||||
// Track the connection to allow unblocking reads on shutdown.
|
||||
s.tcpConns[conn] = struct{}{}
|
||||
s.lock.Unlock()
|
||||
|
||||
tcpWg.Add(1)
|
||||
go func() {
|
||||
// Ignore error here, it is most probably a legit one
|
||||
// if not, it's written to the debug log
|
||||
_ = s.handleTCPConnection(conn, certTxt)
|
||||
|
||||
// Clean up
|
||||
_ = conn.Close()
|
||||
s.lock.Lock()
|
||||
delete(s.tcpConns, conn)
|
||||
s.lock.Unlock()
|
||||
tcpWg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepareServeTCP prepares the server and listener to serving DNSCrypt
|
||||
func (s *Server) prepareServeTCP(l net.Listener) error {
|
||||
// Check that server is properly configured
|
||||
if !s.validate() {
|
||||
return ErrServerConfig
|
||||
}
|
||||
|
||||
// Protect shutdown-related fields
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
s.initOnce.Do(s.init)
|
||||
|
||||
// Mark the server as started if needed
|
||||
s.started = true
|
||||
|
||||
// Track an active TCP listener
|
||||
s.tcpListeners[l] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanUpTCP waits until all TCP messages before cleaning up
|
||||
func (s *Server) cleanUpTCP(tcpWg *sync.WaitGroup, l net.Listener) {
|
||||
// Wait until all TCP connections are processed
|
||||
tcpWg.Wait()
|
||||
|
||||
// Not using it anymore so can be removed from the active listeners
|
||||
s.lock.Lock()
|
||||
delete(s.tcpListeners, l)
|
||||
s.lock.Unlock()
|
||||
|
||||
// The work is finished
|
||||
s.wg.Done()
|
||||
}
|
||||
|
||||
// handleTCPMsg handles a single TCP message. If this method returns error
|
||||
// the connection will be closed
|
||||
func (s *Server) handleTCPMsg(b []byte, conn net.Conn, certTxt string) error {
|
||||
if len(b) < minDNSPacketSize {
|
||||
// Ignore the packets that are too short
|
||||
return ErrTooShort
|
||||
}
|
||||
|
||||
// First of all, check for "ClientMagic" in the incoming query
|
||||
if !bytes.Equal(b[:clientMagicSize], s.ResolverCert.ClientMagic[:]) {
|
||||
// If there's no ClientMagic in the packet, we assume this
|
||||
// is a plain DNS query requesting the certificate data
|
||||
reply, err := s.handleHandshake(b, certTxt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process a plain DNS query: %w", err)
|
||||
}
|
||||
err = writePrefixed(reply, conn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write a response: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we got here, this is an encrypted DNSCrypt message
|
||||
// We should decrypt it first to get the plain DNS query
|
||||
m, q, err := s.decrypt(b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decrypt incoming message: %w", err)
|
||||
}
|
||||
rw := &TCPResponseWriter{
|
||||
tcpConn: conn,
|
||||
encrypt: s.encrypt,
|
||||
req: m,
|
||||
query: q,
|
||||
}
|
||||
err = s.serveDNS(rw, m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process a DNS query: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleTCPConnection handles all queries that are coming to the
|
||||
// specified TCP connection.
|
||||
func (s *Server) handleTCPConnection(conn net.Conn, certTxt string) error {
|
||||
timeout := defaultReadTimeout
|
||||
|
||||
for s.isStarted() {
|
||||
_ = conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
|
||||
b, err := readPrefixed(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.handleTCPMsg(b, conn, certTxt)
|
||||
if err != nil {
|
||||
log.Debug("failed to process DNS query: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
timeout = defaultTCPIdleTimeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
-222
@@ -1,222 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
type encryptionFunc func(m *dns.Msg, q EncryptedQuery) ([]byte, error)
|
||||
|
||||
// UDPResponseWriter is the ResponseWriter implementation for UDP
|
||||
type UDPResponseWriter struct {
|
||||
udpConn *net.UDPConn // UDP connection
|
||||
sess *dns.SessionUDP // SessionUDP (necessary to use dns.WriteToSessionUDP)
|
||||
encrypt encryptionFunc // DNSCrypt encryption function
|
||||
req *dns.Msg // DNS query that was processed
|
||||
query EncryptedQuery // DNSCrypt query properties
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ ResponseWriter = &UDPResponseWriter{}
|
||||
|
||||
// LocalAddr is the server socket local address
|
||||
func (w *UDPResponseWriter) LocalAddr() net.Addr {
|
||||
return w.udpConn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr is the client's address
|
||||
func (w *UDPResponseWriter) RemoteAddr() net.Addr {
|
||||
return w.sess.RemoteAddr()
|
||||
}
|
||||
|
||||
// WriteMsg writes DNS message to the client
|
||||
func (w *UDPResponseWriter) WriteMsg(m *dns.Msg) error {
|
||||
normalize("udp", w.req, m)
|
||||
|
||||
res, err := w.encrypt(m, w.query)
|
||||
if err != nil {
|
||||
log.Tracef("Failed to encrypt the DNS query: %v", err)
|
||||
return err
|
||||
}
|
||||
_, err = dns.WriteToSessionUDP(w.udpConn, res, w.sess)
|
||||
return err
|
||||
}
|
||||
|
||||
// ServeUDP listens to UDP connections, queries are then processed by Server.Handler.
|
||||
// It blocks the calling goroutine and to stop it you need to close the listener
|
||||
// or call Server.Shutdown.
|
||||
func (s *Server) ServeUDP(l *net.UDPConn) error {
|
||||
err := s.prepareServeUDP(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Tracks UDP handling goroutines
|
||||
udpWg := &sync.WaitGroup{}
|
||||
defer s.cleanUpUDP(udpWg, l)
|
||||
|
||||
// Track active goroutine
|
||||
s.wg.Add(1)
|
||||
|
||||
log.Info("Entering DNSCrypt UDP listening loop on udp://%s", l.LocalAddr())
|
||||
|
||||
// Serialize the cert right away and prepare it to be sent to the client
|
||||
certTxt, err := s.getCertTXT()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for s.isStarted() {
|
||||
b, sess, err := s.readUDPMsg(l)
|
||||
|
||||
// Check the error code and exit loop if necessary
|
||||
if err != nil {
|
||||
if !s.isStarted() {
|
||||
// Stopped gracefully
|
||||
return nil
|
||||
}
|
||||
var netErr net.Error
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
// Note that timeout errors will be here (i.e. hitting ReadDeadline)
|
||||
continue
|
||||
}
|
||||
if isConnClosed(err) {
|
||||
log.Info("udpListen.ReadFrom() returned because we're reading from a closed connection, exiting loop")
|
||||
} else {
|
||||
log.Info("got error when reading from UDP listen: %s", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if len(b) < minDNSPacketSize {
|
||||
// Ignore the packets that are too short
|
||||
continue
|
||||
}
|
||||
|
||||
udpWg.Add(1)
|
||||
go func() {
|
||||
s.serveUDPMsg(b, certTxt, sess, l)
|
||||
udpWg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepareServeUDP prepares the server and listener to serving DNSCrypt
|
||||
func (s *Server) prepareServeUDP(l *net.UDPConn) error {
|
||||
// Check that server is properly configured
|
||||
if !s.validate() {
|
||||
return ErrServerConfig
|
||||
}
|
||||
|
||||
// set UDP options to allow receiving OOB data
|
||||
err := setUDPSocketOptions(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Protect shutdown-related fields
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
s.initOnce.Do(s.init)
|
||||
|
||||
// Mark the server as started.
|
||||
// Note that we don't check if it was started before as
|
||||
// Serve* methods can be called multiple times.
|
||||
s.started = true
|
||||
|
||||
// Track an active UDP listener
|
||||
s.udpListeners[l] = struct{}{}
|
||||
return err
|
||||
}
|
||||
|
||||
// cleanUpUDP waits until all UDP messages before cleaning up
|
||||
func (s *Server) cleanUpUDP(udpWg *sync.WaitGroup, l *net.UDPConn) {
|
||||
// Wait until UDP messages are processed
|
||||
udpWg.Wait()
|
||||
|
||||
// Not using it anymore so can be removed from the active listeners
|
||||
s.lock.Lock()
|
||||
delete(s.udpListeners, l)
|
||||
s.lock.Unlock()
|
||||
|
||||
// The work is finished
|
||||
s.wg.Done()
|
||||
}
|
||||
|
||||
// readUDPMsg reads incoming UDP message
|
||||
func (s *Server) readUDPMsg(l *net.UDPConn) ([]byte, *dns.SessionUDP, error) {
|
||||
_ = l.SetReadDeadline(time.Now().Add(defaultReadTimeout))
|
||||
b := make([]byte, s.UDPSize)
|
||||
n, sess, err := dns.ReadFromSessionUDP(l, b)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return b[:n], sess, err
|
||||
}
|
||||
|
||||
// serveUDPMsg handles incoming DNS message
|
||||
func (s *Server) serveUDPMsg(b []byte, certTxt string, sess *dns.SessionUDP, l *net.UDPConn) {
|
||||
// First of all, check for "ClientMagic" in the incoming query
|
||||
if !bytes.Equal(b[:clientMagicSize], s.ResolverCert.ClientMagic[:]) {
|
||||
// If there's no ClientMagic in the packet, we assume this
|
||||
// is a plain DNS query requesting the certificate data
|
||||
reply, err := s.handleHandshake(b, certTxt)
|
||||
if err != nil {
|
||||
log.Tracef("failed to process a plain DNS query: %v", err)
|
||||
}
|
||||
if err == nil {
|
||||
// Ignore errors, we don't care and can't handle them anyway
|
||||
_, _ = dns.WriteToSessionUDP(l, reply, sess)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// If we got here, this is an encrypted DNSCrypt message
|
||||
// We should decrypt it first to get the plain DNS query
|
||||
m, q, err := s.decrypt(b)
|
||||
if err == nil {
|
||||
rw := &UDPResponseWriter{
|
||||
udpConn: l,
|
||||
sess: sess,
|
||||
encrypt: s.encrypt,
|
||||
req: m,
|
||||
query: q,
|
||||
}
|
||||
err = s.serveDNS(rw, m)
|
||||
if err != nil {
|
||||
log.Tracef("failed to process a DNS query: %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Tracef("failed to decrypt incoming message len=%d: %v", len(b), err)
|
||||
}
|
||||
}
|
||||
|
||||
// setUDPSocketOptions method is necessary to be able to use dns.ReadFromSessionUDP / dns.WriteToSessionUDP
|
||||
func setUDPSocketOptions(conn *net.UDPConn) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We don't know if this a IPv4-only, IPv6-only or a IPv4-and-IPv6 connection.
|
||||
// Try enabling receiving of ECN and packet info for both IP versions.
|
||||
// We expect at least one of those syscalls to succeed.
|
||||
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
|
||||
}
|
||||
-270
@@ -1,270 +0,0 @@
|
||||
package dnscrypt
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/ameshkov/dnscrypt/v2/xsecretbox"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
)
|
||||
|
||||
// Prior to encryption, queries are padded using the ISO/IEC 7816-4
|
||||
// format. The padding starts with a byte valued 0x80 followed by a
|
||||
// variable number of NUL bytes.
|
||||
//
|
||||
// ## Padding for client queries over UDP
|
||||
//
|
||||
// <client-query> <client-query-pad> must be at least <min-query-len>
|
||||
// bytes. If the length of the client query is less than <min-query-len>,
|
||||
// the padding length must be adjusted in order to satisfy this
|
||||
// requirement.
|
||||
//
|
||||
// <min-query-len> is a variable length, initially set to 256 bytes, and
|
||||
// must be a multiple of 64 bytes.
|
||||
//
|
||||
// ## Padding for client queries over TCP
|
||||
//
|
||||
// The length of <client-query-pad> is randomly chosen between 1 and 256
|
||||
// bytes (including the leading 0x80), but the total length of <client-query>
|
||||
// <client-query-pad> must be a multiple of 64 bytes.
|
||||
//
|
||||
// For example, an originally unpadded 56-bytes DNS query can be padded as:
|
||||
//
|
||||
// <56-bytes-query> 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
// or
|
||||
// <56-bytes-query> 0x80 (0x00 * 71)
|
||||
// or
|
||||
// <56-bytes-query> 0x80 (0x00 * 135)
|
||||
// or
|
||||
// <56-bytes-query> 0x80 (0x00 * 199)
|
||||
func pad(packet []byte) []byte {
|
||||
// get closest divisible by 64 to <packet-len> + 1 byte for 0x80
|
||||
minQuestionSize := (len(packet)+1+63)/64 + 64
|
||||
|
||||
// padded size can't be less than minUDPQuestionSize
|
||||
minQuestionSize = max(minUDPQuestionSize, minQuestionSize)
|
||||
|
||||
packet = append(packet, 0x80)
|
||||
for len(packet) < minQuestionSize {
|
||||
packet = append(packet, 0)
|
||||
}
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
// unpad - removes padding bytes
|
||||
func unpad(packet []byte) ([]byte, error) {
|
||||
for i := len(packet); ; {
|
||||
if i == 0 {
|
||||
return nil, ErrInvalidPadding
|
||||
}
|
||||
i--
|
||||
if packet[i] == 0x80 {
|
||||
if i < minDNSPacketSize {
|
||||
return nil, ErrInvalidPadding
|
||||
}
|
||||
|
||||
return packet[:i], nil
|
||||
} else if packet[i] != 0x00 {
|
||||
return nil, ErrInvalidPadding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// computeSharedKey - computes a shared key
|
||||
func computeSharedKey(cryptoConstruction CryptoConstruction, secretKey *[keySize]byte, publicKey *[keySize]byte) ([keySize]byte, error) {
|
||||
if cryptoConstruction == XChacha20Poly1305 {
|
||||
sharedKey, err := xsecretbox.SharedKey(*secretKey, *publicKey)
|
||||
if err != nil {
|
||||
return sharedKey, err
|
||||
}
|
||||
return sharedKey, nil
|
||||
} else if cryptoConstruction == XSalsa20Poly1305 {
|
||||
sharedKey := [sharedKeySize]byte{}
|
||||
box.Precompute(&sharedKey, publicKey, secretKey)
|
||||
return sharedKey, nil
|
||||
}
|
||||
return [keySize]byte{}, ErrEsVersion
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func isDigit(b byte) bool { return b >= '0' && b <= '9' }
|
||||
|
||||
func dddToByte(s []byte) byte {
|
||||
return (s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')
|
||||
}
|
||||
|
||||
const (
|
||||
escapedByteSmall = "" +
|
||||
`\000\001\002\003\004\005\006\007\008\009` +
|
||||
`\010\011\012\013\014\015\016\017\018\019` +
|
||||
`\020\021\022\023\024\025\026\027\028\029` +
|
||||
`\030\031`
|
||||
escapedByteLarge = `\127\128\129` +
|
||||
`\130\131\132\133\134\135\136\137\138\139` +
|
||||
`\140\141\142\143\144\145\146\147\148\149` +
|
||||
`\150\151\152\153\154\155\156\157\158\159` +
|
||||
`\160\161\162\163\164\165\166\167\168\169` +
|
||||
`\170\171\172\173\174\175\176\177\178\179` +
|
||||
`\180\181\182\183\184\185\186\187\188\189` +
|
||||
`\190\191\192\193\194\195\196\197\198\199` +
|
||||
`\200\201\202\203\204\205\206\207\208\209` +
|
||||
`\210\211\212\213\214\215\216\217\218\219` +
|
||||
`\220\221\222\223\224\225\226\227\228\229` +
|
||||
`\230\231\232\233\234\235\236\237\238\239` +
|
||||
`\240\241\242\243\244\245\246\247\248\249` +
|
||||
`\250\251\252\253\254\255`
|
||||
)
|
||||
|
||||
// escapeByte returns the \DDD escaping of b which must
|
||||
// satisfy b < ' ' || b > '~'.
|
||||
func escapeByte(b byte) string {
|
||||
if b < ' ' {
|
||||
return escapedByteSmall[b*4 : b*4+4]
|
||||
}
|
||||
|
||||
b -= '~' + 1
|
||||
// The cast here is needed as b*4 may overflow byte.
|
||||
return escapedByteLarge[int(b)*4 : int(b)*4+4]
|
||||
}
|
||||
|
||||
func packTxtString(buf []byte) string {
|
||||
var out strings.Builder
|
||||
out.Grow(3 + len(buf))
|
||||
for i := 0; i < len(buf); i++ {
|
||||
b := buf[i]
|
||||
switch {
|
||||
case b == '"' || b == '\\':
|
||||
out.WriteByte('\\')
|
||||
out.WriteByte(b)
|
||||
case b < ' ' || b > '~':
|
||||
out.WriteString(escapeByte(b))
|
||||
default:
|
||||
out.WriteByte(b)
|
||||
}
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func unpackTxtString(s string) ([]byte, error) {
|
||||
bs := make([]byte, len(s))
|
||||
msg := make([]byte, 0)
|
||||
copy(bs, s)
|
||||
for i := 0; i < len(bs); i++ {
|
||||
if bs[i] == '\\' {
|
||||
i++
|
||||
if i == len(bs) {
|
||||
break
|
||||
}
|
||||
if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
|
||||
msg = append(msg, dddToByte(bs[i:]))
|
||||
i += 2
|
||||
} else if bs[i] == 't' {
|
||||
msg = append(msg, '\t')
|
||||
} else if bs[i] == 'r' {
|
||||
msg = append(msg, '\r')
|
||||
} else if bs[i] == 'n' {
|
||||
msg = append(msg, '\n')
|
||||
} else {
|
||||
msg = append(msg, bs[i])
|
||||
}
|
||||
} else {
|
||||
msg = append(msg, bs[i])
|
||||
}
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// normalize truncates the DNS response if needed depending on the protocol
|
||||
func normalize(proto string, req *dns.Msg, res *dns.Msg) {
|
||||
size := dnsSize(proto, req)
|
||||
// DNSCrypt encryption adds a header to each message, we should
|
||||
// consider this when truncating a message.
|
||||
// 64 should cover all cases
|
||||
size = size - 64
|
||||
|
||||
// Truncate response message
|
||||
res.Truncate(size)
|
||||
|
||||
// In case of UDP it is safer to simply remove all response records
|
||||
// dns.Msg.Truncate method will not consider that we need a response
|
||||
// shorter than dns.MinMsgSize
|
||||
if res.Truncated && proto == "udp" {
|
||||
res.Answer = nil
|
||||
}
|
||||
}
|
||||
|
||||
// dnsSize returns if buffer size *advertised* in the requests OPT record.
|
||||
// Or when the request was over TCP, we return the maximum allowed size of 64K.
|
||||
func dnsSize(proto string, r *dns.Msg) int {
|
||||
size := uint16(0)
|
||||
if o := r.IsEdns0(); o != nil {
|
||||
size = o.UDPSize()
|
||||
}
|
||||
|
||||
if proto != "udp" {
|
||||
return dns.MaxMsgSize
|
||||
}
|
||||
|
||||
if size < dns.MinMsgSize {
|
||||
return dns.MinMsgSize
|
||||
}
|
||||
|
||||
// normalize size
|
||||
return int(size)
|
||||
}
|
||||
|
||||
// readPrefixed -- reads a DNS message with a 2-byte prefix containing message length
|
||||
func readPrefixed(conn net.Conn) ([]byte, error) {
|
||||
l := make([]byte, 2)
|
||||
_, err := conn.Read(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetLen := binary.BigEndian.Uint16(l)
|
||||
if packetLen > dns.MaxMsgSize {
|
||||
return nil, ErrQueryTooLarge
|
||||
}
|
||||
|
||||
buf := make([]byte, packetLen)
|
||||
_, err = io.ReadFull(conn, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// writePrefixed -- write a DNS message to a TCP connection
|
||||
// it first writes a 2-byte prefix followed by the message itself
|
||||
func writePrefixed(b []byte, conn net.Conn) error {
|
||||
l := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(l, uint16(len(b)))
|
||||
_, err := (&net.Buffers{l, b}).WriteTo(conn)
|
||||
return err
|
||||
}
|
||||
|
||||
// isConnClosed - checks if the error signals of a closed server connecting
|
||||
func isConnClosed(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
nerr, ok := err.(*net.OpError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.Contains(nerr.Err.Error(), "use of closed network connection") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
// Package xsecretbox implements encryption/decryption of a message using specified keys
|
||||
package xsecretbox
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
package xsecretbox
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/aead/chacha20/chacha"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
// SharedKey computes a shared secret compatible with the one used by
|
||||
// `crypto_box_xchacha20poly1305`.
|
||||
func SharedKey(secretKey [32]byte, publicKey [32]byte) ([32]byte, error) {
|
||||
var sharedKey [32]byte
|
||||
|
||||
sk, err := curve25519.X25519(secretKey[:], publicKey[:])
|
||||
if err != nil {
|
||||
return sharedKey, err
|
||||
}
|
||||
|
||||
c := byte(0)
|
||||
for i := 0; i < 32; i++ {
|
||||
sharedKey[i] = sk[i]
|
||||
c |= sk[i]
|
||||
}
|
||||
if c == 0 {
|
||||
return sharedKey, errors.New("weak public key")
|
||||
}
|
||||
var nonce [16]byte
|
||||
chacha.HChaCha20(&sharedKey, &nonce, &sharedKey)
|
||||
return sharedKey, nil
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
package xsecretbox
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
|
||||
"github.com/aead/chacha20/chacha"
|
||||
"github.com/aead/poly1305"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeySize is what the name suggests
|
||||
KeySize = 32
|
||||
// NonceSize is what the name suggests
|
||||
NonceSize = 24
|
||||
// TagSize is what the name suggests
|
||||
TagSize = 16
|
||||
)
|
||||
|
||||
// Seal does what the name suggests
|
||||
func Seal(out, nonce, message, key []byte) []byte {
|
||||
if len(nonce) != NonceSize {
|
||||
panic("unsupported nonce size")
|
||||
}
|
||||
if len(key) != KeySize {
|
||||
panic("unsupported key size")
|
||||
}
|
||||
|
||||
var firstBlock [64]byte
|
||||
cipher, _ := chacha.NewCipher(nonce, key, 20)
|
||||
cipher.XORKeyStream(firstBlock[:], firstBlock[:])
|
||||
var polyKey [32]byte
|
||||
copy(polyKey[:], firstBlock[:32])
|
||||
|
||||
ret, out := sliceForAppend(out, TagSize+len(message))
|
||||
firstMessageBlock := message
|
||||
if len(firstMessageBlock) > 32 {
|
||||
firstMessageBlock = firstMessageBlock[:32]
|
||||
}
|
||||
|
||||
tagOut := out
|
||||
out = out[poly1305.TagSize:]
|
||||
for i, x := range firstMessageBlock {
|
||||
out[i] = firstBlock[32+i] ^ x
|
||||
}
|
||||
message = message[len(firstMessageBlock):]
|
||||
ciphertext := out
|
||||
out = out[len(firstMessageBlock):]
|
||||
|
||||
cipher.SetCounter(1)
|
||||
cipher.XORKeyStream(out, message)
|
||||
|
||||
var tag [TagSize]byte
|
||||
hash := poly1305.New(polyKey)
|
||||
_, _ = hash.Write(ciphertext)
|
||||
hash.Sum(tag[:0])
|
||||
copy(tagOut, tag[:])
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Open does what the name suggests
|
||||
func Open(out, nonce, box, key []byte) ([]byte, error) {
|
||||
if len(nonce) != NonceSize {
|
||||
panic("unsupported nonce size")
|
||||
}
|
||||
if len(key) != KeySize {
|
||||
panic("unsupported key size")
|
||||
}
|
||||
if len(box) < TagSize {
|
||||
return nil, errors.New("ciphertext is too short")
|
||||
}
|
||||
|
||||
var firstBlock [64]byte
|
||||
cipher, _ := chacha.NewCipher(nonce, key, 20)
|
||||
cipher.XORKeyStream(firstBlock[:], firstBlock[:])
|
||||
var polyKey [32]byte
|
||||
copy(polyKey[:], firstBlock[:32])
|
||||
|
||||
var tag [TagSize]byte
|
||||
ciphertext := box[TagSize:]
|
||||
hash := poly1305.New(polyKey)
|
||||
_, _ = hash.Write(ciphertext)
|
||||
hash.Sum(tag[:0])
|
||||
if subtle.ConstantTimeCompare(tag[:], box[:TagSize]) != 1 {
|
||||
return nil, errors.New("ciphertext authentication failed")
|
||||
}
|
||||
|
||||
ret, out := sliceForAppend(out, len(ciphertext))
|
||||
|
||||
firstMessageBlock := ciphertext
|
||||
if len(firstMessageBlock) > 32 {
|
||||
firstMessageBlock = firstMessageBlock[:32]
|
||||
}
|
||||
for i, x := range firstMessageBlock {
|
||||
out[i] = firstBlock[32+i] ^ x
|
||||
}
|
||||
ciphertext = ciphertext[len(firstMessageBlock):]
|
||||
out = out[len(firstMessageBlock):]
|
||||
|
||||
cipher.SetCounter(1)
|
||||
cipher.XORKeyStream(out, ciphertext)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 40%
|
||||
threshold: null
|
||||
patch: false
|
||||
changes: false
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
.idea
|
||||
.vscode
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.x
|
||||
|
||||
script:
|
||||
- go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS 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.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
[](https://travis-ci.org/ameshkov/dnsstamps)
|
||||
[](https://codecov.io/github/ameshkov/dnsstamps?branch=master)
|
||||
[](https://goreportcard.com/report/ameshkov/dnsstamps)
|
||||
[](https://godoc.org/github.com/ameshkov/dnsstamps)
|
||||
|
||||
# DNS Stamps
|
||||
|
||||
Implementation of [DNS stamps](https://dnscrypt.info/stamps-specifications/):
|
||||
|
||||
> Server stamps encode all the parameters required to connect to a secure DNS server as a single string.
|
||||
Think about stamps as QR code, but for DNS.
|
||||
|
||||
Half of the code comes from the DNS stamps author: https://github.com/jedisct1/go-dnsstamps
|
||||
|
||||
This library was made for a single purpose - to add missing stamps implementations: plain DNS and DNS-over-TLS.
|
||||
|
||||
TODO: backport to https://github.com/jedisct1/go-dnsstamps
|
||||
-425
@@ -1,425 +0,0 @@
|
||||
package dnsstamps
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDNSCryptPort = 443
|
||||
defaultDoHPort = 443
|
||||
defaultDoTPort = 843
|
||||
defaultDoQPort = 784
|
||||
defaultPlainPort = 53
|
||||
stampProtocol = "sdns://"
|
||||
)
|
||||
|
||||
// ServerInformalProperties represents informal properties about the resolver
|
||||
type ServerInformalProperties uint64
|
||||
|
||||
const (
|
||||
// ServerInformalPropertyDNSSEC means resolver does DNSSEC validation
|
||||
ServerInformalPropertyDNSSEC = ServerInformalProperties(1) << 0
|
||||
// ServerInformalPropertyNoLog means resolver does not record logs
|
||||
ServerInformalPropertyNoLog = ServerInformalProperties(1) << 1
|
||||
// ServerInformalPropertyNoFilter means resolver doesn't intentionally block domains
|
||||
ServerInformalPropertyNoFilter = ServerInformalProperties(1) << 2
|
||||
)
|
||||
|
||||
// StampProtoType is a stamp protocol type
|
||||
type StampProtoType uint8
|
||||
|
||||
const (
|
||||
// StampProtoTypePlain is plain DNS
|
||||
StampProtoTypePlain = StampProtoType(0x00)
|
||||
// StampProtoTypeDNSCrypt is DNSCrypt
|
||||
StampProtoTypeDNSCrypt = StampProtoType(0x01)
|
||||
// StampProtoTypeDoH is DNS-over-HTTPS
|
||||
StampProtoTypeDoH = StampProtoType(0x02)
|
||||
// StampProtoTypeTLS is DNS-over-TLS
|
||||
StampProtoTypeTLS = StampProtoType(0x03)
|
||||
// StampProtoTypeDoQ is DNS-over-QUIC
|
||||
StampProtoTypeDoQ = StampProtoType(0x04)
|
||||
)
|
||||
|
||||
func (stampProtoType *StampProtoType) String() string {
|
||||
switch *stampProtoType {
|
||||
case StampProtoTypePlain:
|
||||
return "Plain"
|
||||
case StampProtoTypeDNSCrypt:
|
||||
return "DNSCrypt"
|
||||
case StampProtoTypeDoH:
|
||||
return "DoH"
|
||||
case StampProtoTypeTLS:
|
||||
return "DoT"
|
||||
case StampProtoTypeDoQ:
|
||||
return "DoQ"
|
||||
default:
|
||||
panic("Unexpected protocol")
|
||||
}
|
||||
}
|
||||
|
||||
// ServerStamp is the DNS stamp representation
|
||||
type ServerStamp struct {
|
||||
ServerAddrStr string // Server address with port
|
||||
ServerPk []uint8 // the DNSCrypt provider’s Ed25519 public key, as 32 raw bytes. Empty for other types.
|
||||
|
||||
// Hash is the SHA256 digest of one of the TBS certificate found in the validation chain,
|
||||
// typically the certificate used to sign the resolver’s certificate. Multiple hashes can
|
||||
// be provided for seamless rotations.
|
||||
Hashes [][]uint8
|
||||
|
||||
// Provider means different things depending on the stamp type
|
||||
// DNSCrypt: the DNSCrypt provider name
|
||||
// DOH and DOT: server's hostname
|
||||
// Plain DNS: not specified
|
||||
ProviderName string
|
||||
|
||||
Path string // Path is the HTTP path, and it has a meaning for DoH stamps only
|
||||
Props ServerInformalProperties // Server properties (DNSSec, NoLog, NoFilter)
|
||||
Proto StampProtoType // Stamp protocol
|
||||
}
|
||||
|
||||
// NewServerStampFromString creates a new DNS stamp from the stamp string
|
||||
func NewServerStampFromString(stampStr string) (ServerStamp, error) {
|
||||
if !strings.HasPrefix(stampStr, stampProtocol) {
|
||||
return ServerStamp{}, fmt.Errorf("stamps are expected to start with %s", stampProtocol)
|
||||
}
|
||||
bin, err := base64.RawURLEncoding.DecodeString(stampStr[len(stampProtocol):])
|
||||
if err != nil {
|
||||
return ServerStamp{}, err
|
||||
}
|
||||
if len(bin) < 1 {
|
||||
return ServerStamp{}, errors.New("stamp is too short")
|
||||
}
|
||||
|
||||
if bin[0] == uint8(StampProtoTypePlain) {
|
||||
return newPlainServerStamp(bin)
|
||||
} else if bin[0] == uint8(StampProtoTypeDNSCrypt) {
|
||||
return newDNSCryptServerStamp(bin)
|
||||
} else if bin[0] == uint8(StampProtoTypeDoH) {
|
||||
return newDoHServerStamp(bin)
|
||||
} else if bin[0] == uint8(StampProtoTypeTLS) {
|
||||
return newDoTOrDoQServerStamp(bin, StampProtoTypeTLS, defaultDoTPort)
|
||||
} else if bin[0] == uint8(StampProtoTypeDoQ) {
|
||||
return newDoTOrDoQServerStamp(bin, StampProtoTypeDoQ, defaultDoQPort)
|
||||
}
|
||||
return ServerStamp{}, errors.New("unsupported stamp version or protocol")
|
||||
}
|
||||
|
||||
func (stamp *ServerStamp) String() string {
|
||||
|
||||
switch stamp.Proto {
|
||||
case StampProtoTypeDNSCrypt:
|
||||
return stamp.dnsCryptString()
|
||||
case StampProtoTypeDoH:
|
||||
return stamp.dohString()
|
||||
case StampProtoTypeTLS:
|
||||
return stamp.dotOrDoqString(StampProtoTypeTLS, defaultDoTPort)
|
||||
case StampProtoTypeDoQ:
|
||||
return stamp.dotOrDoqString(StampProtoTypeDoQ, defaultDoQPort)
|
||||
case StampProtoTypePlain:
|
||||
return stamp.plainString()
|
||||
}
|
||||
|
||||
panic("Unsupported protocol")
|
||||
}
|
||||
|
||||
// id(u8)=0x01 props addrLen(1) serverAddr pkStrlen(1) pkStr providerNameLen(1) providerName
|
||||
func newDNSCryptServerStamp(bin []byte) (ServerStamp, error) {
|
||||
stamp := ServerStamp{Proto: StampProtoTypeDNSCrypt}
|
||||
if len(bin) < 66 {
|
||||
return stamp, errors.New("stamp is too short")
|
||||
}
|
||||
stamp.Props = ServerInformalProperties(binary.LittleEndian.Uint64(bin[1:9]))
|
||||
binLen := len(bin)
|
||||
pos := 9
|
||||
|
||||
stampLen := int(bin[pos])
|
||||
if 1+stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.ServerAddrStr = string(bin[pos : pos+stampLen])
|
||||
pos += stampLen
|
||||
if net.ParseIP(strings.TrimRight(strings.TrimLeft(stamp.ServerAddrStr, "["), "]")) != nil {
|
||||
stamp.ServerAddrStr = fmt.Sprintf("%s:%d", stamp.ServerAddrStr, defaultDNSCryptPort)
|
||||
}
|
||||
|
||||
stampLen = int(bin[pos])
|
||||
if 1+stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.ServerPk = bin[pos : pos+stampLen]
|
||||
pos += stampLen
|
||||
|
||||
stampLen = int(bin[pos])
|
||||
if stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.ProviderName = string(bin[pos : pos+stampLen])
|
||||
pos += stampLen
|
||||
|
||||
if pos != binLen {
|
||||
return stamp, errors.New("invalid stamp (garbage after end)")
|
||||
}
|
||||
return stamp, nil
|
||||
}
|
||||
|
||||
// id(u8)=0x02 props addrLen(1) serverAddr hashLen(1) hash providerNameLen(1) providerName pathLen(1) path
|
||||
func newDoHServerStamp(bin []byte) (ServerStamp, error) {
|
||||
stamp := ServerStamp{Proto: StampProtoTypeDoH}
|
||||
if len(bin) < 22 {
|
||||
return stamp, errors.New("stamp is too short")
|
||||
}
|
||||
stamp.Props = ServerInformalProperties(binary.LittleEndian.Uint64(bin[1:9]))
|
||||
binLen := len(bin)
|
||||
pos := 9
|
||||
|
||||
stampLen := int(bin[pos])
|
||||
if 1+stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.ServerAddrStr = string(bin[pos : pos+stampLen])
|
||||
pos += stampLen
|
||||
|
||||
for {
|
||||
vlen := int(bin[pos])
|
||||
stampLen = vlen & ^0x80
|
||||
if 1+stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
if stampLen > 0 {
|
||||
stamp.Hashes = append(stamp.Hashes, bin[pos:pos+stampLen])
|
||||
}
|
||||
pos += stampLen
|
||||
if vlen&0x80 != 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
stampLen = int(bin[pos])
|
||||
if 1+stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.ProviderName = string(bin[pos : pos+stampLen])
|
||||
pos += stampLen
|
||||
|
||||
stampLen = int(bin[pos])
|
||||
if stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.Path = string(bin[pos : pos+stampLen])
|
||||
pos += stampLen
|
||||
|
||||
if pos != binLen {
|
||||
return stamp, errors.New("invalid stamp (garbage after end)")
|
||||
}
|
||||
|
||||
if net.ParseIP(strings.TrimRight(strings.TrimLeft(stamp.ServerAddrStr, "["), "]")) != nil {
|
||||
stamp.ServerAddrStr = fmt.Sprintf("%s:%d", stamp.ServerAddrStr, defaultDoHPort)
|
||||
}
|
||||
|
||||
return stamp, nil
|
||||
}
|
||||
|
||||
// id(u8)=0x03|0x04 props addrLen(1) serverAddr hashLen(1) hash providerNameLen(1) providerName
|
||||
func newDoTOrDoQServerStamp(bin []byte, stampType StampProtoType, defaultPort uint16) (ServerStamp, error) {
|
||||
stamp := ServerStamp{Proto: stampType}
|
||||
if len(bin) < 22 {
|
||||
return stamp, errors.New("stamp is too short")
|
||||
}
|
||||
stamp.Props = ServerInformalProperties(binary.LittleEndian.Uint64(bin[1:9]))
|
||||
binLen := len(bin)
|
||||
pos := 9
|
||||
|
||||
stampLen := int(bin[pos])
|
||||
if 1+stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.ServerAddrStr = string(bin[pos : pos+stampLen])
|
||||
pos += stampLen
|
||||
|
||||
for {
|
||||
vlen := int(bin[pos])
|
||||
stampLen = vlen & ^0x80
|
||||
if 1+stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
if stampLen > 0 {
|
||||
stamp.Hashes = append(stamp.Hashes, bin[pos:pos+stampLen])
|
||||
}
|
||||
pos += stampLen
|
||||
if vlen&0x80 != 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
stampLen = int(bin[pos])
|
||||
if stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.ProviderName = string(bin[pos : pos+stampLen])
|
||||
pos += stampLen
|
||||
|
||||
if pos != binLen {
|
||||
return stamp, errors.New("invalid stamp (garbage after end)")
|
||||
}
|
||||
|
||||
if net.ParseIP(strings.TrimRight(strings.TrimLeft(stamp.ServerAddrStr, "["), "]")) != nil {
|
||||
stamp.ServerAddrStr = fmt.Sprintf("%s:%d", stamp.ServerAddrStr, defaultPort)
|
||||
}
|
||||
|
||||
return stamp, nil
|
||||
}
|
||||
|
||||
// id(u8)=0x00 props addrLen(1) serverAddr
|
||||
func newPlainServerStamp(bin []byte) (ServerStamp, error) {
|
||||
stamp := ServerStamp{Proto: StampProtoTypePlain}
|
||||
if len(bin) < 17 {
|
||||
return stamp, fmt.Errorf("stamp is too short: len=%d", len(bin))
|
||||
}
|
||||
stamp.Props = ServerInformalProperties(binary.LittleEndian.Uint64(bin[1:9]))
|
||||
binLen := len(bin)
|
||||
pos := 9
|
||||
|
||||
stampLen := int(bin[pos])
|
||||
if stampLen >= binLen-pos {
|
||||
return stamp, errors.New("invalid stamp")
|
||||
}
|
||||
pos++
|
||||
stamp.ServerAddrStr = string(bin[pos : pos+stampLen])
|
||||
pos += stampLen
|
||||
|
||||
if pos != binLen {
|
||||
return stamp, errors.New("invalid stamp (garbage after end)")
|
||||
}
|
||||
|
||||
if net.ParseIP(strings.TrimRight(strings.TrimLeft(stamp.ServerAddrStr, "["), "]")) != nil {
|
||||
stamp.ServerAddrStr = fmt.Sprintf("%s:%d", stamp.ServerAddrStr, defaultPlainPort)
|
||||
}
|
||||
|
||||
return stamp, nil
|
||||
}
|
||||
|
||||
func (stamp *ServerStamp) dnsCryptString() string {
|
||||
bin := make([]uint8, 9)
|
||||
bin[0] = uint8(StampProtoTypeDNSCrypt)
|
||||
binary.LittleEndian.PutUint64(bin[1:9], uint64(stamp.Props))
|
||||
|
||||
serverAddrStr := stamp.ServerAddrStr
|
||||
if strings.HasSuffix(serverAddrStr, ":"+strconv.Itoa(defaultDNSCryptPort)) {
|
||||
serverAddrStr = serverAddrStr[:len(serverAddrStr)-1-len(strconv.Itoa(defaultDNSCryptPort))]
|
||||
}
|
||||
bin = append(bin, uint8(len(serverAddrStr)))
|
||||
bin = append(bin, []uint8(serverAddrStr)...)
|
||||
|
||||
bin = append(bin, uint8(len(stamp.ServerPk)))
|
||||
bin = append(bin, stamp.ServerPk...)
|
||||
|
||||
bin = append(bin, uint8(len(stamp.ProviderName)))
|
||||
bin = append(bin, []uint8(stamp.ProviderName)...)
|
||||
|
||||
str := base64.RawURLEncoding.EncodeToString(bin)
|
||||
|
||||
return stampProtocol + str
|
||||
}
|
||||
|
||||
func (stamp *ServerStamp) dohString() string {
|
||||
bin := make([]uint8, 9)
|
||||
bin[0] = uint8(StampProtoTypeDoH)
|
||||
binary.LittleEndian.PutUint64(bin[1:9], uint64(stamp.Props))
|
||||
|
||||
serverAddrStr := stamp.ServerAddrStr
|
||||
if strings.HasSuffix(serverAddrStr, ":"+strconv.Itoa(defaultDoHPort)) {
|
||||
serverAddrStr = serverAddrStr[:len(serverAddrStr)-1-len(strconv.Itoa(defaultDoHPort))]
|
||||
}
|
||||
bin = append(bin, uint8(len(serverAddrStr)))
|
||||
bin = append(bin, []uint8(serverAddrStr)...)
|
||||
|
||||
if len(stamp.Hashes) == 0 {
|
||||
bin = append(bin, uint8(0))
|
||||
} else {
|
||||
last := len(stamp.Hashes) - 1
|
||||
for i, hash := range stamp.Hashes {
|
||||
vlen := len(hash)
|
||||
if i < last {
|
||||
vlen |= 0x80
|
||||
}
|
||||
bin = append(bin, uint8(vlen))
|
||||
bin = append(bin, hash...)
|
||||
}
|
||||
}
|
||||
|
||||
bin = append(bin, uint8(len(stamp.ProviderName)))
|
||||
bin = append(bin, []uint8(stamp.ProviderName)...)
|
||||
|
||||
bin = append(bin, uint8(len(stamp.Path)))
|
||||
bin = append(bin, []uint8(stamp.Path)...)
|
||||
|
||||
str := base64.RawURLEncoding.EncodeToString(bin)
|
||||
return stampProtocol + str
|
||||
}
|
||||
|
||||
func (stamp *ServerStamp) dotOrDoqString(stampType StampProtoType, defaultPort uint16) string {
|
||||
bin := make([]uint8, 9)
|
||||
bin[0] = uint8(stampType)
|
||||
binary.LittleEndian.PutUint64(bin[1:9], uint64(stamp.Props))
|
||||
|
||||
serverAddrStr := stamp.ServerAddrStr
|
||||
if strings.HasSuffix(serverAddrStr, ":"+strconv.Itoa(int(defaultPort))) {
|
||||
serverAddrStr = serverAddrStr[:len(serverAddrStr)-1-len(strconv.Itoa(int(defaultPort)))]
|
||||
}
|
||||
bin = append(bin, uint8(len(serverAddrStr)))
|
||||
bin = append(bin, []uint8(serverAddrStr)...)
|
||||
|
||||
if len(stamp.Hashes) == 0 {
|
||||
bin = append(bin, uint8(0))
|
||||
} else {
|
||||
last := len(stamp.Hashes) - 1
|
||||
for i, hash := range stamp.Hashes {
|
||||
vlen := len(hash)
|
||||
if i < last {
|
||||
vlen |= 0x80
|
||||
}
|
||||
bin = append(bin, uint8(vlen))
|
||||
bin = append(bin, hash...)
|
||||
}
|
||||
}
|
||||
|
||||
bin = append(bin, uint8(len(stamp.ProviderName)))
|
||||
bin = append(bin, []uint8(stamp.ProviderName)...)
|
||||
|
||||
str := base64.RawURLEncoding.EncodeToString(bin)
|
||||
return stampProtocol + str
|
||||
}
|
||||
|
||||
func (stamp *ServerStamp) plainString() string {
|
||||
bin := make([]uint8, 9)
|
||||
bin[0] = uint8(StampProtoTypePlain)
|
||||
binary.LittleEndian.PutUint64(bin[1:9], uint64(stamp.Props))
|
||||
|
||||
serverAddrStr := stamp.ServerAddrStr
|
||||
if strings.HasSuffix(serverAddrStr, ":"+strconv.Itoa(defaultPlainPort)) {
|
||||
serverAddrStr = serverAddrStr[:len(serverAddrStr)-1-len(strconv.Itoa(defaultPlainPort))]
|
||||
}
|
||||
bin = append(bin, uint8(len(serverAddrStr)))
|
||||
bin = append(bin, []uint8(serverAddrStr)...)
|
||||
|
||||
str := base64.RawURLEncoding.EncodeToString(bin)
|
||||
return stampProtocol + str
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
*.swp
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.7"
|
||||
- "1.8"
|
||||
- "1.9"
|
||||
- "1.10"
|
||||
- tip
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Michael Alexander
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
go-rate
|
||||
===============
|
||||
|
||||
[](https://travis-ci.org/beefsack/go-rate)
|
||||
[](https://godoc.org/github.com/beefsack/go-rate)
|
||||
|
||||
**go-rate** is a rate limiter designed for a range of use cases,
|
||||
including server side spam protection and preventing saturation of APIs you
|
||||
consume.
|
||||
|
||||
It is used in production at
|
||||
[LangTrend](http://langtrend.com/l/Java,PHP,JavaScript) to adhere to the GitHub
|
||||
API rate limits.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Import `github.com/beefsack/go-rate` and create a new rate limiter with
|
||||
the `rate.New(limit int, interval time.Duration)` function.
|
||||
|
||||
The rate limiter provides a `Wait()` and a `Try() (bool, time.Duration)` method
|
||||
for both blocking and non-blocking functionality respectively.
|
||||
|
||||
API documentation available at [godoc.org](http://godoc.org/github.com/beefsack/go-rate).
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
### Blocking rate limiting
|
||||
|
||||
This example demonstrates limiting the output rate to 3 times per second.
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/beefsack/go-rate"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rl := rate.New(3, time.Second) // 3 times per second
|
||||
begin := time.Now()
|
||||
for i := 1; i <= 10; i++ {
|
||||
rl.Wait()
|
||||
fmt.Printf("%d started at %s\n", i, time.Now().Sub(begin))
|
||||
}
|
||||
// Output:
|
||||
// 1 started at 12.584us
|
||||
// 2 started at 40.13us
|
||||
// 3 started at 44.92us
|
||||
// 4 started at 1.000125362s
|
||||
// 5 started at 1.000143066s
|
||||
// 6 started at 1.000144707s
|
||||
// 7 started at 2.000224641s
|
||||
// 8 started at 2.000240751s
|
||||
// 9 started at 2.00024244s
|
||||
// 10 started at 3.000314332s
|
||||
}
|
||||
```
|
||||
|
||||
### Blocking rate limiting with multiple limiters
|
||||
|
||||
This example demonstrates combining rate limiters, one limiting at once per
|
||||
second, the other limiting at 2 times per 3 seconds.
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/beefsack/go-rate"
|
||||
)
|
||||
|
||||
func main() {
|
||||
begin := time.Now()
|
||||
rl1 := rate.New(1, time.Second) // Once per second
|
||||
rl2 := rate.New(2, time.Second*3) // 2 times per 3 seconds
|
||||
for i := 1; i <= 10; i++ {
|
||||
rl1.Wait()
|
||||
rl2.Wait()
|
||||
fmt.Printf("%d started at %s\n", i, time.Now().Sub(begin))
|
||||
}
|
||||
// Output:
|
||||
// 1 started at 11.197us
|
||||
// 2 started at 1.00011941s
|
||||
// 3 started at 3.000105858s
|
||||
// 4 started at 4.000210639s
|
||||
// 5 started at 6.000189578s
|
||||
// 6 started at 7.000289992s
|
||||
// 7 started at 9.000289942s
|
||||
// 8 started at 10.00038286s
|
||||
// 9 started at 12.000386821s
|
||||
// 10 started at 13.000465465s
|
||||
}
|
||||
```
|
||||
|
||||
### Non-blocking rate limiting
|
||||
|
||||
This example demonstrates non-blocking rate limiting, such as would be used to
|
||||
limit spam in a chat client.
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/beefsack/go-rate"
|
||||
)
|
||||
|
||||
var rl = rate.New(3, time.Second) // 3 times per second
|
||||
|
||||
func say(message string) {
|
||||
if ok, remaining := rl.Try(); ok {
|
||||
fmt.Printf("You said: %s\n", message)
|
||||
} else {
|
||||
fmt.Printf("Spam filter triggered, please wait %s\n", remaining)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
for i := 1; i <= 5; i++ {
|
||||
say(fmt.Sprintf("Message %d", i))
|
||||
}
|
||||
time.Sleep(time.Second / 2)
|
||||
say("I waited half a second, is that enough?")
|
||||
time.Sleep(time.Second / 2)
|
||||
say("Okay, I waited a second.")
|
||||
// Output:
|
||||
// You said: Message 1
|
||||
// You said: Message 2
|
||||
// You said: Message 3
|
||||
// Spam filter triggered, please wait 999.980816ms
|
||||
// Spam filter triggered, please wait 999.976704ms
|
||||
// Spam filter triggered, please wait 499.844795ms
|
||||
// You said: Okay, I waited a second.
|
||||
}
|
||||
```
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
* [Michael Alexander](https://github.com/beefsack)
|
||||
* [Geert-Johan Riemer](https://github.com/GeertJohan)
|
||||
* [Matt T. Proud](https://github.com/matttproud)
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
package rate
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A RateLimiter limits the rate at which an action can be performed. It
|
||||
// applies neither smoothing (like one could achieve in a token bucket system)
|
||||
// nor does it offer any conception of warmup, wherein the rate of actions
|
||||
// granted are steadily increased until a steady throughput equilibrium is
|
||||
// reached.
|
||||
type RateLimiter struct {
|
||||
limit int
|
||||
interval time.Duration
|
||||
mtx sync.Mutex
|
||||
times list.List
|
||||
}
|
||||
|
||||
// New creates a new rate limiter for the limit and interval.
|
||||
func New(limit int, interval time.Duration) *RateLimiter {
|
||||
lim := &RateLimiter{
|
||||
limit: limit,
|
||||
interval: interval,
|
||||
}
|
||||
lim.times.Init()
|
||||
return lim
|
||||
}
|
||||
|
||||
// Wait blocks if the rate limit has been reached. Wait offers no guarantees
|
||||
// of fairness for multiple actors if the allowed rate has been temporarily
|
||||
// exhausted.
|
||||
func (r *RateLimiter) Wait() {
|
||||
for {
|
||||
ok, remaining := r.Try()
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
time.Sleep(remaining)
|
||||
}
|
||||
}
|
||||
|
||||
// Try returns true if under the rate limit, or false if over and the
|
||||
// remaining time before the rate limit expires.
|
||||
func (r *RateLimiter) Try() (ok bool, remaining time.Duration) {
|
||||
r.mtx.Lock()
|
||||
defer r.mtx.Unlock()
|
||||
now := time.Now()
|
||||
if l := r.times.Len(); l < r.limit {
|
||||
r.times.PushBack(now)
|
||||
return true, 0
|
||||
}
|
||||
frnt := r.times.Front()
|
||||
if diff := now.Sub(frnt.Value.(time.Time)); diff < r.interval {
|
||||
return false, r.interval - diff
|
||||
}
|
||||
frnt.Value = now
|
||||
r.times.MoveToBack(frnt)
|
||||
return true, 0
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Jun Kimura
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-320
@@ -1,320 +0,0 @@
|
||||
# GCache
|
||||
|
||||

|
||||
[](https://pkg.go.dev/github.com/bluele/gcache?tab=doc)
|
||||
|
||||
Cache library for golang. It supports expirable Cache, LFU, LRU and ARC.
|
||||
|
||||
## Features
|
||||
|
||||
* Supports expirable Cache, LFU, LRU and ARC.
|
||||
|
||||
* Goroutine safe.
|
||||
|
||||
* Supports event handlers which evict, purge, and add entry. (Optional)
|
||||
|
||||
* Automatically load cache if it doesn't exists. (Optional)
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ go get github.com/bluele/gcache
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
### Manually set a key-value pair.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bluele/gcache"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gc := gcache.New(20).
|
||||
LRU().
|
||||
Build()
|
||||
gc.Set("key", "ok")
|
||||
value, err := gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Get: ok
|
||||
```
|
||||
|
||||
### Manually set a key-value pair, with an expiration time.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bluele/gcache"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gc := gcache.New(20).
|
||||
LRU().
|
||||
Build()
|
||||
gc.SetWithExpire("key", "ok", time.Second*10)
|
||||
value, _ := gc.Get("key")
|
||||
fmt.Println("Get:", value)
|
||||
|
||||
// Wait for value to expire
|
||||
time.Sleep(time.Second*10)
|
||||
|
||||
value, err = gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Get: ok
|
||||
// 10 seconds later, new attempt:
|
||||
panic: ErrKeyNotFound
|
||||
```
|
||||
|
||||
|
||||
### Automatically load value
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bluele/gcache"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gc := gcache.New(20).
|
||||
LRU().
|
||||
LoaderFunc(func(key interface{}) (interface{}, error) {
|
||||
return "ok", nil
|
||||
}).
|
||||
Build()
|
||||
value, err := gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Get: ok
|
||||
```
|
||||
|
||||
### Automatically load value with expiration
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bluele/gcache"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var evictCounter, loaderCounter, purgeCounter int
|
||||
gc := gcache.New(20).
|
||||
LRU().
|
||||
LoaderExpireFunc(func(key interface{}) (interface{}, *time.Duration, error) {
|
||||
loaderCounter++
|
||||
expire := 1 * time.Second
|
||||
return "ok", &expire, nil
|
||||
}).
|
||||
EvictedFunc(func(key, value interface{}) {
|
||||
evictCounter++
|
||||
fmt.Println("evicted key:", key)
|
||||
}).
|
||||
PurgeVisitorFunc(func(key, value interface{}) {
|
||||
purgeCounter++
|
||||
fmt.Println("purged key:", key)
|
||||
}).
|
||||
Build()
|
||||
value, err := gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
time.Sleep(1 * time.Second)
|
||||
value, err = gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
gc.Purge()
|
||||
if loaderCounter != evictCounter+purgeCounter {
|
||||
panic("bad")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Get: ok
|
||||
evicted key: key
|
||||
Get: ok
|
||||
purged key: key
|
||||
```
|
||||
|
||||
|
||||
## Cache Algorithm
|
||||
|
||||
* Least-Frequently Used (LFU)
|
||||
|
||||
Discards the least frequently used items first.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// size: 10
|
||||
gc := gcache.New(10).
|
||||
LFU().
|
||||
Build()
|
||||
gc.Set("key", "value")
|
||||
}
|
||||
```
|
||||
|
||||
* Least Recently Used (LRU)
|
||||
|
||||
Discards the least recently used items first.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// size: 10
|
||||
gc := gcache.New(10).
|
||||
LRU().
|
||||
Build()
|
||||
gc.Set("key", "value")
|
||||
}
|
||||
```
|
||||
|
||||
* Adaptive Replacement Cache (ARC)
|
||||
|
||||
Constantly balances between LRU and LFU, to improve the combined result.
|
||||
|
||||
detail: http://en.wikipedia.org/wiki/Adaptive_replacement_cache
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// size: 10
|
||||
gc := gcache.New(10).
|
||||
ARC().
|
||||
Build()
|
||||
gc.Set("key", "value")
|
||||
}
|
||||
```
|
||||
|
||||
* SimpleCache (Default)
|
||||
|
||||
SimpleCache has no clear priority for evict cache. It depends on key-value map order.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// size: 10
|
||||
gc := gcache.New(10).Build()
|
||||
gc.Set("key", "value")
|
||||
v, err := gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Loading Cache
|
||||
|
||||
If specified `LoaderFunc`, values are automatically loaded by the cache, and are stored in the cache until either evicted or manually invalidated.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
gc := gcache.New(10).
|
||||
LRU().
|
||||
LoaderFunc(func(key interface{}) (interface{}, error) {
|
||||
return "value", nil
|
||||
}).
|
||||
Build()
|
||||
v, _ := gc.Get("key")
|
||||
// output: "value"
|
||||
fmt.Println(v)
|
||||
}
|
||||
```
|
||||
|
||||
GCache coordinates cache fills such that only one load in one process of an entire replicated set of processes populates the cache, then multiplexes the loaded value to all callers.
|
||||
|
||||
## Expirable cache
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// LRU cache, size: 10, expiration: after a hour
|
||||
gc := gcache.New(10).
|
||||
LRU().
|
||||
Expiration(time.Hour).
|
||||
Build()
|
||||
}
|
||||
```
|
||||
|
||||
## Event handlers
|
||||
|
||||
### Evicted handler
|
||||
|
||||
Event handler for evict the entry.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
gc := gcache.New(2).
|
||||
EvictedFunc(func(key, value interface{}) {
|
||||
fmt.Println("evicted key:", key)
|
||||
}).
|
||||
Build()
|
||||
for i := 0; i < 3; i++ {
|
||||
gc.Set(i, i*i)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
evicted key: 0
|
||||
```
|
||||
|
||||
### Added handler
|
||||
|
||||
Event handler for add the entry.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
gc := gcache.New(2).
|
||||
AddedFunc(func(key, value interface{}) {
|
||||
fmt.Println("added key:", key)
|
||||
}).
|
||||
Build()
|
||||
for i := 0; i < 3; i++ {
|
||||
gc.Set(i, i*i)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
added key: 0
|
||||
added key: 1
|
||||
added key: 2
|
||||
```
|
||||
|
||||
# Author
|
||||
|
||||
**Jun Kimura**
|
||||
|
||||
* <http://github.com/bluele>
|
||||
* <junkxdev@gmail.com>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user