diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9f4c3bc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Release + +on: + push: + tags: + - "v*" + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17 + - name: Docker Login + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 33d1b9d..01a1197 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,47 +1,41 @@ -name: Build and Test +name: Test on: push: - branches: [ master ] - pull_request: - branches: [ master ] + branches: + - main + paths: + - "*.go" jobs: - - build: - name: Build + test: + name: Test runs-on: ubuntu-latest steps: + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: ^1.16 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Generate self signed cert + run: openssl req -x509 -newkey rsa:4096 -sha256 -days 356 -nodes -keyout key.pem -out cert.pem -subj "/CN=localhost" + + - name: Build + run: go build -v ./... + + - name: Get gopherbadger + run: go get github.com/jpoles1/gopherbadger + + - name: Run test + run: gopherbadger -style=for-the-badge -covercmd "go test -v -race -covermode atomic -coverprofile=coverage.out ./... && go tool cover -func=coverage.out" - - name: Set up Go 1.x - uses: actions/setup-go@v2 - with: - go-version: ^1.13 - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Get dependencies - run: | - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi - - - name: Build - run: go build -v ./... - - - name: Test - run: go test -v -race -covermode atomic -coverprofile=covprofile ./... - - - name: Install goveralls - env: - GO111MODULE: off - run: go get github.com/mattn/goveralls - - - name: Send coverage - env: - COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: $(go env GOPATH)/bin/goveralls -coverprofile=covprofile -service=github - + - name: Commit coverage badge + uses: EndBug/add-and-commit@v7 + with: + message: "ci: update coverage" + add: "coverage_badge.png" + author_name: "github-actions[bot]" + author_email: "github-actions[bot]@users.noreply.github.com" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7221182 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +*.pem +odohd +dist/ +cover.out \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..c8611c3 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,45 @@ +before: + hooks: + - go mod download +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - freebsd + goarch: + - amd64 + - arm64 +nfpms: + - id: odohd + package_name: odohd + vendor: Emerald Onion + homepage: https://github.com/emeraldonion/odohd + maintainer: Nate Sales + description: Oblivious DNS over HTTPS server + license: MIT + section: utils + priority: extra + formats: + - deb + - rpm +dockers: + - image_templates: ["ghcr.io/emeraldonion/odohd:{{ .Version }}-amd64"] + dockerfile: Dockerfile + use: buildx + build_flag_templates: + - --platform=linux/amd64 + - --label=org.opencontainers.image.title=odohd + - --label=org.opencontainers.image.description=Oblivious DNS over HTTPS Server + - --label=org.opencontainers.image.url=https://github.com/emeraldonion/odohd + - --label=org.opencontainers.image.source=https://github.com/emeraldonion/odohd + - --label=org.opencontainers.image.version={{ .Version }} + - --label=org.opencontainers.image.revision={{ .FullCommit }} + - --label=org.opencontainers.image.licenses=MIT +docker_manifests: + - name_template: ghcr.io/emeraldonion/odohd:{{ .Version }} + image_templates: + - ghcr.io/emeraldonion/odohd:{{ .Version }}-amd64 + - name_template: ghcr.io/emeraldonion/odohd:latest + image_templates: + - ghcr.io/emeraldonion/odohd:{{ .Version }}-amd64 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c397aa0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine +COPY odohd /usr/bin/odohd +ENTRYPOINT ["/usr/bin/odohd"] diff --git a/Makefile b/Makefile deleted file mode 100644 index 8af560c..0000000 --- a/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -test: - go test ./... - -all: - go build -o odoh-server - -logs-target: - gcloud app logs tail -s odoh-target - -logs-proxy: - gcloud app logs tail -s odoh-proxy - -deploy-target: - gcloud app deploy --stop-previous-version target.yaml - -deploy-proxy: - gcloud app deploy --stop-previous-version proxy.yaml \ No newline at end of file diff --git a/Procfile b/Procfile deleted file mode 100644 index c0f90c9..0000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: ./bin/odoh-server diff --git a/README.md b/README.md index e268f39..7c48459 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,32 @@ -# odoh-server-go +# odohd -[![Coverage Status](https://coveralls.io/repos/github/cloudflare/odoh-server-go/badge.svg?branch=master)](https://coveralls.io/github/cloudflare/odoh-server-go?branch=master) +[Oblivious DoH Server](https://tools.ietf.org/html/draft-pauly-dprive-oblivious-doh) based on [Cloudflare's odoh-server-go](https://github.com/cloudflare/odoh-server-go) -[Oblivious DoH Server](https://tools.ietf.org/html/draft-pauly-dprive-oblivious-doh) +![Coverage Badge](coverage_badge.png) +[![Go Report](https://goreportcard.com/badge/github.com/emeraldonion/odohd?style=for-the-badge)](https://goreportcard.com/report/github.com/emeraldonion/odohd) +[![License](https://img.shields.io/github/license/emeraldonion/odohd?style=for-the-badge)](https://raw.githubusercontent.com/emeraldonion/odohd/main/LICENSE) +[![Release](https://img.shields.io/github/v/release/emeraldonion/odohd?style=for-the-badge)](https://github.com/emeraldonion/odohd/releases) -# Preconfigured Deployments +This fork includes changes for a server suited to Emerald Onion's production deployment. -[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) -[![deploy to Scalingo](https://cdn.scalingo.com/deploy/button.svg)](https://my.scalingo.com/deploy) - -# Local development - -To deploy the server locally, first acquire a TLS certificate using [mkcert](https://github.com/FiloSottile/mkcert) as follows: - -~~~ -$ mkcert -key-file key.pem -cert-file cert.pem 127.0.0.1 localhost -~~~ - -Then build and run the server as follows: - -~~~ -$ make all -$ CERT=cert.pem KEY=key.pem PORT=4567 ./odoh-server -~~~ - -You may then run the [corresponding client](https://github.com/cloudflare/odoh-client-go) as follows: - -~~~ -$ ./odoh-client odoh --proxy localhost:4567 --target odoh.cloudflare-dns.com --domain cloudflare.com -;; opcode: QUERY, status: NOERROR, id: 14306 -;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 - -;; QUESTION SECTION: -;cloudflare.com. IN AAAA - -;; ANSWER SECTION: -cloudflare.com. 271 IN AAAA 2606:4700::6810:84e5 -cloudflare.com. 271 IN AAAA 2606:4700::6810:85e5 -~~~ - -# Usage - -To deploy, run: - -~~~ -$ gcloud app deploy proxy.yaml -... -$ gcloud app deploy target.yaml -... -~~~ - -To check on its status, run: - -~~~ -$ gcloud app browse -~~~ - -To stream logs when deployed, run - -~~~ -$ gcloud app logs tail -s default -~~~ - -To run locally build and run the project using - -```shell -go build -PORT=8080 ./odoh-server-go -``` - -By default, the proxy listens on `/proxy` and the target listens on `/dns-query`. - -## Reverse proxy - -You need to deploy a reverse proxy with a valid TLS server certificate -for clients to be able to authenticate the target or proxy. - -The simplest option for this is using [Caddy](https://caddyserver.com). -Caddy will automatically provision a TLS certificate using ACME from [Let's Encrypt](https://letsencrypt.org). - -For instance: +## Usage: ``` -caddy reverse-proxy --from https://odoh.example.net:443 --to 127.0.0.1:8080 -``` - -Alternatively, use a Caddyfile similar to: - -``` -odoh.example.net - -reverse_proxy localhost:8080 +Usage: + odohd [OPTIONS] + +Application Options: + -l, --listen= Address to listen on (default: localhost:8080) + -m, --metrics-listen= Address to listen metrics server on (default: localhost:8081) + -r, --resolver= Target DNS resolver (default: 127.0.0.1:53) + -t, --no-tls Disable TLS + -c, --cert= TLS certificate file + -k, --key= TLS key file + --resolver-timeout= Resolver timeout (seconds) (default: 2.5) + --proxy-timeout= Proxy timeout (seconds) (default: 2.5) + -v, --verbose Enable verbose logging + -V, --version Show version and exit + +Help Options: + -h, --help Show this help message ``` -and run `caddy start`. diff --git a/app.json b/app.json deleted file mode 100644 index 7527201..0000000 --- a/app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "ODoH Target and Proxy: Go", - "description": "Deploy and ODoH Target and Proxy", - "keywords": [ - "DNS", - "go", - "ODoH", - "privacy" - ], - "website": "http://github.com/cloudflare/odoh-server-go", - "repository": "http://github.com/cloudflare/odoh-server-go" -} \ No newline at end of file diff --git a/app.yaml b/app.yaml deleted file mode 100644 index 607916a..0000000 --- a/app.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# [START runtime] -# service: odoh-proxy -runtime: go112 -# env: flex -# [END runtime] diff --git a/coverage_badge.png b/coverage_badge.png new file mode 100644 index 0000000..5799069 Binary files /dev/null and b/coverage_badge.png differ diff --git a/go.mod b/go.mod index d610052..85b6498 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,36 @@ -module github.com/cloudflare/odoh-server-go +module github.com/emeraldonion/odohd -go 1.14 +go 1.17 require ( - cloud.google.com/go/logging v1.1.1 - github.com/cisco/go-hpke v0.0.0-20201215202025-9cebdf8f33d4 - github.com/cloudflare/odoh-go v0.1.4 - github.com/elastic/go-elasticsearch/v8 v8.0.0-20201022194115-1af099fb3eca - github.com/miekg/dns v1.1.35 + github.com/cisco/go-hpke v0.0.0-20220110164554-aec202176774 + github.com/cloudflare/odoh-go v1.0.0 + github.com/jessevdk/go-flags v1.5.0 + github.com/miekg/dns v1.1.48 + github.com/prometheus/client_golang v1.12.1 + github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.7.0 +) + +require ( + git.schwanenlied.me/yawning/x448.git v0.0.0-20170617130356-01b048fb03d6 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cisco/go-tls-syntax v0.0.0-20200617162716-46b0cfb76b9b // indirect + github.com/cloudflare/circl v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.33.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect + golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect + golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect + golang.org/x/tools v0.1.10 // indirect + golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 2130b57..9b0de0f 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.70.0 h1:ujhG1RejZYi+HYfJNlgBh3j/bVKD8DewM7AkJ5UPyBc= -cloud.google.com/go v0.70.0/go.mod h1:/UTKYRQTWjVnSe7nGvoSzxEFUELzSI/yAYd0JQT6cRo= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -23,8 +21,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/logging v1.1.1 h1:mU+6wZyP0llWyobJ+aJFqeEfDzMp95R449wEPPILVX0= -cloud.google.com/go/logging v1.1.1/go.mod h1:oShOorPr2XGlPEXXI9VUJQH10md4lW25RYpSJjhE0TM= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -33,31 +29,44 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.schwanenlied.me/yawning/x448.git v0.0.0-20170617130356-01b048fb03d6 h1:w8IZgCntCe0RuBJp+dENSMwEBl/k8saTgJ5hPca5IWw= git.schwanenlied.me/yawning/x448.git v0.0.0-20170617130356-01b048fb03d6/go.mod h1:wQaGCqEu44ykB17jZHCevrgSVl3KJnwQBObUtrKU4uU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cisco/go-hpke v0.0.0-20201215202025-9cebdf8f33d4 h1:vucWGQEsuhekt2bY29mxEdaqdGpb7Ij1eio6actPzP4= -github.com/cisco/go-hpke v0.0.0-20201215202025-9cebdf8f33d4/go.mod h1:RSsoIHRMBe69FbF/fIbmWYa3rrC6vuPyC0MbNUpel3Q= +github.com/cisco/go-hpke v0.0.0-20210215210317-01c430f1f302/go.mod h1:RSsoIHRMBe69FbF/fIbmWYa3rrC6vuPyC0MbNUpel3Q= +github.com/cisco/go-hpke v0.0.0-20220110164554-aec202176774 h1:qh4/iQM+NF66UCzC6vcSs+MGBnQeyrcr6Sbv5ObAOIQ= +github.com/cisco/go-hpke v0.0.0-20220110164554-aec202176774/go.mod h1:RSsoIHRMBe69FbF/fIbmWYa3rrC6vuPyC0MbNUpel3Q= github.com/cisco/go-tls-syntax v0.0.0-20200617162716-46b0cfb76b9b h1:Ves2turKTX7zruivAcUOQg155xggcbv3suVdbKCBQNM= github.com/cisco/go-tls-syntax v0.0.0-20200617162716-46b0cfb76b9b/go.mod h1:0AZAV7lYvynZQ5ErHlGMKH+4QYMyNCFd+AiL9MlrCYA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.0.0 h1:64b6pyfCFbYm623ncIkYGNZaOcmIbyd+CjyMi2L9vdI= github.com/cloudflare/circl v1.0.0/go.mod h1:MhjB3NEEhJbTOdLLq964NIUisXDxaE1WkQPUxtgZXiY= -github.com/cloudflare/odoh-go v0.1.4 h1:z+7+4bFZjk78NUqlw6WKZXhQaOsv2dUUyeQCiczWjU8= -github.com/cloudflare/odoh-go v0.1.4/go.mod h1:SDBrEsRyq5fhkqOqKH5EbOjG4yA+Azx4UOgpaxlQU5k= +github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/odoh-go v1.0.0 h1:4ZRBHNFC0wefDpWKuSXDuw6SsEulP3QrS/rqG9RVCgo= +github.com/cloudflare/odoh-go v1.0.0/go.mod h1:J3Doz827YDYvz4hEmJU6q45hRFOqxUBL6NRUuEfjMxA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/elastic/go-elasticsearch/v8 v8.0.0-20201022194115-1af099fb3eca h1:bQtWepJpVTzze/AcH7r9riu3hAHgjNkIQNePw7gyVu4= -github.com/elastic/go-elasticsearch/v8 v8.0.0-20201022194115-1af099fb3eca/go.mod h1:xe9a/L2aeOgFKKgrO3ibQTnMdpAeL0GC+5/HpGScSa4= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -65,10 +74,19 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -90,8 +108,10 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -101,11 +121,11 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -114,52 +134,106 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201009210932-67992a1a5a35/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ= +github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.33.0 h1:rHgav/0a6+uYgGdNt3jwz8FNSesO/Hsang3O0T9A5SE= +github.com/prometheus/common v0.33.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -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= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -181,7 +255,6 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -190,10 +263,13 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 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 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -201,10 +277,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -219,15 +295,23 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -235,22 +319,28 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -263,16 +353,35 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/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-20210603081109-ebe580a85c40/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -296,7 +405,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -317,14 +425,15 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201017001424-6003fad69a88 h1:ZB1XYzdDo7c/O48jzjMkvIjnC120Z9/CwgDWhePjQdQ= -golang.org/x/tools v0.0.0-20201017001424-6003fad69a88/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -341,14 +450,11 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.33.0 h1:+gL0XvACeMIvpwLZ5rQZzLn5cwOsgg8dIcfJ2SYfBVw= -google.golang.org/api v0.33.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -379,9 +485,6 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 h1:bFFRpT+e8JJVY7lMMfvezL1ZIwqiwmPl2bsE2yx4HqM= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -394,9 +497,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -406,13 +506,23 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..d02bfbb --- /dev/null +++ b/main.go @@ -0,0 +1,166 @@ +// The MIT License +// +// Copyright (c) 2019-2020, Cloudflare, Inc. and Apple, Inc. All rights reserved. +// +// 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. + +package main + +import ( + "crypto/rand" + "fmt" + "net/http" + "os" + "time" + + "github.com/cisco/go-hpke" + "github.com/cloudflare/odoh-go" + "github.com/jessevdk/go-flags" + log "github.com/sirupsen/logrus" +) + +const ( + // HPKE constants + kemID = hpke.DHKEM_X25519 + kdfID = hpke.KDF_HKDF_SHA256 + aeadID = hpke.AEAD_AESGCM128 + + // Keying material (seed) should have as many bits of entropy as the bit length of the x25519 secret key + defaultSeedLength = 32 +) + +// Set by build process +var ( + version = "dev" + commit = "unknown" + date = "unknown" +) + +// CLI flags +var opts struct { + ListenAddr string `short:"l" long:"listen" description:"Address to listen on" default:"localhost:8080"` + MetricsListenAddr string `short:"m" long:"metrics-listen" description:"Address to listen metrics server on" default:"localhost:8081"` + Resolver string `short:"r" long:"resolver" description:"Target DNS resolver" default:"127.0.0.1:53"` + DisableTls bool `short:"t" long:"no-tls" description:"Disable TLS"` + Cert string `short:"c" long:"cert" description:"TLS certificate file"` + Key string `short:"k" long:"key" description:"TLS key file"` + ResolverTimeout float32 `long:"resolver-timeout" description:"Resolver timeout (seconds)" default:"2.5"` + ProxyTimeout float32 `long:"proxy-timeout" description:"Proxy timeout (seconds)" default:"2.5"` + Verbose bool `short:"v" long:"verbose" description:"Enable verbose logging"` + ShowVersion bool `short:"V" long:"version" description:"Show version and exit"` +} + +func keyPair() (*odoh.ObliviousDoHKeyPair, error) { + // Random seed for HPKE keypair + seed := make([]byte, defaultSeedLength) + _, err := rand.Read(seed) + if err != nil { + return nil, err + } + + kp, err := odoh.CreateKeyPairFromSeed(hpke.DHKEM_X25519, hpke.KDF_HKDF_SHA256, hpke.AEAD_AESGCM128, seed) + if err != nil { + return nil, err + } + + return &kp, err +} + +// serverPair returns a target and proxy server +func serverPair(keyPair odoh.ObliviousDoHKeyPair) (*targetServer, *proxyServer) { + log.Debugf("resolver timeout: %+v", opts.ResolverTimeout) + target := &targetServer{ + resolver: &targetResolver{ + timeout: time.Duration(opts.ResolverTimeout) * time.Second, + nameserver: opts.Resolver, + }, + odohKeyPair: keyPair, + } + + log.Debugf("proxy timeout: %+v", opts.ProxyTimeout) + proxy := &proxyServer{ + client: &http.Client{ + Transport: &http.Transport{ + MaxIdleConnsPerHost: 1024, + TLSHandshakeTimeout: time.Duration(opts.ProxyTimeout) * time.Second, + }, + }, + } + + return target, proxy +} + +// setupHandlers configures HTTP handlers +func setupHandlers(target *targetServer, proxy *proxyServer) { + http.HandleFunc("/proxy", proxy.proxyQueryHandler) + http.HandleFunc("/dns-query", target.targetQueryHandler) + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprint(w, "ok") }) + http.HandleFunc("/.well-known/odohconfigs", target.configHandler) +} + +// serve starts the HTTP server +func serve(listenAddr string, tls bool, tlsCert, tlsKey string) { + // Start the server + log.Infof("Starting ODoH listener on %s", listenAddr) + if tls { // HTTPS listener + log.Fatal(http.ListenAndServeTLS(listenAddr, tlsCert, tlsKey, nil)) + } else { // HTTP listener + log.Fatal(http.ListenAndServe(listenAddr, nil)) + } +} + +func main() { + // Parse cli flags + _, err := flags.ParseArgs(&opts, os.Args) + if err != nil { + os.Exit(1) + } + + // Enable debug logging in development releases + if //noinspection GoBoolExpressions + version == "dev" || opts.Verbose { + log.SetLevel(log.DebugLevel) + log.Debugln("Verbose logging enabled") + } + + if opts.ShowVersion { + log.Printf("odohd %s commit %s date %s\n", version, commit, date) + os.Exit(0) + } + + // Validate TLS cert/key + if !opts.DisableTls && (opts.Cert == "" || opts.Key == "") { + log.Fatal("--cert and --key must be set when TLS is enabled") + } + + // Start metrics server + go func() { + log.Infof("Starting metrics server on %s", opts.MetricsListenAddr) + log.Fatal(metricsServe(opts.MetricsListenAddr)) + }() + + kp, err := keyPair() + if err != nil { + log.Fatal(err) + } + + proxy, target := serverPair(*kp) + setupHandlers(proxy, target) + serve(opts.ListenAddr, !opts.DisableTls, opts.Cert, opts.Key) +} diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..cb444d4 --- /dev/null +++ b/main_test.go @@ -0,0 +1,70 @@ +package main + +import ( + "crypto/tls" + "io" + "net/http" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestMain(m *testing.M) { + kp, err := keyPair() + if err != nil { + panic(err) + } + + // Disable TLS strict certificate checking for local self signed cert + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + + proxy, target := serverPair(*kp) + setupHandlers(proxy, target) + + os.Exit(m.Run()) +} + +func TestGenKeyPair(t *testing.T) { + _, err := keyPair() + assert.Nil(t, err) +} + +func TestServeHTTP(t *testing.T) { + go serve("127.0.0.1:8080", false, "", "") + + // Wait for server startup + time.Sleep(50 * time.Millisecond) + + httpClient := http.Client{} + req, err := http.NewRequest("GET", "http://127.0.0.1:8080/health", nil) + assert.Nil(t, err) + + resp, err := httpClient.Do(req) + assert.Nil(t, err) + + body, err := io.ReadAll(resp.Body) + assert.Nil(t, err) + + assert.Equal(t, "ok", string(body)) +} + +func TestServeHTTPS(t *testing.T) { + go serve("127.0.0.1:8443", true, "cert.pem", "key.pem") + + // Wait for server startup + time.Sleep(50 * time.Millisecond) + + httpClient := http.Client{} + req, err := http.NewRequest("GET", "https://127.0.0.1:8443/health", nil) + assert.Nil(t, err) + + resp, err := httpClient.Do(req) + assert.Nil(t, err) + + body, err := io.ReadAll(resp.Body) + assert.Nil(t, err) + + assert.Equal(t, "ok", string(body)) +} diff --git a/metrics.go b/metrics.go new file mode 100644 index 0000000..7cd938f --- /dev/null +++ b/metrics.go @@ -0,0 +1,34 @@ +package main + +import ( + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var ( + metricTargetQueries = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "odohd_target_queries", + Help: "Total queries as a target", + }) + metricProxyQueries = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "odohd_proxy_queries", + Help: "Total queries as a proxy", + }) + metricTargetValidQueries = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "odohd_target_valid_queries", + Help: "Total valid queries as a target", + }) + metricProxyValidQueries = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "odohd_proxy_valid_queries", + Help: "Total valid queries as a proxy", + }) +) + +// metricsServe starts the metrics HTTP server +func metricsServe(listenAddr string) error { + http.Handle("/metrics", promhttp.Handler()) + return http.ListenAndServe(listenAddr, nil) +} diff --git a/metrics_test.go b/metrics_test.go new file mode 100644 index 0000000..8627ba3 --- /dev/null +++ b/metrics_test.go @@ -0,0 +1,28 @@ +package main + +import ( + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestMetricsServe(t *testing.T) { + go func() { + err := metricsServe("127.0.0.1:8081") + assert.Nil(t, err) + }() + + // Wait for server startup + time.Sleep(50 * time.Millisecond) + + httpClient := http.Client{} + req, err := http.NewRequest("GET", "http://127.0.0.1:8081/metrics", nil) + assert.Nil(t, err) + + resp, err := httpClient.Do(req) + assert.Nil(t, err) + + assert.Equal(t, 200, resp.StatusCode) +} diff --git a/odoh_server.go b/odoh_server.go deleted file mode 100644 index 0e77279..0000000 --- a/odoh_server.go +++ /dev/null @@ -1,194 +0,0 @@ -// The MIT License -// -// Copyright (c) 2019-2020, Cloudflare, Inc. and Apple, Inc. All rights reserved. -// -// 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. - -package main - -import ( - "crypto/rand" - "encoding/hex" - "fmt" - "log" - "net/http" - "os" - "time" - - "github.com/cisco/go-hpke" - odoh "github.com/cloudflare/odoh-go" -) - -const ( - // HPKE constants - kemID = hpke.DHKEM_X25519 - kdfID = hpke.KDF_HKDF_SHA256 - aeadID = hpke.AEAD_AESGCM128 - - // keying material (seed) should have as many bits of entropy as the bit - // length of the x25519 secret key - defaultSeedLength = 32 - - // HTTP constants. Fill in your proxy and target here. - defaultPort = "8080" - proxyURI = "https://dnstarget.example.net" - targetURI = "https://dnsproxy.example.net" - proxyEndpoint = "/proxy" - queryEndpoint = "/dns-query" - healthEndpoint = "/health" - configEndpoint = "/.well-known/odohconfigs" - - // Environment variables - secretSeedEnvironmentVariable = "SEED_SECRET_KEY" - targetNameEnvironmentVariable = "TARGET_INSTANCE_NAME" - experimentIDEnvironmentVariable = "EXPERIMENT_ID" - telemetryTypeEnvironmentVariable = "TELEMETRY_TYPE" - certificateEnvironmentVariable = "CERT" - keyEnvironmentVariable = "KEY" -) - -var ( - // DNS constants. Fill in a DNS server to forward to here. - nameServers = []string{"1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"} -) - -type odohServer struct { - endpoints map[string]string - Verbose bool - target *targetServer - proxy *proxyServer - DOHURI string -} - -func (s odohServer) indexHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("%s Handling %s\n", r.Method, r.URL.Path) - fmt.Fprint(w, "ODOH service\n") - fmt.Fprint(w, "----------------\n") - fmt.Fprintf(w, "Proxy endpoint: https://%s%s{?targethost,targetpath}\n", r.Host, s.endpoints["Proxy"]) - fmt.Fprintf(w, "Target endpoint: https://%s%s{?dns}\n", r.Host, s.endpoints["Target"]) - fmt.Fprint(w, "----------------\n") -} - -func (s odohServer) healthCheckHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("%s Handling %s\n", r.Method, r.URL.Path) - fmt.Fprint(w, "ok") -} - -func main() { - port := os.Getenv("PORT") - if port == "" { - port = defaultPort - } - - var seed []byte - if seedHex := os.Getenv(secretSeedEnvironmentVariable); seedHex != "" { - log.Printf("Using Secret Key Seed : [%v]", seedHex) - var err error - seed, err = hex.DecodeString(seedHex) - if err != nil { - panic(err) - } - } else { - seed = make([]byte, defaultSeedLength) - rand.Read(seed) - } - - var serverName string - if serverNameSetting := os.Getenv(targetNameEnvironmentVariable); serverNameSetting != "" { - serverName = serverNameSetting - } else { - serverName = "server_localhost" - } - log.Printf("Setting Server Name as %v", serverName) - - var experimentID string - if experimentID = os.Getenv(experimentIDEnvironmentVariable); experimentID == "" { - experimentID = "EXP_LOCAL" - } - - var telemetryType string - if telemetryType = os.Getenv(telemetryTypeEnvironmentVariable); telemetryType == "" { - telemetryType = "LOG" - } - - var certFile string - if certFile = os.Getenv(certificateEnvironmentVariable); certFile == "" { - certFile = "cert.pem" - } - - var keyFile string - if keyFile = os.Getenv(keyEnvironmentVariable); keyFile == "" { - keyFile = "key.pem" - } - - keyPair, err := odoh.CreateKeyPairFromSeed(kemID, kdfID, aeadID, seed) - if err != nil { - log.Fatal("Failed to create a private key. Exiting now.") - } - - endpoints := make(map[string]string) - endpoints["Target"] = queryEndpoint - endpoints["Proxy"] = proxyEndpoint - endpoints["Health"] = healthEndpoint - endpoints["Config"] = configEndpoint - - resolversInUse := make([]resolver, len(nameServers)) - - for index := 0; index < len(nameServers); index++ { - resolver := &targetResolver{ - timeout: 2500 * time.Millisecond, - nameserver: nameServers[index], - } - resolversInUse[index] = resolver - } - - target := &targetServer{ - verbose: false, - resolver: resolversInUse, - odohKeyPair: keyPair, - telemetryClient: getTelemetryInstance(telemetryType), - serverInstanceName: serverName, - experimentId: experimentID, - } - - proxy := &proxyServer{ - client: &http.Client{ - Transport: &http.Transport{ - MaxIdleConnsPerHost: 1024, - TLSHandshakeTimeout: 0 * time.Second, - }, - }, - } - - server := odohServer{ - endpoints: endpoints, - target: target, - proxy: proxy, - DOHURI: fmt.Sprintf("%s/%s", targetURI, queryEndpoint), - } - - http.HandleFunc(proxyEndpoint, server.proxy.proxyQueryHandler) - http.HandleFunc(queryEndpoint, server.target.targetQueryHandler) - http.HandleFunc(healthEndpoint, server.healthCheckHandler) - http.HandleFunc(configEndpoint, target.configHandler) - http.HandleFunc("/", server.indexHandler) - - log.Printf("Listening on port %v with cert %v and key %v\n", port, certFile, keyFile) - log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%s", port), certFile, keyFile, nil)) -} diff --git a/proxy.go b/proxy.go index 449f0ec..e5baea9 100644 --- a/proxy.go +++ b/proxy.go @@ -26,9 +26,10 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" - "log" + "io" "net/http" + + log "github.com/sirupsen/logrus" ) type proxyServer struct { @@ -37,15 +38,14 @@ type proxyServer struct { } var ( - errWrongMethod = fmt.Errorf("Unsupported method") - errMissingTargetHost = fmt.Errorf("Missing proxy targethost query parameter") - errMissingTargetPath = fmt.Errorf("Missing proxy targetpath query parameter") - errEmptyRequestBody = fmt.Errorf("Missing request body") + errWrongMethod = fmt.Errorf("unsupported method") + errMissingTargetHost = fmt.Errorf("missing proxy targethost query parameter") + errMissingTargetPath = fmt.Errorf("missing proxy targetpath query parameter") + errEmptyRequestBody = fmt.Errorf("missing request body") ) func forwardProxyRequest(client *http.Client, targetName string, targetPath string, body []byte, headerContentType string) (*http.Response, error) { - targetURL := "https://" + targetName + targetPath - req, err := http.NewRequest("POST", targetURL, bytes.NewReader(body)) + req, err := http.NewRequest("POST", "https://"+targetName+targetPath, bytes.NewReader(body)) if err != nil { log.Println("Failed creating target POST request") return nil, errors.New("failed creating target POST request") @@ -56,7 +56,9 @@ func forwardProxyRequest(client *http.Client, targetName string, targetPath stri } func (p *proxyServer) proxyQueryHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("%s Handling %s\n", r.Method, r.URL.Path) + log.Debug("handling proxy request") + + metricProxyQueries.Inc() if r.Method != "POST" { p.lastError = errWrongMethod @@ -81,8 +83,15 @@ func (p *proxyServer) proxyQueryHandler(w http.ResponseWriter, r *http.Request) return } - defer r.Body.Close() - body, err := ioutil.ReadAll(r.Body) + log.Debugf("targethost: %s targetpath: %s", targetName, targetPath) + + defer func(body io.ReadCloser) { + err := body.Close() + if err != nil { + log.Warn(err) + } + }(r.Body) + body, err := io.ReadAll(r.Body) if err != nil || len(body) == 0 { p.lastError = errEmptyRequestBody log.Printf(p.lastError.Error()) @@ -92,6 +101,7 @@ func (p *proxyServer) proxyQueryHandler(w http.ResponseWriter, r *http.Request) headerContentType := r.Header.Get("Content-Type") + metricProxyValidQueries.Inc() response, err := forwardProxyRequest(p.client, targetName, targetPath, body, headerContentType) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -103,13 +113,22 @@ func (p *proxyServer) proxyQueryHandler(w http.ResponseWriter, r *http.Request) return } - defer response.Body.Close() - responseBody, err := ioutil.ReadAll(response.Body) + defer func(body io.ReadCloser) { + err := body.Close() + if err != nil { + log.Warn(err) + } + }(response.Body) + + responseBody, err := io.ReadAll(response.Body) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.Header().Set("Content-Type", headerContentType) - w.Write(responseBody) + _, err = w.Write(responseBody) + if err != nil { + log.Warn(err) + } } diff --git a/proxy.yaml b/proxy.yaml deleted file mode 100644 index d9f58a4..0000000 --- a/proxy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# [START runtime] -service: odoh-proxy -runtime: go114 -# [END runtime] diff --git a/proxy_test.go b/proxy_test.go index 84e326d..360cb9c 100644 --- a/proxy_test.go +++ b/proxy_test.go @@ -24,7 +24,6 @@ package main import ( "fmt" - "io/ioutil" "net/http" "net/http/httptest" "net/url" @@ -32,29 +31,13 @@ import ( "testing" ) -type testTarget struct { - expectedStatusCode int -} - -func (t testTarget) handleRequest(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - return - } - - headerContentType := r.Header.Get("Content-Type") - w.Header().Set("Content-Type", headerContentType) - w.Write(body) -} - func TestProxyMethod(t *testing.T) { proxy := proxyServer{} handler := http.HandlerFunc(proxy.proxyQueryHandler) fakeQueryBody := strings.NewReader("test body") - fakeQueryURL := queryEndpoint + fakeQueryURL := "/dns-query" request, err := http.NewRequest("GET", fakeQueryURL, fakeQueryBody) if err != nil { t.Fatal(err) @@ -64,10 +47,10 @@ func TestProxyMethod(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Code; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Failed when sent an invalid request method. Expected %d, got %d", http.StatusBadRequest, status)) + t.Fatal(fmt.Errorf("failed when sent an invalid request method. Expected %d, got %d", http.StatusBadRequest, status)) } if proxy.lastError != errWrongMethod { - t.Fatal(fmt.Errorf("Incorrect error. Expected %s", errWrongMethod.Error())) + t.Fatal(fmt.Errorf("incorrect error, expected %s", errWrongMethod.Error())) } } @@ -79,13 +62,13 @@ func TestProxyQueryParametersMissing(t *testing.T) { fakeQueryBody := strings.NewReader("test body") testURLs := []string{ - queryEndpoint, + "/dns-query", "/not-the-right-endpoint", - queryEndpoint + "?targethost=", - queryEndpoint + "?targetpath=bar", + "/dns-query?targethost=", + "/dns-query?targetpath=bar", } - for _, url := range testURLs { - request, err := http.NewRequest("POST", url, fakeQueryBody) + for _, testUrl := range testURLs { + request, err := http.NewRequest("POST", testUrl, fakeQueryBody) if err != nil { t.Fatal(err) } @@ -94,19 +77,19 @@ func TestProxyQueryParametersMissing(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Code; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Failed when sent invalid parameters. Expected %d, got %d", http.StatusBadRequest, status)) + t.Fatal(fmt.Errorf("failed when sent invalid parameters. Expected %d, got %d", http.StatusBadRequest, status)) } if proxy.lastError != errMissingTargetHost { - t.Fatal(fmt.Errorf("Incorrect error. Expected %s", errMissingTargetHost.Error())) + t.Fatal(fmt.Errorf("incorrect error, expected %s", errMissingTargetHost.Error())) } } testURLs = []string{ - queryEndpoint + "?targethost=foo", - queryEndpoint + "?targethost=foo&targetpath=", + "/dns-query?targethost=foo", + "/dns-query?targethost=foo&targetpath=", } - for _, url := range testURLs { - request, err := http.NewRequest("POST", url, fakeQueryBody) + for _, testUrl := range testURLs { + request, err := http.NewRequest("POST", testUrl, fakeQueryBody) if err != nil { t.Fatal(err) } @@ -115,10 +98,10 @@ func TestProxyQueryParametersMissing(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Code; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Failed when sent invalid parameters. Expected %d, got %d", http.StatusBadRequest, status)) + t.Fatal(fmt.Errorf("failed when sent invalid parameters. Expected %d, got %d", http.StatusBadRequest, status)) } if proxy.lastError != errMissingTargetPath { - t.Fatal(fmt.Errorf("Incorrect error. Expected %s", errMissingTargetPath.Error())) + t.Fatal(fmt.Errorf("incorrect error, expected %s", errMissingTargetPath.Error())) } } } @@ -129,7 +112,7 @@ func TestProxyQueryMissingBody(t *testing.T) { handler := http.HandlerFunc(proxy.proxyQueryHandler) emptyQueryBody := strings.NewReader("") - request, err := http.NewRequest("POST", queryEndpoint+"?targethost=foo&targetpath=bar", emptyQueryBody) + request, err := http.NewRequest("POST", "/dns-query?targethost=foo&targetpath=bar", emptyQueryBody) if err != nil { t.Fatal(err) } @@ -138,10 +121,10 @@ func TestProxyQueryMissingBody(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Code; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Failed when sent invalid parameters. Expected %d, got %d", http.StatusBadRequest, status)) + t.Fatal(fmt.Errorf("failed when sent invalid parameters. Expected %d, got %d", http.StatusBadRequest, status)) } if proxy.lastError != errEmptyRequestBody { - t.Fatal(fmt.Errorf("Incorrect error. Expected %s", errEmptyRequestBody.Error())) + t.Fatal(fmt.Errorf("incorrect error, expected %s", errEmptyRequestBody.Error())) } } @@ -153,7 +136,7 @@ func TestProxyIncorrectTarget(t *testing.T) { handler := http.HandlerFunc(proxy.proxyQueryHandler) fakeQueryBody := strings.NewReader("test body") - fakeQueryURL := queryEndpoint + "?targethost=nottherighttarget.com&targetpath=/" + fakeQueryURL := "/dns-query?targethost=nottherighttarget.com&targetpath=/" request, err := http.NewRequest("POST", fakeQueryURL, fakeQueryBody) if err != nil { @@ -164,13 +147,16 @@ func TestProxyIncorrectTarget(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Code; status != http.StatusInternalServerError { - t.Fatal(fmt.Errorf("Failed to propagate the desired error code. Expected %d, got %d", http.StatusInternalServerError, status)) + t.Fatal(fmt.Errorf("failed to propagate the desired error code. Expected %d, got %d", http.StatusInternalServerError, status)) } } func TestProxyStatusCodePropagationOK(t *testing.T) { ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, "OK") + _, err := fmt.Fprintln(w, "OK") + if err != nil { + t.Fatal(err) + } })) defer ts.Close() @@ -187,7 +173,7 @@ func TestProxyStatusCodePropagationOK(t *testing.T) { handler := http.HandlerFunc(proxy.proxyQueryHandler) fakeQueryBody := strings.NewReader("test body") - fakeQueryURL := queryEndpoint + "?targethost=" + testTargetURL + "&targetpath=/" + fakeQueryURL := "/dns-query" + "?targethost=" + testTargetURL + "&targetpath=/" request, err := http.NewRequest("POST", fakeQueryURL, fakeQueryBody) if err != nil { @@ -198,7 +184,7 @@ func TestProxyStatusCodePropagationOK(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Code; status != http.StatusOK { - t.Fatal(fmt.Errorf("Failed to propagate the desired error code. Expected %d, got %d", http.StatusOK, status)) + t.Fatal(fmt.Errorf("failed to propagate the desired error code. Expected %d, got %d", http.StatusOK, status)) } } @@ -222,7 +208,7 @@ func TestProxyStatusCodePropagationFailure(t *testing.T) { handler := http.HandlerFunc(proxy.proxyQueryHandler) fakeQueryBody := strings.NewReader("test body") - fakeQueryURL := queryEndpoint + "?targethost=" + testTargetURL + "&targetpath=/" + fakeQueryURL := "/dns-query" + "?targethost=" + testTargetURL + "&targetpath=/" request, err := http.NewRequest("POST", fakeQueryURL, fakeQueryBody) if err != nil { @@ -233,6 +219,6 @@ func TestProxyStatusCodePropagationFailure(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Code; status != expectedFailure { - t.Fatal(fmt.Errorf("Failed to propagate the desired error code. Expected %d, got %d", expectedFailure, status)) + t.Fatal(fmt.Errorf("failed to propagate the desired error code. Expected %d, got %d", expectedFailure, status)) } } diff --git a/resolver.go b/resolver.go index 439899a..16f3d99 100644 --- a/resolver.go +++ b/resolver.go @@ -31,7 +31,6 @@ import ( ) type resolver interface { - name() string resolve(query *dns.Msg) (*dns.Msg, error) } @@ -40,19 +39,19 @@ type targetResolver struct { timeout time.Duration } -func (s targetResolver) name() string { - return s.nameserver -} - func (s targetResolver) resolve(query *dns.Msg) (*dns.Msg, error) { connection := new(dns.Conn) var err error if connection.Conn, err = net.DialTimeout("tcp", s.nameserver, s.timeout*time.Millisecond); err != nil { - return nil, fmt.Errorf("Failed starting resolver connection") + return nil, fmt.Errorf("failed starting resolver connection") } - connection.SetReadDeadline(time.Now().Add(s.timeout * time.Millisecond)) - connection.SetWriteDeadline(time.Now().Add(s.timeout * time.Millisecond)) + if err = connection.SetReadDeadline(time.Now().Add(s.timeout * time.Millisecond)); err != nil { + return nil, err + } + if err = connection.SetWriteDeadline(time.Now().Add(s.timeout * time.Millisecond)); err != nil { + return nil, err + } if err := connection.WriteMsg(query); err != nil { return nil, err diff --git a/scalingo.json b/scalingo.json deleted file mode 100644 index 7527201..0000000 --- a/scalingo.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "ODoH Target and Proxy: Go", - "description": "Deploy and ODoH Target and Proxy", - "keywords": [ - "DNS", - "go", - "ODoH", - "privacy" - ], - "website": "http://github.com/cloudflare/odoh-server-go", - "repository": "http://github.com/cloudflare/odoh-server-go" -} \ No newline at end of file diff --git a/target.go b/target.go index 05c8414..3c65bb1 100644 --- a/target.go +++ b/target.go @@ -23,25 +23,22 @@ package main import ( + "bytes" "encoding/base64" + "errors" "fmt" - "io/ioutil" - "log" - "math/rand" + "io" "net/http" "time" - odoh "github.com/cloudflare/odoh-go" + "github.com/cloudflare/odoh-go" "github.com/miekg/dns" + log "github.com/sirupsen/logrus" ) type targetServer struct { - verbose bool - resolver []resolver - odohKeyPair odoh.ObliviousDoHKeyPair - telemetryClient *telemetry - serverInstanceName string - experimentId string + resolver resolver + odohKeyPair odoh.ObliviousDoHKeyPair } const ( @@ -60,7 +57,7 @@ func (s *targetServer) parseQueryFromRequest(r *http.Request) (*dns.Msg, error) case http.MethodGet: var queryBody string if queryBody = r.URL.Query().Get("dns"); queryBody == "" { - return nil, fmt.Errorf("Missing DNS query parameter in GET request") + return nil, fmt.Errorf("missing DNS query parameter in GET request") } encodedMessage, err := base64.RawURLEncoding.DecodeString(queryBody) @@ -74,8 +71,13 @@ func (s *targetServer) parseQueryFromRequest(r *http.Request) (*dns.Msg, error) return nil, fmt.Errorf("incorrect content type, expected '%s', got %s", dnsMessageContentType, r.Header.Get("Content-Type")) } - defer r.Body.Close() - encodedMessage, err := ioutil.ReadAll(r.Body) + defer func(body io.ReadCloser) { + err := body.Close() + if err != nil { + log.Warn(err) + } + }(r.Body) + encodedMessage, err := io.ReadAll(r.Body) if err != nil { return nil, err } @@ -93,79 +95,69 @@ func (s *targetServer) resolveQueryWithResolver(q *dns.Msg, r resolver) ([]byte, return nil, err } - if s.verbose { - log.Printf("Query=%s\n", packedQuery) - } + log.Debugf("Query=%s\n", packedQuery) start := time.Now() response, err := r.resolve(q) - elapsed := time.Since(start) - - packedResponse, err := response.Pack() if err != nil { - log.Println("Failed encoding DNS response:", err) + log.Println("Resolution failed: ", err) return nil, err } + elapsed := time.Since(start) - if s.verbose { - log.Printf("Answer=%s elapsed=%s\n", packedResponse, elapsed.String()) + var packedResponse []byte + if response != nil { + packedResponse, err = response.Pack() + if err != nil { + log.Warnf("failed encoding DNS response: %s", err) + return nil, err + } + } else { + errMsg := "empty response from resolver" + log.Warnf(errMsg) + return nil, errors.New(errMsg) } + log.Debugf("Answer=%s elapsed=%s\n", packedResponse, elapsed.String()) + return packedResponse, err } func (s *targetServer) dohQueryHandler(w http.ResponseWriter, r *http.Request) { - requestReceivedTime := time.Now() - exp := experiment{} - exp.ExperimentID = s.experimentId - exp.IngestedFrom = s.serverInstanceName - exp.ProtocolType = "ClearText-ODOH" - exp.RequestID = nil - timestamp := runningTime{} - - timestamp.Start = requestReceivedTime.UnixNano() query, err := s.parseQueryFromRequest(r) if err != nil { log.Println("Failed parsing request:", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } - timestamp.TargetQueryDecryptionTime = time.Now().UnixNano() - availableResolvers := len(s.resolver) - chosenResolver := rand.Intn(availableResolvers) - packedResponse, err := s.resolveQueryWithResolver(query, s.resolver[chosenResolver]) + metricTargetValidQueries.Inc() + packedResponse, err := s.resolveQueryWithResolver(query, s.resolver) if err != nil { log.Println("Failed resolving DNS query:", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } - endTime := time.Now().UnixNano() - timestamp.TargetQueryResolutionTime = endTime - timestamp.TargetAnswerEncryptionTime = endTime - timestamp.EndTime = endTime - - exp.Timestamp = timestamp - exp.Resolver = s.resolver[chosenResolver].name() - exp.Status = true - - if s.telemetryClient.logClient != nil { - go s.telemetryClient.streamTelemetryToGCPLogging([]string{exp.serialize()}) - } else if s.telemetryClient.esClient != nil { - go s.telemetryClient.streamDataToElastic([]string{exp.serialize()}) - } w.Header().Set("Content-Type", dnsMessageContentType) - w.Write(packedResponse) + _, err = w.Write(packedResponse) + if err != nil { + log.Warn(err) + } } func (s *targetServer) parseObliviousQueryFromRequest(r *http.Request) (odoh.ObliviousDNSMessage, error) { if r.Method != http.MethodPost { - return odoh.ObliviousDNSMessage{}, fmt.Errorf("Unsupported HTTP method for Oblivious DNS query: %s", r.Method) + return odoh.ObliviousDNSMessage{}, fmt.Errorf("unsupported HTTP method for Oblivious DNS query: %s", r.Method) } - defer r.Body.Close() - encryptedMessageBytes, err := ioutil.ReadAll(r.Body) + defer func(body io.ReadCloser) { + err := body.Close() + if err != nil { + log.Warn(err) + } + }(r.Body) + encryptedMessageBytes, err := io.ReadAll(r.Body) if err != nil { return odoh.ObliviousDNSMessage{}, err } @@ -176,23 +168,16 @@ func (s *targetServer) parseObliviousQueryFromRequest(r *http.Request) (odoh.Obl func (s *targetServer) createObliviousResponseForQuery(context odoh.ResponseContext, dnsResponse []byte) (odoh.ObliviousDNSMessage, error) { response := odoh.CreateObliviousDNSResponse(dnsResponse, 0) odohResponse, err := context.EncryptResponse(response) - - if s.verbose { - log.Printf("Encrypted response: %x", odohResponse) + if err != nil { + return odoh.ObliviousDNSMessage{}, err } + log.Debugf("Encrypted response: %x", odohResponse) + return odohResponse, err } func (s *targetServer) odohQueryHandler(w http.ResponseWriter, r *http.Request) { - requestReceivedTime := time.Now() - exp := experiment{} - exp.ExperimentID = s.experimentId - exp.IngestedFrom = s.serverInstanceName - exp.ProtocolType = "ODOH" - timestamp := runningTime{} - - timestamp.Start = requestReceivedTime.UnixNano() odohMessage, err := s.parseObliviousQueryFromRequest(r) if err != nil { log.Println("parseObliviousQueryFromRequest failed:", err) @@ -200,6 +185,13 @@ func (s *targetServer) odohQueryHandler(w http.ResponseWriter, r *http.Request) return } + keyID := s.odohKeyPair.Config.Contents.KeyID() + receivedKeyID := odohMessage.KeyID + if !bytes.Equal(keyID, receivedKeyID) { + log.Println("received keyID is different from expected key ID") + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + } + obliviousQuery, responseContext, err := s.odohKeyPair.DecryptQuery(odohMessage) if err != nil { log.Println("DecryptQuery failed:", err) @@ -214,66 +206,35 @@ func (s *targetServer) odohQueryHandler(w http.ResponseWriter, r *http.Request) return } - queryParseAndDecryptionCompleteTime := time.Now().UnixNano() - timestamp.TargetQueryDecryptionTime = queryParseAndDecryptionCompleteTime - - chosenResolver := rand.Intn(len(s.resolver)) - packedResponse, err := s.resolveQueryWithResolver(query, s.resolver[chosenResolver]) + metricTargetValidQueries.Inc() + packedResponse, err := s.resolveQueryWithResolver(query, s.resolver) if err != nil { log.Println("resolveQueryWithResolver failed:", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } - queryResolutionCompleteTime := time.Now().UnixNano() - timestamp.TargetQueryResolutionTime = queryResolutionCompleteTime - obliviousResponse, err := s.createObliviousResponseForQuery(responseContext, packedResponse) if err != nil { log.Println("createObliviousResponseForQuery failed:", err) - timestamp.TargetAnswerEncryptionTime = 0 - timestamp.EndTime = 0 - exp.Timestamp = timestamp - exp.Status = false - exp.Resolver = "" - if s.telemetryClient.logClient != nil { - go s.telemetryClient.streamTelemetryToGCPLogging([]string{exp.serialize()}) - } else if s.telemetryClient.esClient != nil { - go s.telemetryClient.streamDataToElastic([]string{exp.serialize()}) - } http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } packedResponseMessage := obliviousResponse.Marshal() - - answerEncryptionAndSerializeCompletionTime := time.Now().UnixNano() - timestamp.TargetAnswerEncryptionTime = answerEncryptionAndSerializeCompletionTime - - if s.verbose { - log.Printf("Target response: %x", packedResponseMessage) - } - - returnResponseTime := time.Now().UnixNano() - timestamp.EndTime = returnResponseTime - - exp.Timestamp = timestamp - exp.Resolver = s.resolver[chosenResolver].name() - exp.Status = true - - if s.telemetryClient.logClient != nil { - go s.telemetryClient.streamTelemetryToGCPLogging([]string{exp.serialize()}) - } else if s.telemetryClient.esClient != nil { - go s.telemetryClient.streamDataToElastic([]string{exp.serialize()}) - } + log.Debugf("target response: %x", packedResponseMessage) w.Header().Set("Content-Type", odohMessageContentType) - w.Write(packedResponseMessage) + log.Debug("sending packedResponseMessage") + _, err = w.Write(packedResponseMessage) + if err != nil { + log.Warn(err) + } } func (s *targetServer) targetQueryHandler(w http.ResponseWriter, r *http.Request) { - if s.verbose { - log.Printf("%s Handling %s\n", r.Method, r.URL.Path) - } + log.Debugf("handling target request") + + metricTargetQueries.Inc() if r.Header.Get("Content-Type") == dnsMessageContentType { s.dohQueryHandler(w, r) @@ -285,10 +246,11 @@ func (s *targetServer) targetQueryHandler(w http.ResponseWriter, r *http.Request } } -func (s *targetServer) configHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("%s Handling %s\n", r.Method, r.URL.Path) - +func (s *targetServer) configHandler(w http.ResponseWriter, _ *http.Request) { configSet := []odoh.ObliviousDoHConfig{s.odohKeyPair.Config} configs := odoh.CreateObliviousDoHConfigs(configSet) - w.Write(configs.Marshal()) + _, err := w.Write(configs.Marshal()) + if err != nil { + log.Warn(err) + } } diff --git a/target.yaml b/target.yaml deleted file mode 100644 index 65390ba..0000000 --- a/target.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# [START runtime] -service: odoh-target -runtime: go114 -env_variables: - SEED_SECRET_KEY: "99fe7e7aa97178f40c8cc145d38b3d25" - GOOGLE_APPLICATION_CREDENTIALS: "odoh-target-service-account.json" - TARGET_INSTANCE_NAME: "server_target_gcp" - EXPERIMENT_ID: "EXP_1" -# [END runtime] diff --git a/target_test.go b/target_test.go index d9bdca5..b7c70b6 100644 --- a/target_test.go +++ b/target_test.go @@ -28,13 +28,14 @@ import ( "encoding/base64" "errors" "fmt" - "io/ioutil" + "io" "net" "net/http" "net/http/httptest" "testing" + "time" - odoh "github.com/cloudflare/odoh-go" + "github.com/cloudflare/odoh-go" "github.com/miekg/dns" ) @@ -43,10 +44,6 @@ type localResolver struct { queryResponseMap map[string][]byte // Packed DNS queries to responses } -func (r localResolver) name() string { - return "localResolver" -} - func (r localResolver) resolve(query *dns.Msg) (*dns.Msg, error) { packedQuery, err := query.Pack() if err != nil { @@ -55,7 +52,7 @@ func (r localResolver) resolve(query *dns.Msg) (*dns.Msg, error) { packed, ok := r.queryResponseMap[string(packedQuery)] if !ok { - return nil, errors.New("Failed to resolve") + return nil, errors.New("failed to resolve") } response := &dns.Msg{} @@ -103,11 +100,14 @@ func createLocalResolver(t *testing.T) *localResolver { func createKeyPair(t *testing.T) odoh.ObliviousDoHKeyPair { seed := make([]byte, defaultSeedLength) - rand.Read(seed) + _, err := rand.Read(seed) + if err != nil { + t.Fatal(err) + } keyPair, err := odoh.CreateKeyPairFromSeed(kemID, kdfID, aeadID, seed) if err != nil { - t.Fatal("Failed to create a private key. Exiting now.") + t.Fatal("failed to create a private key. Exiting now.") } return keyPair @@ -115,9 +115,8 @@ func createKeyPair(t *testing.T) odoh.ObliviousDoHKeyPair { func createTarget(t *testing.T, r resolver) targetServer { return targetServer{ - resolver: []resolver{r}, - odohKeyPair: createKeyPair(t), - telemetryClient: getTelemetryInstance("LOG"), + resolver: r, + odohKeyPair: createKeyPair(t), } } @@ -131,7 +130,7 @@ func TestConfigHandler(t *testing.T) { handler := http.HandlerFunc(target.configHandler) - request, err := http.NewRequest("GET", configEndpoint, nil) + request, err := http.NewRequest("GET", "/.well-known/odohconfigs", nil) if err != nil { t.Fatal(err) } @@ -140,10 +139,10 @@ func TestConfigHandler(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Code; status != http.StatusOK { - t.Fatal(fmt.Errorf("Failed request with error code: %d", status)) + t.Fatal(fmt.Errorf("failed request with error code: %d", status)) } - body, err := ioutil.ReadAll(rr.Result().Body) + body, err := io.ReadAll(rr.Result().Body) if err != nil { t.Fatal("Failed to read body:", err) } @@ -152,13 +151,14 @@ func TestConfigHandler(t *testing.T) { t.Fatal("Received invalid config") } } + func TestQueryHandlerInvalidContentType(t *testing.T) { r := createLocalResolver(t) target := createTarget(t, r) handler := http.HandlerFunc(target.targetQueryHandler) - request, err := http.NewRequest("GET", queryEndpoint, nil) + request, err := http.NewRequest("GET", "/dns-query", nil) if err != nil { t.Fatal(err) } @@ -168,7 +168,7 @@ func TestQueryHandlerInvalidContentType(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Result().StatusCode; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Result did not yield %d, got %d instead", http.StatusBadRequest, status)) + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusBadRequest, status)) } } @@ -179,7 +179,7 @@ func TestQueryHandlerDoHWithPOST(t *testing.T) { handler := http.HandlerFunc(target.targetQueryHandler) q := r.queries[0] - request, err := http.NewRequest(http.MethodPost, queryEndpoint, bytes.NewReader([]byte(q))) + request, err := http.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader([]byte(q))) if err != nil { t.Fatal(err) } @@ -189,18 +189,18 @@ func TestQueryHandlerDoHWithPOST(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Result().StatusCode; status != http.StatusOK { - t.Fatal(fmt.Errorf("Result did not yield %d, got %d instead", http.StatusOK, status)) + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusOK, status)) } if rr.Result().Header.Get("Content-Type") != dnsMessageContentType { - t.Fatal("Invalid content type response") + t.Fatal("invalid content type response") } - responseBody, err := ioutil.ReadAll(rr.Result().Body) + responseBody, err := io.ReadAll(rr.Result().Body) if err != nil { t.Fatal(err) } if !bytes.Equal(responseBody, r.queryResponseMap[q]) { - t.Fatal("Incorrect response received") + t.Fatal("incorrect response received") } } @@ -213,7 +213,7 @@ func TestQueryHandlerDoHWithGET(t *testing.T) { q := r.queries[0] encodedQuery := base64.RawURLEncoding.EncodeToString([]byte(q)) - request, err := http.NewRequest(http.MethodGet, queryEndpoint+"?dns="+encodedQuery, nil) + request, err := http.NewRequest(http.MethodGet, "/dns-query?dns="+encodedQuery, nil) if err != nil { t.Fatal(err) } @@ -223,18 +223,18 @@ func TestQueryHandlerDoHWithGET(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Result().StatusCode; status != http.StatusOK { - t.Fatal(fmt.Errorf("Result did not yield %d, got %d instead", http.StatusOK, status)) + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusOK, status)) } if rr.Result().Header.Get("Content-Type") != dnsMessageContentType { - t.Fatal("Invalid content type response") + t.Fatal("invalid content type response") } - responseBody, err := ioutil.ReadAll(rr.Result().Body) + responseBody, err := io.ReadAll(rr.Result().Body) if err != nil { t.Fatal(err) } if !bytes.Equal(responseBody, r.queryResponseMap[q]) { - t.Fatal("Incorrect response received") + t.Fatal("incorrect response received") } } @@ -246,7 +246,7 @@ func TestQueryHandlerDoHWithInvalidMethod(t *testing.T) { q := r.queries[0] encodedQuery := base64.RawURLEncoding.EncodeToString([]byte(q)) - request, err := http.NewRequest(http.MethodPut, queryEndpoint+"?dns="+encodedQuery, nil) + request, err := http.NewRequest(http.MethodPut, "/dns-query"+"?dns="+encodedQuery, nil) if err != nil { t.Fatal(err) } @@ -256,7 +256,7 @@ func TestQueryHandlerDoHWithInvalidMethod(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Result().StatusCode; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Result did not yield %d, got %d instead", http.StatusBadRequest, status)) + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusBadRequest, status)) } } @@ -273,7 +273,7 @@ func TestQueryHandlerODoHWithInvalidMethod(t *testing.T) { t.Fatal(err) } - request, err := http.NewRequest(http.MethodGet, queryEndpoint, bytes.NewReader(encryptedQuery.Marshal())) + request, err := http.NewRequest(http.MethodGet, "/dns-query", bytes.NewReader(encryptedQuery.Marshal())) if err != nil { t.Fatal(err) } @@ -283,7 +283,7 @@ func TestQueryHandlerODoHWithInvalidMethod(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Result().StatusCode; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Result did not yield %d, got %d instead", http.StatusBadRequest, status)) + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusBadRequest, status)) } } @@ -300,7 +300,7 @@ func TestQueryHandlerODoH(t *testing.T) { t.Fatal(err) } - request, err := http.NewRequest(http.MethodPost, queryEndpoint, bytes.NewReader(encryptedQuery.Marshal())) + request, err := http.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(encryptedQuery.Marshal())) if err != nil { t.Fatal(err) } @@ -310,13 +310,13 @@ func TestQueryHandlerODoH(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Result().StatusCode; status != http.StatusOK { - t.Fatal(fmt.Errorf("Result did not yield %d, got %d instead", http.StatusOK, status)) + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusOK, status)) } if rr.Result().Header.Get("Content-Type") != odohMessageContentType { - t.Fatal("Invalid content type response") + t.Fatal("invalid content type response") } - responseBody, err := ioutil.ReadAll(rr.Result().Body) + responseBody, err := io.ReadAll(rr.Result().Body) if err != nil { t.Fatal(err) } @@ -332,7 +332,7 @@ func TestQueryHandlerODoH(t *testing.T) { } if !bytes.Equal(response, r.queryResponseMap[q]) { - t.Fatal(fmt.Errorf("Incorrect response received. Got %v, expected %v", response, r.queryResponseMap[q])) + t.Fatal(fmt.Errorf("incorrect response received. Got %v, expected %v", response, r.queryResponseMap[q])) } } @@ -350,7 +350,7 @@ func TestQueryHandlerODoHWithInvalidKey(t *testing.T) { t.Fatal(err) } - request, err := http.NewRequest(http.MethodPost, queryEndpoint, bytes.NewReader(encryptedQuery.Marshal())) + request, err := http.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(encryptedQuery.Marshal())) if err != nil { t.Fatal(err) } @@ -359,8 +359,8 @@ func TestQueryHandlerODoHWithInvalidKey(t *testing.T) { rr := httptest.NewRecorder() handler.ServeHTTP(rr, request) - if status := rr.Result().StatusCode; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Result did not yield %d, got %d instead", http.StatusBadRequest, status)) + if status := rr.Result().StatusCode; status != http.StatusUnauthorized { + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusBadRequest, status)) } } @@ -379,7 +379,59 @@ func TestQueryHandlerODoHWithCorruptCiphertext(t *testing.T) { queryBytes := encryptedQuery.Marshal() queryBytes[len(queryBytes)-1] ^= 0xFF - request, err := http.NewRequest(http.MethodPost, queryEndpoint, bytes.NewReader(queryBytes)) + request, err := http.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(queryBytes)) + if err != nil { + t.Fatal(err) + } + request.Header.Add("Content-Type", odohMessageContentType) + + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, request) + + if status := rr.Result().StatusCode; status != http.StatusBadRequest { + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusBadRequest, status)) + } +} + +func TestQueryHandlerODoHWithMalformedQuery(t *testing.T) { + r := createLocalResolver(t) + target := createTarget(t, r) + + handler := http.HandlerFunc(target.targetQueryHandler) + + // malformed odoh query + queryBytes := []byte{1, 2, 3} + request, err := http.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(queryBytes)) + if err != nil { + t.Fatal(err) + } + request.Header.Add("Content-Type", odohMessageContentType) + + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, request) + + if status := rr.Result().StatusCode; status != http.StatusBadRequest { + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusBadRequest, status)) + } +} + +func TestODoHResolutionWithRealResolver(t *testing.T) { + r := &targetResolver{ + timeout: 2500 * time.Millisecond, + nameserver: "1.1.1.1:53", + } + target := createTarget(t, r) + + handler := http.HandlerFunc(target.targetQueryHandler) + + // malformed DNS query + obliviousQuery := odoh.CreateObliviousDNSQuery([]byte{1, 2, 3}, 0) + encryptedQuery, _, err := target.odohKeyPair.Config.Contents.EncryptQuery(obliviousQuery) + if err != nil { + t.Fatal(err) + } + + request, err := http.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(encryptedQuery.Marshal())) if err != nil { t.Fatal(err) } @@ -389,6 +441,68 @@ func TestQueryHandlerODoHWithCorruptCiphertext(t *testing.T) { handler.ServeHTTP(rr, request) if status := rr.Result().StatusCode; status != http.StatusBadRequest { - t.Fatal(fmt.Errorf("Result did not yield %d, got %d instead", http.StatusBadRequest, status)) + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusBadRequest, status)) } + + handler = target.targetQueryHandler + + // valid dns query + q := new(dns.Msg) + q.SetQuestion("example.com.", dns.TypeA) + packedQuery, err := q.Pack() + if err != nil { + t.Fatal(err) + } + obliviousQuery = odoh.CreateObliviousDNSQuery(packedQuery, 0) + encryptedQuery, _, err = target.odohKeyPair.Config.Contents.EncryptQuery(obliviousQuery) + if err != nil { + t.Fatal(err) + } + + request, err = http.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(encryptedQuery.Marshal())) + if err != nil { + t.Fatal(err) + } + request.Header.Add("Content-Type", odohMessageContentType) + + rr = httptest.NewRecorder() + handler.ServeHTTP(rr, request) + + if status := rr.Result().StatusCode; status != http.StatusOK { + t.Fatal(fmt.Errorf("result did not yield %d, got %d instead", http.StatusOK, status)) + } + if rr.Result().Header.Get("Content-Type") != odohMessageContentType { + t.Fatal("Invalid content type response") + } +} + +func TestTargetTimeout(t *testing.T) { + r := &targetResolver{ + timeout: time.Second, + nameserver: "127.127.127.127:0", + } + target := createTarget(t, r) + handler := http.HandlerFunc(target.targetQueryHandler) + + // valid dns query + q := new(dns.Msg) + q.SetQuestion("example.com.", dns.TypeA) + packedQuery, err := q.Pack() + if err != nil { + t.Fatal(err) + } + obliviousQuery := odoh.CreateObliviousDNSQuery(packedQuery, 0) + encryptedQuery, _, err := target.odohKeyPair.Config.Contents.EncryptQuery(obliviousQuery) + if err != nil { + t.Fatal(err) + } + + request, err := http.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(encryptedQuery.Marshal())) + if err != nil { + t.Fatal(err) + } + request.Header.Add("Content-Type", odohMessageContentType) + + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, request) } diff --git a/telemetry.go b/telemetry.go deleted file mode 100644 index 62502f6..0000000 --- a/telemetry.go +++ /dev/null @@ -1,146 +0,0 @@ -// The MIT License -// -// Copyright (c) 2020, Cloudflare, Inc. All rights reserved. -// -// 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. - -package main - -import ( - "cloud.google.com/go/logging" - "context" - "encoding/json" - "github.com/elastic/go-elasticsearch/v8" - "github.com/elastic/go-elasticsearch/v8/esapi" - "log" - "net/http" - "strings" - "sync" -) - -// This runningTime structure contains the epoch timestamps for the following operations -// 1. Start => Epoch time at which the request is received by the ObliviousDNSHandler -// 2. TargetQueryDecryptionTime => Epoch -type runningTime struct { - Start int64 - TargetQueryDecryptionTime int64 - TargetQueryResolutionTime int64 - TargetAnswerEncryptionTime int64 - EndTime int64 -} - -type experiment struct { - RequestID []byte - Resolver string - Timestamp runningTime - Status bool - IngestedFrom string - ExperimentID string - ProtocolType string -} - -func (e *experiment) serialize() string { - exp := &e - response, err := json.Marshal(exp) - if err != nil { - log.Printf("Unable to log the information correctly.") - } - return string(response) -} - -type telemetry struct { - sync.RWMutex - esClient *elasticsearch.Client - buffer []string - logClient *logging.Client - cloudlogger *logging.Logger -} - -const ( - INDEX = "server_telemetry" - TYPE = "client_localhost" -) - -var telemetryInstance telemetry - -func getTelemetryInstance(telemetryType string) *telemetry { - var err error - if telemetryType == "ELK" { - elasticsearchTransport := elasticsearch.Config{ - Addresses: []string{ - "http://localhost:9200", - }, - Transport: &http.Transport{ - MaxIdleConnsPerHost: 1024, - }, - } - telemetryInstance.esClient, err = elasticsearch.NewClient(elasticsearchTransport) - if err != nil { - log.Fatalf("Unable to create an elasticsearch client connection.") - } - } else if telemetryType == "GCP" { - ctx := context.Background() - projectID := "odoh-target" - telemetryInstance.logClient, err = logging.NewClient(ctx, projectID) - if err != nil { - log.Fatalf("Unable to create a logging instance to Google Cloud %v", err) - } - logName := "odohserver-gcp" - telemetryInstance.cloudlogger = telemetryInstance.logClient.Logger(logName) - } else { - telemetryInstance.cloudlogger = nil - telemetryInstance.esClient = nil - } - return &telemetryInstance -} - -func (t *telemetry) streamTelemetryToGCPLogging(dataItems []string) { - defer t.cloudlogger.Flush() - for _, item := range dataItems { - log.Printf("Logging %v to the GCP instance\n", item) - t.cloudlogger.Log(logging.Entry{Payload: item}) - } -} - -func (t *telemetry) streamDataToElastic(dataItems []string) { - var wg sync.WaitGroup - for index, item := range dataItems { - wg.Add(1) - go func(i int, message string) { - defer wg.Done() - req := esapi.IndexRequest{ - Index: INDEX, - Body: strings.NewReader(message), - Refresh: "true", - } - - res, err := req.Do(context.Background(), t.esClient) - if err != nil { - log.Printf("Unable to send the request to elastic.") - } - defer res.Body.Close() - if res.IsError() { - log.Printf("[%s] Error Indexing Value [%s]", res.Status(), message) - } else { - log.Printf("Successfully Inserted [%s]", message) - } - }(index, item) - } - wg.Wait() -}