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:
Dimitry Kolyshev
2023-08-17 15:30:59 +03:00
parent d1469a436f
commit 8929e7ad07
1445 changed files with 862 additions and 513464 deletions
+12 -4
View File
@@ -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
+39 -18
View File
@@ -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
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+4
View File
@@ -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=
+5
View File
@@ -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
+34
View File
@@ -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
)
+115
View File
@@ -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=
+19
View File
@@ -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"
)
+34
View File
@@ -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
}
+4 -5
View File
@@ -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)
+1 -1
View File
@@ -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"
-67
View File
@@ -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"
+114
View File
@@ -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"
+31
View File
@@ -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"
+196
View File
@@ -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"\
./...
+79
View File
@@ -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\
;
+84
View File
@@ -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"
)
+71
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,2 +0,0 @@
// Package cache provides a simple LRU cache implementation
package cache
-52
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
File diff suppressed because it is too large Load Diff
-319
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,48 +0,0 @@
[![Godoc Reference](https://godoc.org/github.com/aead/poly1305?status.svg)](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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,8 +0,0 @@
coverage:
status:
project:
default:
target: 60%
threshold: null
patch: false
changes: false
-5
View File
@@ -1,5 +0,0 @@
.idea
.vscode
coverage.txt
build
dnscrypt
-52
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,209 +0,0 @@
[![Code Coverage](https://img.shields.io/codecov/c/github/ameshkov/dnscrypt/master.svg)](https://codecov.io/github/ameshkov/dnscrypt?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/ameshkov/dnscrypt)](https://goreportcard.com/report/ameshkov/dnscrypt)
[![Go Doc](https://godoc.org/github.com/ameshkov/dnscrypt?status.svg)](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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,2 +0,0 @@
// Package xsecretbox implements encryption/decryption of a message using specified keys
package xsecretbox
-31
View File
@@ -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
View File
@@ -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
View File
@@ -1,8 +0,0 @@
coverage:
status:
project:
default:
target: 40%
threshold: null
patch: false
changes: false
-2
View File
@@ -1,2 +0,0 @@
.idea
.vscode
-15
View File
@@ -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
View File
@@ -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
View File
@@ -1,17 +0,0 @@
[![Build Status](https://travis-ci.org/ameshkov/dnsstamps.svg?branch=master)](https://travis-ci.org/ameshkov/dnsstamps)
[![Code Coverage](https://img.shields.io/codecov/c/github/ameshkov/dnsstamps/master.svg)](https://codecov.io/github/ameshkov/dnsstamps?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/ameshkov/dnsstamps)](https://goreportcard.com/report/ameshkov/dnsstamps)
[![Go Doc](https://godoc.org/github.com/ameshkov/dnsstamps?status.svg)](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
View File
@@ -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
View File
@@ -1 +0,0 @@
*.swp
-8
View File
@@ -1,8 +0,0 @@
language: go
go:
- "1.7"
- "1.8"
- "1.9"
- "1.10"
- tip
-21
View File
@@ -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
View File
@@ -1,151 +0,0 @@
go-rate
===============
[![Build Status](https://travis-ci.org/beefsack/go-rate.svg?branch=master)](https://travis-ci.org/beefsack/go-rate)
[![GoDoc](https://godoc.org/github.com/beefsack/go-rate?status.svg)](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
View File
@@ -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
View File
@@ -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
View File
@@ -1,320 +0,0 @@
# GCache
![Test](https://github.com/bluele/gcache/workflows/Test/badge.svg)
[![GoDoc](https://godoc.org/github.com/bluele/gcache?status.svg)](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