REPOSITORY / ScuroNeko/mtg

Compare commits

DIFF REPOSITORY

Compare commits

..
1 Commit
Author SHA1 Message Date
9seconds 68e1d66017 Merge remote-tracking branch 'origin/master' into stable 2019-11-18 12:56:21 +03:00
89 changed files with 636 additions and 1604 deletions
+1 -7
View File
@@ -1,9 +1,3 @@
version.go
vendor/ vendor/
tags tags
.github/
.bin/
*.md
mtg
.golangci.toml
.gitignore
run.sh
-134
View File
@@ -1,134 +0,0 @@
---
name: CI
on:
push:
tags:
- v*
branches:
- master
- stable
- v1
release:
types:
- published
- released
pull_request:
types:
- opened
- edited
- reopened
- synchronize
- ready_for_review
jobs:
test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
matrix:
go_version:
- ~1.14
- ~1.15
- ~1.16
- ^1.17
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: recursive
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go_version }}
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: Run tests
run: go test -coverprofile=./coverage.txt -covermode=atomic -v ./...
- name: Collect coverage
uses: codecov/codecov-action@v1
with:
file: ./coverage.txt
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: recursive
- name: Run linter
uses: golangci/golangci-lint-action@v2
with:
version: v1.42.0
docker:
name: Docker
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: recursive
- name: Get Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: nineseconds/mtg,ghcr.io/9seconds/mtg
tag-semver: "{{version}},{{major}},{{major}}.{{minor}}"
- name: Setup QEMU
uses: docker/setup-qemu-action@v1
- name: Setup BuildX
uses: docker/setup-buildx-action@v1
- name: Setup cache
uses: actions/cache@v2
with:
path: /tmp/buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Login to GHCR.io
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GH_PAT }}
- name: Build and push
uses: docker/build-push-action@v2
with:
pull: true
context: .
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
cache-from: type=local,src=/tmp/buildx-cache
cache-to: type=local,dest=/tmp/buildx-cache
-69
View File
@@ -1,69 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches:
- master
- stable
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '24 20 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+1 -1
View File
@@ -7,5 +7,5 @@
*.out *.out
mtg mtg
vendor/ vendor/
version.go
ccbuilds/ ccbuilds/
.bin/
+1 -1
View File
@@ -10,4 +10,4 @@ format = "colored-line-number"
[linters] [linters]
enable-all = true enable-all = true
disable = ["gochecknoglobals", "gas", "gomnd", "goerr113", "exhaustivestruct"] disable = ["gochecknoglobals"]
+19
View File
@@ -0,0 +1,19 @@
---
language: go
sudo: false
dist: trusty
go:
- 1.13.x
- master
before_script: make prepare
script:
- make all
- make lint
matrix:
allow_failures:
- go: master
+13 -9
View File
@@ -1,7 +1,7 @@
############################################################################### ###############################################################################
# BUILD STAGE # BUILD STAGE
FROM golang:1.17-alpine AS build FROM golang:1.13-alpine
RUN set -x \ RUN set -x \
&& apk --no-cache --update add \ && apk --no-cache --update add \
@@ -9,13 +9,15 @@ RUN set -x \
ca-certificates \ ca-certificates \
curl \ curl \
git \ git \
make make \
upx
COPY . /app COPY . /go/src/github.com/9seconds/mtg/
WORKDIR /app
RUN set -x \ RUN set -x \
&& make -j 4 static && cd /go/src/github.com/9seconds/mtg \
&& make -j 4 static \
&& upx --ultra-brute -qq ./mtg
############################################################################### ###############################################################################
@@ -24,9 +26,11 @@ RUN set -x \
FROM scratch FROM scratch
ENTRYPOINT ["/mtg"] ENTRYPOINT ["/mtg"]
ENV MTG_BIND=0.0.0.0:3128 \ ENV MTG_IP=0.0.0.0 \
MTG_STATS_BIND=0.0.0.0:3129 MTG_PORT=3128 \
MTG_STATS_IP=0.0.0.0 \
MTG_STATS_PORT=3129
EXPOSE 3128 3129 EXPOSE 3128 3129
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=build /app/mtg /mtg COPY --from=0 /go/src/github.com/9seconds/mtg/mtg /mtg
+29 -47
View File
@@ -4,89 +4,71 @@ APP_NAME := $(IMAGE_NAME)
CC_BINARIES := $(shell bash -c "echo -n $(APP_NAME)-{linux,freebsd,openbsd}-{386,amd64} $(APP_NAME)-linux-{arm,arm64}") CC_BINARIES := $(shell bash -c "echo -n $(APP_NAME)-{linux,freebsd,openbsd}-{386,amd64} $(APP_NAME)-linux-{arm,arm64}")
GOLANGCI_LINT_VERSION := v1.42.0 GOLANGCI_LINT_VERSION := v1.21.0
VERSION_GO := $(shell go version) VERSION_GO := $(shell go version)
VERSION_DATE := $(shell date -Ru) VERSION_DATE := $(shell date -Ru)
VERSION_TAG := $(shell git describe --tags --always) VERSION_TAG := $(shell git describe --tags --always)
COMMON_BUILD_FLAGS := -mod=readonly -ldflags="-s -w -X 'main.version=$(VERSION_TAG) ($(VERSION_GO)) [$(VERSION_DATE)]'" COMMON_BUILD_FLAGS := -ldflags="-s -w -X 'main.version=$(VERSION_TAG) ($(VERSION_GO)) [$(VERSION_DATE)]'"
GOBIN := $(ROOT_DIR)/.bin MOD_ON := env GO111MODULE=on
GOTOOL := env "GOBIN=$(GOBIN)" "PATH=$(ROOT_DIR)/.bin:$(PATH)" MOD_OFF := env GO111MODULE=auto
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
.PHONY: all $(APP_NAME):
all: build @$(MOD_ON) go build $(COMMON_BUILD_FLAGS) -o "$(APP_NAME)"
.PHONY: build static-$(APP_NAME):
build: @$(MOD_ON) env CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo $(COMMON_BUILD_FLAGS) -o "$(APP_NAME)"
@go build $(COMMON_BUILD_FLAGS) -o "$(APP_NAME)"
$(APP_NAME): build
.PHONY: static
static:
@env CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo $(COMMON_BUILD_FLAGS) -o "$(APP_NAME)"
$(APP_NAME)-%: GOOS=$(shell echo -n "$@" | sed 's?$(APP_NAME)-??' | cut -f1 -d-) $(APP_NAME)-%: GOOS=$(shell echo -n "$@" | sed 's?$(APP_NAME)-??' | cut -f1 -d-)
$(APP_NAME)-%: GOARCH=$(shell echo -n "$@" | sed 's?$(APP_NAME)-??' | cut -f2 -d-) $(APP_NAME)-%: GOARCH=$(shell echo -n "$@" | sed 's?$(APP_NAME)-??' | cut -f2 -d-)
$(APP_NAME)-%: ccbuilds $(APP_NAME)-%: ccbuilds
@env "GOOS=$(GOOS)" "GOARCH=$(GOARCH)" \ @$(MOD_ON) env "GOOS=$(GOOS)" "GOARCH=$(GOARCH)" \
go build \ go build \
$(COMMON_BUILD_FLAGS) \ $(COMMON_BUILD_FLAGS) \
-o "./ccbuilds/$(APP_NAME)-$(GOOS)-$(GOARCH)" -o "./ccbuilds/$(APP_NAME)-$(GOOS)-$(GOARCH)"
.PHONY: ccbuilds
ccbuilds: ccbuilds:
@rm -rf ./ccbuilds && mkdir -p ./ccbuilds @rm -rf ./ccbuilds && mkdir -p ./ccbuilds
vendor: go.mod go.sum vendor: go.mod go.sum
@$(MOD_ON) go mod vendor @$(MOD_ON) go mod vendor
.PHONY: test # -----------------------------------------------------------------------------
test:
@go test -v ./...
.PHONY: citest .PHONY: all
citest: all: $(APP_NAME)
@go test -coverprofile=coverage.txt -covermode=atomic -race -v ./...
.PHONY: static
static: static-$(APP_NAME)
.PHONY: crosscompile .PHONY: crosscompile
crosscompile: $(CC_BINARIES) crosscompile: $(CC_BINARIES)
.PHONY: crosscompile-dir
crosscompile-dir:
@rm -rf "$(CC_DIR)" && mkdir -p "$(CC_DIR)"
.PHONY: lint
lint: vendor
@$(MOD_OFF) golangci-lint run
.PHONY: clean .PHONY: clean
clean: clean:
@git clean -xfd && \ @git clean -xfd && \
git reset --hard >/dev/null && \ git reset --hard >/dev/null && \
git submodule foreach --recursive sh -c 'git clean -xfd && git reset --hard' >/dev/null git submodule foreach --recursive sh -c 'git clean -xfd && git reset --hard' >/dev/null
.PHONY: lint
lint:
@$(GOTOOL) golangci-lint run
.PHONY: docker .PHONY: docker
docker: docker:
@docker build --pull -t "$(IMAGE_NAME)" "$(ROOT_DIR)" @docker build --pull -t "$(IMAGE_NAME)" "$(ROOT_DIR)"
.PHONY: doc .PHONY: prepare
doc: prepare: install-lint
@$(GOTOOL) godoc -http 0.0.0.0:10000
.PHONY: install-tools .PHONY: install-lint
install-tools: install-tools-lint install-tools-godoc install-lint:
@curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh \
.PHONY: install-tools-lint | $(MOD_OFF) bash -s -- -b $(GOPATH)/bin $(GOLANGCI_LINT_VERSION)
install-tools-lint:
@mkdir -p "$(GOBIN)" || true && \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh \
| bash -s -- -b "$(GOBIN)" "$(GOLANGCI_LINT_VERSION)"
.PHONY: install-tools-godoc
install-tools-godoc:
@mkdir -p "$(GOBIN)" || true && \
$(GOTOOL) go get -u golang.org/x/tools/cmd/godoc
.PHONY: update-deps
upgrade-deps:
$go get -u && go mod tidy
+4 -9
View File
@@ -159,9 +159,6 @@ by design and we have the negligible possibility of duplication
(probability is 1/(2^64)) but it could be quite effective to prevent (probability is 1/(2^64)) but it could be quite effective to prevent
replays. replays.
It is possible to disable this cache. To do that, please explicitly set
its size to 0.
## FakeTLS ## FakeTLS
@@ -189,18 +186,16 @@ supported environment variables:
| `MTG_IPV6` | `-6`, `--public-ipv6` | [Autodetect](https://ifconfig.co) | IPv6 address:port of this proxy. This is required if you NAT your proxy or run it in a docker container. In that case, you absolutely need to specify public IPv6 address of the proxy, otherwise either URLs will be broken or proxy could not access Telegram middle proxies. | | `MTG_IPV6` | `-6`, `--public-ipv6` | [Autodetect](https://ifconfig.co) | IPv6 address:port of this proxy. This is required if you NAT your proxy or run it in a docker container. In that case, you absolutely need to specify public IPv6 address of the proxy, otherwise either URLs will be broken or proxy could not access Telegram middle proxies. |
| `MTG_STATS_BIND` | `-t`, `--stats-bind` | `127.0.0.1:3129` | Which hist:port should we bind the internal statistics HTTP server (Prometheus). | | `MTG_STATS_BIND` | `-t`, `--stats-bind` | `127.0.0.1:3129` | Which hist:port should we bind the internal statistics HTTP server (Prometheus). |
| `MTG_STATS_NAMESPACE` | `--stats-namespace` | `mtg` | Which namespace should be used for prometheus metrics. | | `MTG_STATS_NAMESPACE` | `--stats-namespace` | `mtg` | Which namespace should be used for prometheus metrics. |
| `MTG_STATSD_ADDR` | `--statsd-addr` | | host:port of statsd service. No defaults, by default we do not send anything there. | | `MTG_STATSD_ADDR` | `--statsd-addr` | | IP:host addresses of statsd service. No defaults, by defaults we do not send anything there. |
| `MTG_STATSD_PORT` | `--statsd-port` | `8125` | Which port should we use to work with statsd. |
| `MTG_STATSD_PREFIX` | `--statsd-prefix` | `mtg` | Which bucket prefix we should use. For example, if you set `mtg`, then metric `traffic.ingress` would be send as `mtg.traffic.ingress`. | | `MTG_STATSD_PREFIX` | `--statsd-prefix` | `mtg` | Which bucket prefix we should use. For example, if you set `mtg`, then metric `traffic.ingress` would be send as `mtg.traffic.ingress`. |
| `MTG_STATSD_TAGS_FORMAT` | `--statsd-tags-format` | | Which tags format we should use. By default, we are using default vanilla statsd tags format but if you want to send directly to InfluxDB or Datadog, please specify it there. Possible options are `influxdb` and `datadog`. | | `MTG_STATSD_TAGS_FORMAT` | `--statsd-tags-format` | | Which tags format we should use. By default, we are using default vanilla statsd tags format but if you want to send directly to InfluxDB or Datadog, please specify it there. Possible options are `influxdb` and `datadog`. |
| `MTG_STATSD_TAGS` | `--statsd-tags` | | Which tags should we send to statsd with our metrics. Please specify them as `key=value` pairs. | | `MTG_STATSD_TAGS` | `--statsd-tags` | | Which tags should we send to statsd with our metrics. Please specify them as `key=value` pairs. |
| `MTG_BUFFER_WRITE` | `-w`, `--write-buffer` | `32KB` | The size of TCP write buffer in bytes. Write buffer is the buffer for messages which are going from client to Telegram. | | `MTG_BUFFER_WRITE` | `-w`, `--write-buffer` | `65536` | The size of TCP write buffer in bytes. Write buffer is the buffer for messages which are going from client to Telegram. |
| `MTG_BUFFER_READ` | `-r`, `--read-buffer` | `32KB` | The size of TCP read buffer in bytes. Read buffer is the buffer for messages from Telegram to client. | | `MTG_BUFFER_READ` | `-r`, `--read-buffer` | `131072` | The size of TCP read buffer in bytes. Read buffer is the buffer for messages from Telegram to client. |
| `MTG_ANTIREPLAY_MAXSIZE` | `--anti-replay-max-size` | `128MB` | Max size of antireplay cache. | | `MTG_ANTIREPLAY_MAXSIZE` | `--anti-replay-max-size` | `128MB` | Max size of antireplay cache. |
| `MTG_CLOAK_PORT` | `--cloak-port` | `443` | Which port we should use to connect to cloaked host in FakeTLS mode. | | `MTG_CLOAK_PORT` | `--cloak-port` | `443` | Which port we should use to connect to cloaked host in FakeTLS mode. |
| `MTG_MULTIPLEX_PERCONNECTION` | `--multiplex-per-connection` | `50` | How many client connections can share a single Telegram connection in adtag mode | | `MTG_MULTIPLEX_PERCONNECTION` | `--multiplex-per-connection` | `50` | How many client connections can share a single Telegram connection in adtag mode |
| `MTG_NTP_SERVERS` | `--ntp-server` | default pool | A list of NTP servers to use. |
| `MTG_PREFER_DIRECT_IP` | `--prefer-ip` | `ipv6` | Which IP protocol to prefer if possible. Works mostly in direct mode. |
Usually you want to modify only read/write buffer sizes. If you feel Usually you want to modify only read/write buffer sizes. If you feel
that proxy is slow, try to increase both sizes giving more priority to that proxy is slow, try to increase both sizes giving more priority to
-24
View File
@@ -1,24 +0,0 @@
# Security Policy
## Supported Versions
We support 2 tracks: 2.x and 1.x
| Version | Supported |
| ------- | ------------------ |
| 2.x | :white_check_mark: |
| 1.x | :interrobang: |
| < 1.0 | :x: |
1.x has adtag support. We do not plan any active development there but we guarantee:
1. Regular dependency updates
2. Updates for Golang versions
3. Security vulnerability fixes
4. Merging small PRs
2.x is in active development and have a full support.
## Reporting a Vulnerability
If you have found a vulnerability, please report about it to nineseconds@yandex.ru, telegram @9seconds or here in issues
+4 -4
View File
@@ -11,19 +11,19 @@ type cache struct {
data *fastcache.Cache data *fastcache.Cache
} }
func (c cache) AddObfuscated2(data []byte) { func (c *cache) AddObfuscated2(data []byte) {
c.data.Set(keyObfuscated2(data), nil) c.data.Set(keyObfuscated2(data), nil)
} }
func (c cache) AddTLS(data []byte) { func (c *cache) AddTLS(data []byte) {
c.data.Set(keyTLS(data), nil) c.data.Set(keyTLS(data), nil)
} }
func (c cache) HasObfuscated2(data []byte) bool { func (c *cache) HasObfuscated2(data []byte) bool {
return c.data.Has(keyObfuscated2(data)) return c.data.Has(keyObfuscated2(data))
} }
func (c cache) HasTLS(data []byte) bool { func (c *cache) HasTLS(data []byte) bool {
return c.data.Has(keyTLS(data)) return c.data.Has(keyTLS(data))
} }
+4 -14
View File
@@ -3,28 +3,18 @@ package antireplay
import ( import (
"sync" "sync"
"github.com/9seconds/mtg/config"
"github.com/VictoriaMetrics/fastcache" "github.com/VictoriaMetrics/fastcache"
"mtg/config"
) )
type CacheInterface interface {
AddObfuscated2([]byte)
AddTLS([]byte)
HasObfuscated2([]byte) bool
HasTLS([]byte) bool
}
var ( var (
Cache CacheInterface Cache cache
initOnce sync.Once initOnce sync.Once
) )
func Init() { func Init() {
initOnce.Do(func() { initOnce.Do(func() {
if config.C.AntiReplayMaxSize == 0 { Cache.data = fastcache.New(config.C.AntiReplayMaxSize)
Cache = nilCache{}
} else {
Cache = cache{fastcache.New(config.C.AntiReplayMaxSize)}
}
}) })
} }
-8
View File
@@ -1,8 +0,0 @@
package antireplay
type nilCache struct{}
func (n nilCache) AddObfuscated2(_ []byte) {}
func (n nilCache) AddTLS(_ []byte) {}
func (n nilCache) HasObfuscated2(_ []byte) bool { return false }
func (n nilCache) HasTLS(_ []byte) bool { return false }
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"github.com/9seconds/mtg/config" "mtg/config"
) )
func Generate(secretType, hostname string) { func Generate(secretType, hostname string) {
+13 -12
View File
@@ -5,21 +5,22 @@ import (
"os" "os"
"time" "time"
"github.com/9seconds/mtg/antireplay"
"github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/faketls"
"github.com/9seconds/mtg/hub"
"github.com/9seconds/mtg/ntp"
"github.com/9seconds/mtg/obfuscated2"
"github.com/9seconds/mtg/proxy"
"github.com/9seconds/mtg/stats"
"github.com/9seconds/mtg/telegram"
"github.com/9seconds/mtg/utils"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
"mtg/antireplay"
"mtg/config"
"mtg/faketls"
"mtg/hub"
"mtg/ntp"
"mtg/obfuscated2"
"mtg/proxy"
"mtg/stats"
"mtg/telegram"
"mtg/utils"
) )
func Proxy() error { // nolint: funlen,cyclop func Proxy() error { // nolint: funlen
ctx := utils.GetSignalContext() ctx := utils.GetSignalContext()
atom := zap.NewAtomicLevel() atom := zap.NewAtomicLevel()
@@ -49,7 +50,7 @@ func Proxy() error { // nolint: funlen,cyclop
zap.S().Debugw("Configuration", "config", config.Printable()) zap.S().Debugw("Configuration", "config", config.Printable())
if config.C.MiddleProxyMode() { if len(config.C.AdTag) > 0 {
zap.S().Infow("Use middle proxy connection to Telegram") zap.S().Infow("Use middle proxy connection to Telegram")
diff, err := ntp.Fetch() diff, err := ntp.Fetch()
+2 -2
View File
@@ -9,7 +9,7 @@ import (
func Fatal(arg interface{}) { func Fatal(arg interface{}) {
if value, ok := arg.(error); ok { if value, ok := arg.(error); ok {
arg = fmt.Errorf("fatal error: %+v", value) // nolint: errorlint arg = fmt.Errorf("fatal error: %+v", value)
} }
PrintStderr(arg) PrintStderr(arg)
@@ -21,7 +21,7 @@ func PrintStderr(args ...interface{}) {
} }
func PrintStdout(args ...interface{}) { func PrintStdout(args ...interface{}) {
fmt.Println(args...) // nolint: forbidigo fmt.Println(args...)
} }
func PrintJSONStderr(data interface{}) { func PrintJSONStderr(data interface{}) {
+27 -104
View File
@@ -6,7 +6,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math"
"net" "net"
"github.com/alecthomas/units" "github.com/alecthomas/units"
@@ -22,8 +21,6 @@ func (s SecretMode) String() string {
return "simple" return "simple"
case SecretModeSecured: case SecretModeSecured:
return "secured" return "secured"
case SecretModeTLS:
return "tls"
} }
return "tls" return "tls"
@@ -35,13 +32,6 @@ const (
SecretModeTLS SecretModeTLS
) )
type PreferIP uint8
const (
PreferIPv4 PreferIP = iota
PreferIPv6
)
const SimpleSecretLength = 16 const SimpleSecretLength = 16
type OptionType uint8 type OptionType uint8
@@ -50,8 +40,6 @@ const (
OptionTypeDebug OptionType = iota OptionTypeDebug OptionType = iota
OptionTypeVerbose OptionTypeVerbose
OptionTypePreferIP
OptionTypeBind OptionTypeBind
OptionTypePublicIPv4 OptionTypePublicIPv4
OptionTypePublicIPv6 OptionTypePublicIPv6
@@ -71,88 +59,38 @@ const (
OptionTypeMultiplexPerConnection OptionTypeMultiplexPerConnection
OptionTypeNTPServers
OptionTypeSecret OptionTypeSecret
OptionTypeAdtag OptionTypeAdtag
) )
type Config struct { type Config struct {
Bind *net.TCPAddr `json:"bind"` Bind *net.TCPAddr `json:"bind"`
PublicIPv4 *net.TCPAddr `json:"public_ipv4"` // nolint: tagliatelle PublicIPv4 *net.TCPAddr `json:"public_ipv4"`
PublicIPv6 *net.TCPAddr `json:"public_ipv6"` // nolint: tagliatelle PublicIPv6 *net.TCPAddr `json:"public_ipv6"`
StatsBind *net.TCPAddr `json:"stats_bind"` // nolint: tagliatelle StatsBind *net.TCPAddr `json:"stats_bind"`
StatsdAddr *net.TCPAddr `json:"stats_addr"` // nolint: tagliatelle StatsdAddr *net.TCPAddr `json:"stats_addr"`
StatsdTagsFormat *statsd.TagFormat `json:"statsd_tags_format"` // nolint: tagliatelle StatsdTagsFormat *statsd.TagFormat `json:"statsd_tags_format"`
StatsNamespace string `json:"stats_namespace"` // nolint: tagliatelle StatsNamespace string `json:"stats_namespace"`
CloakHost string `json:"cloak_host"` // nolint: tagliatelle CloakHost string `json:"cloak_host"`
StatsdTags map[string]string `json:"statsd_tags"` // nolint: tagliatelle StatsdTags map[string]string `json:"statsd_tags"`
WriteBuffer int `json:"write_buffer"` // nolint: tagliatelle WriteBuffer int `json:"write_buffer"`
ReadBuffer int `json:"read_buffer"` // nolint: tagliatelle ReadBuffer int `json:"read_buffer"`
CloakPort int `json:"cloak_port"` // nolint: tagliatelle CloakPort int `json:"cloak_port"`
AntiReplayMaxSize int `json:"anti_replay_max_size"` // nolint: tagliatelle AntiReplayMaxSize int `json:"anti_replay_max_size"`
MultiplexPerConnection int `json:"multiplex_per_connection"` // nolint: tagliatelle MultiplexPerConnection int `json:"multiplex_per_connection"`
Debug bool `json:"debug"` Debug bool `json:"debug"`
Verbose bool `json:"verbose"` Verbose bool `json:"verbose"`
SecretMode SecretMode `json:"secret_mode"` // nolint: tagliatelle SecretMode SecretMode `json:"secret_mode"`
PreferIP PreferIP `json:"prefer_ip"` // nolint: tagliatelle
NTPServers []string `json:"ntp_servers"` // nolint: tagliatelle
Secret []byte `json:"secret"` Secret []byte `json:"secret"`
AdTag []byte `json:"adtag"` AdTag []byte `json:"adtag"`
} }
func (c *Config) ClientReadBuffer() int {
return c.ReadBuffer
}
func (c *Config) ClientWriteBuffer() int {
return c.WriteBuffer
}
func (c *Config) MiddleProxyMode() bool {
return len(c.AdTag) > 0
}
func (c *Config) ProxyReadBuffer() int {
value := c.ReadBuffer
if c.MiddleProxyMode() {
value = c.adjustProxyValue(value)
}
return value
}
func (c *Config) ProxyWriteBuffer() int {
value := c.WriteBuffer
if c.MiddleProxyMode() {
value = c.adjustProxyValue(value)
}
return value
}
func (c *Config) adjustProxyValue(value int) int {
if c.MultiplexPerConnection == 0 {
return value
}
fvalue := float64(value)
newValue := fvalue * 2 * math.Log(float64(c.MultiplexPerConnection))
newValue = math.Ceil(newValue)
newValue = math.Max(fvalue, newValue)
return int(newValue)
}
type Opt struct { type Opt struct {
Option OptionType Option OptionType
Value interface{} Value interface{}
@@ -160,43 +98,33 @@ type Opt struct {
var C = Config{} var C = Config{}
func Init(options ...Opt) error { // nolint: gocyclo, funlen, cyclop func Init(options ...Opt) error { // nolint: gocyclo, funlen
for _, opt := range options { for _, opt := range options {
switch opt.Option { switch opt.Option {
case OptionTypeDebug: case OptionTypeDebug:
C.Debug = opt.Value.(bool) // nolint: forcetypeassert C.Debug = opt.Value.(bool)
case OptionTypeVerbose: case OptionTypeVerbose:
C.Verbose = opt.Value.(bool) // nolint: forcetypeassert C.Verbose = opt.Value.(bool)
case OptionTypePreferIP:
value := opt.Value.(string) // nolint: forcetypeassert
switch value {
case "ipv4":
C.PreferIP = PreferIPv4
case "ipv6":
C.PreferIP = PreferIPv6
default:
return fmt.Errorf("incorrect direct IP mode %s", value)
}
case OptionTypeBind: case OptionTypeBind:
C.Bind = opt.Value.(*net.TCPAddr) // nolint: forcetypeassert C.Bind = opt.Value.(*net.TCPAddr)
case OptionTypePublicIPv4: case OptionTypePublicIPv4:
C.PublicIPv4 = opt.Value.(*net.TCPAddr) // nolint: forcetypeassert C.PublicIPv4 = opt.Value.(*net.TCPAddr)
if C.PublicIPv4 == nil { if C.PublicIPv4 == nil {
C.PublicIPv4 = &net.TCPAddr{} C.PublicIPv4 = &net.TCPAddr{}
} }
case OptionTypePublicIPv6: case OptionTypePublicIPv6:
C.PublicIPv6 = opt.Value.(*net.TCPAddr) // nolint: forcetypeassert C.PublicIPv6 = opt.Value.(*net.TCPAddr)
if C.PublicIPv6 == nil { if C.PublicIPv6 == nil {
C.PublicIPv6 = &net.TCPAddr{} C.PublicIPv6 = &net.TCPAddr{}
} }
case OptionTypeStatsBind: case OptionTypeStatsBind:
C.StatsBind = opt.Value.(*net.TCPAddr) // nolint: forcetypeassert C.StatsBind = opt.Value.(*net.TCPAddr)
case OptionTypeStatsNamespace: case OptionTypeStatsNamespace:
C.StatsNamespace = opt.Value.(string) // nolint: forcetypeassert C.StatsNamespace = opt.Value.(string)
case OptionTypeStatsdAddress: case OptionTypeStatsdAddress:
C.StatsdAddr = opt.Value.(*net.TCPAddr) // nolint: forcetypeassert C.StatsdAddr = opt.Value.(*net.TCPAddr)
case OptionTypeStatsdTagsFormat: case OptionTypeStatsdTagsFormat:
value := opt.Value.(string) // nolint: forcetypeassert value := opt.Value.(string)
switch value { switch value {
case "datadog": case "datadog":
C.StatsdTagsFormat = statsd.TagFormatDatadog C.StatsdTagsFormat = statsd.TagFormatDatadog
@@ -206,7 +134,7 @@ func Init(options ...Opt) error { // nolint: gocyclo, funlen, cyclop
return fmt.Errorf("incorrect statsd tag %s", value) return fmt.Errorf("incorrect statsd tag %s", value)
} }
case OptionTypeStatsdTags: case OptionTypeStatsdTags:
C.StatsdTags = opt.Value.(map[string]string) // nolint: forcetypeassert C.StatsdTags = opt.Value.(map[string]string)
case OptionTypeWriteBufferSize: case OptionTypeWriteBufferSize:
C.WriteBuffer = int(opt.Value.(units.Base2Bytes)) C.WriteBuffer = int(opt.Value.(units.Base2Bytes))
case OptionTypeReadBufferSize: case OptionTypeReadBufferSize:
@@ -217,15 +145,10 @@ func Init(options ...Opt) error { // nolint: gocyclo, funlen, cyclop
C.AntiReplayMaxSize = int(opt.Value.(units.Base2Bytes)) C.AntiReplayMaxSize = int(opt.Value.(units.Base2Bytes))
case OptionTypeMultiplexPerConnection: case OptionTypeMultiplexPerConnection:
C.MultiplexPerConnection = int(opt.Value.(uint)) C.MultiplexPerConnection = int(opt.Value.(uint))
case OptionTypeNTPServers:
C.NTPServers = opt.Value.([]string) // nolint: forcetypeassert
if len(C.NTPServers) == 0 {
return errors.New("ntp server list is empty")
}
case OptionTypeSecret: case OptionTypeSecret:
C.Secret = opt.Value.([]byte) // nolint: forcetypeassert C.Secret = opt.Value.([]byte)
case OptionTypeAdtag: case OptionTypeAdtag:
C.AdTag = opt.Value.([]byte) // nolint: forcetypeassert C.AdTag = opt.Value.([]byte)
default: default:
return fmt.Errorf("unknown tag %v", opt.Option) return fmt.Errorf("unknown tag %v", opt.Option)
} }
+2 -2
View File
@@ -41,7 +41,7 @@ func fetchIP(ctx context.Context, network string) (net.IP, error) {
Timeout: ifconfigTimeout, Timeout: ifconfigTimeout,
Transport: &http.Transport{ Transport: &http.Transport{
DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) { DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, network, addr) // nolint: wrapcheck return dialer.DialContext(ctx, network, addr)
}, },
}, },
} }
@@ -60,7 +60,7 @@ func fetchIP(ctx context.Context, network string) (net.IP, error) {
return nil, fmt.Errorf("cannot perform a request: %w", err) return nil, fmt.Errorf("cannot perform a request: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close() // nolint: errcheck
respDataBytes, err := ioutil.ReadAll(resp.Body) respDataBytes, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
+14 -22
View File
@@ -8,16 +8,16 @@ import (
) )
type URLs struct { type URLs struct {
TG string `json:"tg_url"` // nolint: tagliatelle TG string `json:"tg_url"`
TMe string `json:"tme_url"` // nolint: tagliatelle TMe string `json:"tme_url"`
TGQRCode string `json:"tg_qrcode"` // nolint: tagliatelle TGQRCode string `json:"tg_qrcode"`
TMeQRCode string `json:"tme_qrcode"` // nolint: tagliatelle TMeQRCode string `json:"tme_qrcode"`
} }
type IPURLs struct { type IPURLs struct {
IPv4 *URLs `json:"ipv4,omitempty"` IPv4 URLs `json:"ipv4"`
IPv6 *URLs `json:"ipv6,omitempty"` IPv6 URLs `json:"ipv6"`
BotSecret string `json:"secret_for_mtproxybot"` // nolint: tagliatelle BotSecret string `json:"secret_for_mtproxybot"`
} }
func GetURLs() (urls IPURLs) { func GetURLs() (urls IPURLs) {
@@ -32,33 +32,25 @@ func GetURLs() (urls IPURLs) {
secret = "ee" + hex.EncodeToString(C.Secret) + hex.EncodeToString([]byte(C.CloakHost)) secret = "ee" + hex.EncodeToString(C.Secret) + hex.EncodeToString([]byte(C.CloakHost))
} }
if C.PublicIPv4.IP != nil {
urls.IPv4 = makeURLs(C.PublicIPv4, secret) urls.IPv4 = makeURLs(C.PublicIPv4, secret)
}
if C.PublicIPv6.IP != nil {
urls.IPv6 = makeURLs(C.PublicIPv6, secret) urls.IPv6 = makeURLs(C.PublicIPv6, secret)
}
urls.BotSecret = hex.EncodeToString(C.Secret) urls.BotSecret = hex.EncodeToString(C.Secret)
return urls return urls
} }
func makeURLs(addr *net.TCPAddr, secret string) *URLs { func makeURLs(addr *net.TCPAddr, secret string) (urls URLs) {
urls := &URLs{}
values := url.Values{} values := url.Values{}
values.Set("server", addr.IP.String()) values.Set("server", addr.IP.String())
values.Set("port", strconv.Itoa(addr.Port)) values.Set("port", strconv.Itoa(addr.Port))
values.Set("secret", secret) values.Set("secret", secret)
return &URLs{ urls.TG = makeTGURL(values)
TG: makeTGURL(values), urls.TMe = makeTMeURL(values)
TMe: makeTMeURL(values), urls.TGQRCode = makeQRCodeURL(urls.TG)
TGQRCode: makeQRCodeURL(urls.TG), urls.TMeQRCode = makeQRCodeURL(urls.TG)
TMeQRCode: makeQRCodeURL(urls.TMe),
} return
} }
func makeTGURL(values url.Values) string { func makeTGURL(values url.Values) string {
-2
View File
@@ -8,8 +8,6 @@ func (c ConnectionProtocol) String() string {
return "any" return "any"
case ConnectionProtocolIPv4: case ConnectionProtocolIPv4:
return "ipv4" return "ipv4"
case ConnectionProtocolIPv6:
return "ipv6"
} }
return "ipv6" return "ipv6"
+1 -3
View File
@@ -21,9 +21,7 @@ func (t ConnectionType) Tag() []byte {
return ConnectionTagAbridged return ConnectionTagAbridged
case ConnectionTypeIntermediate: case ConnectionTypeIntermediate:
return ConnectionTagIntermediate return ConnectionTagIntermediate
case ConnectionTypeSecure, ConnectionTypeUnknown: default:
return ConnectionTagSecure return ConnectionTagSecure
} }
return ConnectionTagSecure
} }
+33 -21
View File
@@ -2,23 +2,23 @@ package faketls
import ( import (
"bufio" "bufio"
"bytes"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net" "net"
"strconv" "strconv"
"sync"
"time" "time"
"github.com/9seconds/mtg/antireplay" "mtg/antireplay"
"github.com/9seconds/mtg/config" "mtg/config"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
"github.com/9seconds/mtg/obfuscated2" "mtg/obfuscated2"
"github.com/9seconds/mtg/protocol" "mtg/protocol"
"github.com/9seconds/mtg/stats" "mtg/stats"
"github.com/9seconds/mtg/tlstypes" "mtg/tlstypes"
"github.com/9seconds/mtg/wrappers/stream" "mtg/wrappers/stream"
) )
type ClientProtocol struct { type ClientProtocol struct {
@@ -49,13 +49,13 @@ func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conn
} }
conn := stream.NewFakeTLS(socket) conn := stream.NewFakeTLS(socket)
conn, err := c.ClientProtocol.Handshake(conn) conn, err := c.ClientProtocol.Handshake(conn)
if err != nil { if err != nil {
return nil, err // nolint: wrapcheck return nil, err
} }
return conn, err // nolint: wrapcheck return conn, err
} }
func (c *ClientProtocol) tlsHandshake(conn io.ReadWriter) error { func (c *ClientProtocol) tlsHandshake(conn io.ReadWriter) error {
@@ -64,10 +64,7 @@ func (c *ClientProtocol) tlsHandshake(conn io.ReadWriter) error {
return fmt.Errorf("cannot read initial record: %w", err) return fmt.Errorf("cannot read initial record: %w", err)
} }
buf := &bytes.Buffer{} clientHello, err := tlstypes.ParseClientHello(helloRecord.Data.Bytes())
helloRecord.Data.WriteBytes(buf)
clientHello, err := tlstypes.ParseClientHello(buf.Bytes())
if err != nil { if err != nil {
return fmt.Errorf("cannot parse client hello: %w", err) return fmt.Errorf("cannot parse client hello: %w", err)
} }
@@ -89,7 +86,6 @@ func (c *ClientProtocol) tlsHandshake(conn io.ReadWriter) error {
if antireplay.Cache.HasTLS(clientHello.Random[:]) { if antireplay.Cache.HasTLS(clientHello.Random[:]) {
stats.Stats.ReplayDetected() stats.Stats.ReplayDetected()
return errors.New("replay attack is detected") return errors.New("replay attack is detected")
} }
@@ -105,16 +101,32 @@ func (c *ClientProtocol) tlsHandshake(conn io.ReadWriter) error {
} }
func (c *ClientProtocol) cloakHost(clientConn io.ReadWriteCloser) { func (c *ClientProtocol) cloakHost(clientConn io.ReadWriteCloser) {
stats.Stats.CloakedRequest()
addr := net.JoinHostPort(config.C.CloakHost, strconv.Itoa(config.C.CloakPort)) addr := net.JoinHostPort(config.C.CloakHost, strconv.Itoa(config.C.CloakPort))
hostConn, err := net.Dial("tcp", addr) hostConn, err := net.Dial("tcp", addr)
if err != nil { if err != nil {
return return
} }
cloak(clientConn, hostConn) defer hostConn.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go c.pipe(hostConn, clientConn, wg)
go c.pipe(clientConn, hostConn, wg)
wg.Wait()
}
func (c *ClientProtocol) pipe(dst io.WriteCloser, src io.Reader, wg *sync.WaitGroup) {
defer func() {
wg.Done()
dst.Close()
}()
io.Copy(dst, src) // nolint: errcheck
} }
func MakeClientProtocol() protocol.ClientProtocol { func MakeClientProtocol() protocol.ClientProtocol {
-73
View File
@@ -1,73 +0,0 @@
package faketls
import (
"context"
"io"
"sync"
"time"
"github.com/9seconds/mtg/wrappers/rwc"
)
const (
cloakLastActivityTimeout = 5 * time.Second
cloakMaxTimeout = 30 * time.Second
)
func cloak(one, another io.ReadWriteCloser) {
defer func() {
one.Close()
another.Close()
}()
channelPing := make(chan struct{}, 1)
ctx, cancel := context.WithCancel(context.Background())
one = rwc.NewPing(ctx, one, channelPing)
another = rwc.NewPing(ctx, another, channelPing)
wg := &sync.WaitGroup{}
wg.Add(2)
go cloakPipe(one, another, wg)
go cloakPipe(another, one, wg)
go func() {
wg.Wait()
cancel()
}()
go func() {
lastActivityTimer := time.NewTimer(cloakLastActivityTimeout)
defer lastActivityTimer.Stop()
maxTimer := time.NewTimer(cloakMaxTimeout)
defer maxTimer.Stop()
for {
select {
case <-channelPing:
lastActivityTimer.Stop()
lastActivityTimer = time.NewTimer(cloakLastActivityTimeout)
case <-ctx.Done():
return
case <-lastActivityTimer.C:
cancel()
return
case <-maxTimer.C:
cancel()
return
}
}
}()
<-ctx.Done()
}
func cloakPipe(one io.Writer, another io.Reader, wg *sync.WaitGroup) {
defer wg.Done()
io.Copy(one, another) // nolint: errcheck
}
+15 -27
View File
@@ -1,32 +1,20 @@
module github.com/9seconds/mtg module mtg
go 1.17 go 1.13
require ( require (
github.com/VictoriaMetrics/fastcache v1.6.0 github.com/VictoriaMetrics/fastcache v1.5.2
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d
github.com/beevik/ntp v0.3.0 github.com/beevik/ntp v0.2.0
github.com/golang/snappy v0.0.4 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.2.1
github.com/prometheus/common v0.30.0 // indirect github.com/prometheus/procfs v0.0.7 // indirect
github.com/prometheus/procfs v0.7.3 // indirect github.com/smira/go-statsd v1.3.1
github.com/smira/go-statsd v1.3.2 go.uber.org/multierr v1.4.0 // indirect
go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.13.0
go.uber.org/zap v1.19.0 golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 golang.org/x/net v0.0.0-20191116160921-f9c825593386 // indirect
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect golang.org/x/sys v0.0.0-20191118013547-6254a7c3cac6
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 golang.org/x/tools v0.0.0-20191118051429-5a76f03bc7c3 // indirect
golang.org/x/tools v0.1.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/alecthomas/kingpin.v2 v2.2.6
) )
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
)
+82 -416
View File
@@ -1,158 +1,70 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
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/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
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=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/VictoriaMetrics/fastcache v1.5.2 h1:Erd8iIuBAL9kke8JzM4+WxkKuFkHh3ktwLanJvDgR44=
github.com/VictoriaMetrics/fastcache v1.5.2/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/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 h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/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 h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4=
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw= github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM=
github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA=
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.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-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 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.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 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/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
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/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/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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.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/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/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=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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/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/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.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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 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/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.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/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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -161,352 +73,106 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 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 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.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.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.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/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/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
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.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.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 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.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.0.7 h1:RS5GAlMbnkWkhs4+bPocMTmGjYkuCY5djjqEDdXOhcQ=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.0.7/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 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/smira/go-statsd v1.3.1 h1:JalGiHNdK7GqVAPpg7j0Kwp2jZrz/fCg/B4ZuNuBY2w=
github.com/smira/go-statsd v1.3.2 h1:1EeuzxNZ/TD9apbTOFSM9nulqfcsQFmT4u1A2DREabI= github.com/smira/go-statsd v1.3.1/go.mod h1:1srXJ9/pbnN04G8f4F1jUzsGOnwkPKXciyqpewGlkC4=
github.com/smira/go-statsd v1.3.2/go.mod h1:1srXJ9/pbnN04G8f4F1jUzsGOnwkPKXciyqpewGlkC4= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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-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-20191117063200-497ca9f6d64f h1:kz4KIr+xcPUsI3VMoqWfPMvtnJ6MGfiVwsWSVzphMO4=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-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=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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-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= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-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-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-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-20191116160921-f9c825593386 h1:ktbWvQrW08Txdxno1PiDpSxPXG6ndGsfnJjRRtkM0LQ=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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-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-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-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-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-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-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191118013547-6254a7c3cac6 h1:8mlr2HX+lfl0eaQcjiHfVeM2FHxWkuYQ5a2Wcy8mE1s=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191118013547-6254a7c3cac6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/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=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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/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.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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191118051429-5a76f03bc7c3 h1:3gzOmNy3PLCZ+3Ru/n5Gh7pPjsieiytYSDxFj6QY/oI=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191118051429-5a76f03bc7c3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
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.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
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/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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
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/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=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
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/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=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
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/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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+6 -15
View File
@@ -4,16 +4,14 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"sync" "sync"
"time"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/mtproto"
"github.com/9seconds/mtg/mtproto/rpc"
"github.com/9seconds/mtg/protocol"
"go.uber.org/zap" "go.uber.org/zap"
)
const connectionTTL = time.Hour "mtg/conntypes"
"mtg/mtproto"
"mtg/mtproto/rpc"
"mtg/protocol"
)
type connection struct { type connection struct {
conn conntypes.PacketReadWriteCloser conn conntypes.PacketReadWriteCloser
@@ -30,12 +28,9 @@ type connection struct {
channelConnDetach chan conntypes.ConnID channelConnDetach chan conntypes.ConnID
} }
func (c *connection) run() { // nolint: cyclop func (c *connection) run() {
defer c.Close() defer c.Close()
ttl := time.NewTimer(connectionTTL)
defer ttl.Stop()
for { for {
select { select {
case <-c.channelDone: case <-c.channelDone:
@@ -44,9 +39,6 @@ func (c *connection) run() { // nolint: cyclop
} }
return return
case <-ttl.C:
c.logger.Debugw("Closing connection by TTL")
c.Close()
case resp := <-c.channelRead: case resp := <-c.channelRead:
if channel, ok := c.proxyConns[string(resp.ConnID[:])]; ok { if channel, ok := c.proxyConns[string(resp.ConnID[:])]; ok {
if resp.Type == rpc.ProxyResponseTypeCloseExt { if resp.Type == rpc.ProxyResponseTypeCloseExt {
@@ -89,7 +81,6 @@ func (c *connection) readLoop() {
response, err := rpc.ParseProxyResponse(packet) response, err := rpc.ParseProxyResponse(packet)
if err != nil { if err != nil {
c.logger.Debugw("Failed response", "error", err) c.logger.Debugw("Failed response", "error", err)
continue continue
} }
+6 -6
View File
@@ -4,14 +4,18 @@ import (
"fmt" "fmt"
"sort" "sort"
"github.com/9seconds/mtg/config" "mtg/config"
) )
type connectionList struct { type connectionList struct {
connections []*connection connections []*connection
} }
func (c *connectionList) get(conn *ProxyConn) (*connection, error) { func (c *connectionList) Get(conn *ProxyConn) (*connection, error) {
if len(c.connections) > 0 {
c.gc()
}
if len(c.connections) > 0 && c.connections[0].Len() < config.C.MultiplexPerConnection { if len(c.connections) > 0 && c.connections[0].Len() < config.C.MultiplexPerConnection {
if err := c.connections[0].Attach(conn); err == nil { if err := c.connections[0].Attach(conn); err == nil {
return c.connections[0], nil return c.connections[0], nil
@@ -25,7 +29,6 @@ func (c *connectionList) get(conn *ProxyConn) (*connection, error) {
if err = newConn.Attach(conn); err != nil { if err = newConn.Attach(conn); err != nil {
newConn.Close() newConn.Close()
return nil, fmt.Errorf("cannot attach to the newly created connection: %w", err) return nil, fmt.Errorf("cannot attach to the newly created connection: %w", err)
} }
@@ -38,9 +41,6 @@ func (c *connectionList) get(conn *ProxyConn) (*connection, error) {
func (c *connectionList) gc() { func (c *connectionList) gc() {
prevLen := len(c.connections) prevLen := len(c.connections)
if prevLen == 0 {
return
}
for i := len(c.connections) - 1; i >= 0; i-- { for i := len(c.connections) - 1; i >= 0; i-- {
lastIndex := len(c.connections) - 1 lastIndex := len(c.connections) - 1
+2 -2
View File
@@ -4,7 +4,7 @@ import (
"context" "context"
"sync" "sync"
"github.com/9seconds/mtg/protocol" "mtg/protocol"
) )
type hub struct { type hub struct {
@@ -18,7 +18,7 @@ func (h *hub) Register(req *protocol.TelegramRequest) (*ProxyConn, error) {
} }
func (h *hub) getMux(req *protocol.TelegramRequest) *mux { func (h *hub) getMux(req *protocol.TelegramRequest) *mux {
key := 32767 + int32(req.ClientProtocol.DC()) + 100000*int32(req.ClientProtocol.ConnectionProtocol()) var key int32 = 32767 + int32(req.ClientProtocol.DC()) + 100000*int32(req.ClientProtocol.ConnectionProtocol())
h.mutex.RLock() h.mutex.RLock()
m, ok := h.muxes[key] m, ok := h.muxes[key]
+1 -1
View File
@@ -1,6 +1,6 @@
package hub package hub
import "github.com/9seconds/mtg/protocol" import "mtg/protocol"
type Interface interface { type Interface interface {
Register(*protocol.TelegramRequest) (*ProxyConn, error) Register(*protocol.TelegramRequest) (*ProxyConn, error)
+3 -12
View File
@@ -2,14 +2,11 @@ package hub
import ( import (
"context" "context"
"time"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
"github.com/9seconds/mtg/protocol" "mtg/protocol"
) )
const muxGCEvery = time.Minute
type muxNewRequest struct { type muxNewRequest struct {
req *protocol.TelegramRequest req *protocol.TelegramRequest
resp chan<- muxNewResponse resp chan<- muxNewResponse
@@ -29,9 +26,6 @@ type mux struct {
} }
func (m *mux) run() { func (m *mux) run() {
gcTicker := time.NewTicker(muxGCEvery)
defer gcTicker.Stop()
for { for {
select { select {
case <-m.ctx.Done(): case <-m.ctx.Done():
@@ -40,12 +34,9 @@ func (m *mux) run() {
} }
return return
case <-gcTicker.C:
m.connections.gc()
case req := <-m.channelNew: case req := <-m.channelNew:
m.connections.gc()
proxyConn := newProxyConn(req.req, m.channelClosed) proxyConn := newProxyConn(req.req, m.channelClosed)
conn, err := m.connections.get(proxyConn) conn, err := m.connections.Get(proxyConn)
if err == nil { if err == nil {
m.clients[string(req.req.ConnID[:])] = conn m.clients[string(req.req.ConnID[:])] = conn
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
"github.com/9seconds/mtg/mtproto/rpc" "mtg/mtproto/rpc"
"github.com/9seconds/mtg/protocol" "mtg/protocol"
) )
const ( const (
+8 -43
View File
@@ -3,17 +3,16 @@ package main
import ( import (
"math/rand" "math/rand"
"os" "os"
"runtime/debug"
"strings"
"time" "time"
"github.com/9seconds/mtg/cli"
"github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/utils"
kingpin "gopkg.in/alecthomas/kingpin.v2" kingpin "gopkg.in/alecthomas/kingpin.v2"
"mtg/cli"
"mtg/config"
"mtg/utils"
) )
var version = "dev" // has to be set by ldflags var version = "dev" // this has to be set by build ld flags
var ( var (
app = kingpin.New("mtg", "Simple MTPROTO proxy.") app = kingpin.New("mtg", "Simple MTPROTO proxy.")
@@ -42,11 +41,6 @@ var (
Short('v'). Short('v').
Envar("MTG_VERBOSE"). Envar("MTG_VERBOSE").
Bool() Bool()
runPreferIP = runCommand.Flag("prefer-ip",
"Prefer this IP protocol if possible. Valid options are 'ipv4' and 'ipv6'").
Envar("MTG_PREFER_DIRECT_IP").
Default("ipv6").
Enum("ipv4", "ipv6")
runBind = runCommand.Flag("bind", runBind = runCommand.Flag("bind",
"Host:Port to bind proxy to."). "Host:Port to bind proxy to.").
Short('b'). Short('b').
@@ -91,13 +85,13 @@ var (
"Write buffer size. You can think about it as a buffer from client to Telegram."). "Write buffer size. You can think about it as a buffer from client to Telegram.").
Short('w'). Short('w').
Envar("MTG_BUFFER_WRITE"). Envar("MTG_BUFFER_WRITE").
Default("32KB"). Default("64KB").
Bytes() Bytes()
runReadBufferSize = runCommand.Flag("read-buffer", runReadBufferSize = runCommand.Flag("read-buffer",
"Read buffer size. You can think about it as a buffer from Telegram to client."). "Read buffer size. You can think about it as a buffer from Telegram to client.").
Short('r'). Short('r').
Envar("MTG_BUFFER_READ"). Envar("MTG_BUFFER_READ").
Default("32KB"). Default("128KB").
Bytes() Bytes()
runTLSCloakPort = runCommand.Flag("cloak-port", runTLSCloakPort = runCommand.Flag("cloak-port",
"Port which should be used for host cloaking."). "Port which should be used for host cloaking.").
@@ -114,18 +108,13 @@ var (
Envar("MTG_MULTIPLEX_PERCONNECTION"). Envar("MTG_MULTIPLEX_PERCONNECTION").
Default("50"). Default("50").
Uint() Uint()
runNTPServers = runCommand.Flag("ntp-server",
"A list of NTP servers to use.").
Envar("MTG_NTP_SERVERS").
Default("0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org").
Strings()
runSecret = runCommand.Arg("secret", "Secret of this proxy.").Required().HexBytes() runSecret = runCommand.Arg("secret", "Secret of this proxy.").Required().HexBytes()
runAdtag = runCommand.Arg("adtag", "ADTag of the proxy.").HexBytes() runAdtag = runCommand.Arg("adtag", "ADTag of the proxy.").HexBytes()
) )
func main() { func main() {
rand.Seed(time.Now().UTC().UnixNano()) rand.Seed(time.Now().UTC().UnixNano())
app.Version(getVersion()) app.Version(version)
app.HelpFlag.Short('h') app.HelpFlag.Short('h')
if err := utils.SetLimits(); err != nil { if err := utils.SetLimits(); err != nil {
@@ -139,7 +128,6 @@ func main() {
err := config.Init( err := config.Init(
config.Opt{Option: config.OptionTypeDebug, Value: *runDebug}, config.Opt{Option: config.OptionTypeDebug, Value: *runDebug},
config.Opt{Option: config.OptionTypeVerbose, Value: *runVerbose}, config.Opt{Option: config.OptionTypeVerbose, Value: *runVerbose},
config.Opt{Option: config.OptionTypePreferIP, Value: *runPreferIP},
config.Opt{Option: config.OptionTypeBind, Value: *runBind}, config.Opt{Option: config.OptionTypeBind, Value: *runBind},
config.Opt{Option: config.OptionTypePublicIPv4, Value: *runPublicIPv4}, config.Opt{Option: config.OptionTypePublicIPv4, Value: *runPublicIPv4},
config.Opt{Option: config.OptionTypePublicIPv6, Value: *runPublicIPv6}, config.Opt{Option: config.OptionTypePublicIPv6, Value: *runPublicIPv6},
@@ -153,7 +141,6 @@ func main() {
config.Opt{Option: config.OptionTypeCloakPort, Value: *runTLSCloakPort}, config.Opt{Option: config.OptionTypeCloakPort, Value: *runTLSCloakPort},
config.Opt{Option: config.OptionTypeAntiReplayMaxSize, Value: *runAntiReplayMaxSize}, config.Opt{Option: config.OptionTypeAntiReplayMaxSize, Value: *runAntiReplayMaxSize},
config.Opt{Option: config.OptionTypeMultiplexPerConnection, Value: *runMultiplexPerConnection}, config.Opt{Option: config.OptionTypeMultiplexPerConnection, Value: *runMultiplexPerConnection},
config.Opt{Option: config.OptionTypeNTPServers, Value: *runNTPServers},
config.Opt{Option: config.OptionTypeSecret, Value: *runSecret}, config.Opt{Option: config.OptionTypeSecret, Value: *runSecret},
config.Opt{Option: config.OptionTypeAdtag, Value: *runAdtag}, config.Opt{Option: config.OptionTypeAdtag, Value: *runAdtag},
) )
@@ -166,25 +153,3 @@ func main() {
} }
} }
} }
func getVersion() string {
if version != "dev" {
return version
}
info, ok := debug.ReadBuildInfo()
if !ok {
return version
}
builder := strings.Builder{}
builder.WriteString(info.Main.Version)
if info.Main.Sum != "" {
builder.WriteString(" (checksum: ")
builder.WriteString(info.Main.Sum)
builder.WriteRune(')')
}
return builder.String()
}
+8 -8
View File
@@ -3,12 +3,12 @@ package mtproto
import ( import (
"fmt" "fmt"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
"github.com/9seconds/mtg/mtproto/rpc" "mtg/mtproto/rpc"
"github.com/9seconds/mtg/protocol" "mtg/protocol"
"github.com/9seconds/mtg/telegram" "mtg/telegram"
"github.com/9seconds/mtg/wrappers/packet" "mtg/wrappers/packet"
"github.com/9seconds/mtg/wrappers/stream" "mtg/wrappers/stream"
) )
func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.PacketReadWriteCloser, error) { func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.PacketReadWriteCloser, error) {
@@ -19,8 +19,8 @@ func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.PacketReadWriteC
} }
rpcNonceConn := packet.NewMtprotoFrame(conn, rpc.SeqNoNonce) rpcNonceConn := packet.NewMtprotoFrame(conn, rpc.SeqNoNonce)
rpcNonceReq, err := doRPCNonceRequest(rpcNonceConn) rpcNonceReq, err := doRPCNonceRequest(rpcNonceConn)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot do nonce request: %w", err) return nil, fmt.Errorf("cannot do nonce request: %w", err)
} }
@@ -51,7 +51,7 @@ func doRPCNonceRequest(conn conntypes.BasePacketWriter) (*rpc.NonceRequest, erro
} }
if err := conn.Write(rpcNonceReq.Bytes()); err != nil { if err := conn.Write(rpcNonceReq.Bytes()); err != nil {
return nil, err // nolint: wrapcheck return nil, err
} }
return rpcNonceReq, nil return rpcNonceReq, nil
+1 -1
View File
@@ -7,7 +7,7 @@ const (
SeqNoHandshake = -1 SeqNoHandshake = -1
) )
// Different constants for RPC protocol. // Different constants for RPC protocol
var ( var (
TagCloseExt = []byte{0xa2, 0x34, 0xb6, 0x5e} TagCloseExt = []byte{0xa2, 0x34, 0xb6, 0x5e}
TagProxyAns = []byte{0x0d, 0xda, 0x03, 0x44} TagProxyAns = []byte{0x0d, 0xda, 0x03, 0x44}
+4 -4
View File
@@ -17,10 +17,10 @@ type HandshakeResponse struct {
func (r *HandshakeResponse) Bytes() []byte { func (r *HandshakeResponse) Bytes() []byte {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.Write(r.Type) buf.Write(r.Type) // nolint: gosec
buf.Write(r.Flags) buf.Write(r.Flags) // nolint: gosec
buf.Write(r.SenderPID) buf.Write(r.SenderPID) // nolint: gosec
buf.Write(r.PeerPID) buf.Write(r.PeerPID) // nolint: gosec
return buf.Bytes() return buf.Bytes()
} }
+6 -6
View File
@@ -15,13 +15,13 @@ type NonceResponse struct {
// Bytes returns serialized form of the nonce response. // Bytes returns serialized form of the nonce response.
func (r *NonceResponse) Bytes() []byte { func (r *NonceResponse) Bytes() []byte {
buf := bytes.Buffer{} buf := &bytes.Buffer{}
buf.Write(r.Type) buf.Write(r.Type) // nolint: gosec
buf.Write(r.KeySelector) buf.Write(r.KeySelector) // nolint: gosec
buf.Write(r.Crypto) buf.Write(r.Crypto) // nolint: gosec
buf.Write(r.CryptoTS) buf.Write(r.CryptoTS) // nolint: gosec
buf.Write(r.Nonce) buf.Write(r.Nonce) // nolint: gosec
return buf.Bytes() return buf.Bytes()
} }
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
) )
type ProxyResponseType uint8 type ProxyResponseType uint8
+8 -3
View File
@@ -5,16 +5,22 @@ import (
"math/rand" "math/rand"
"time" "time"
"github.com/9seconds/mtg/config"
"github.com/beevik/ntp" "github.com/beevik/ntp"
"go.uber.org/zap" "go.uber.org/zap"
) )
const autoUpdatePeriod = time.Minute const autoUpdatePeriod = time.Minute
var ntpEndpoints = [...]string{
"0.pool.ntp.org",
"1.pool.ntp.org",
"2.pool.ntp.org",
"3.pool.ntp.org",
}
// Fetch fetches the data on time drift. // Fetch fetches the data on time drift.
func Fetch() (time.Duration, error) { func Fetch() (time.Duration, error) {
url := config.C.NTPServers[rand.Intn(len(config.C.NTPServers))] // nolint: gosec url := ntpEndpoints[rand.Intn(len(ntpEndpoints))]
resp, err := ntp.Query(url) resp, err := ntp.Query(url)
if err != nil { if err != nil {
@@ -39,7 +45,6 @@ func AutoUpdate() {
diff, err := Fetch() diff, err := Fetch()
if err != nil { if err != nil {
logger.Debugw("Cannot fetch time from NTP", "error", err) logger.Debugw("Cannot fetch time from NTP", "error", err)
continue continue
} }
+12 -13
View File
@@ -9,13 +9,13 @@ import (
"io" "io"
"time" "time"
"github.com/9seconds/mtg/antireplay" "mtg/antireplay"
"github.com/9seconds/mtg/config" "mtg/config"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
"github.com/9seconds/mtg/protocol" "mtg/protocol"
"github.com/9seconds/mtg/stats" "mtg/stats"
"github.com/9seconds/mtg/utils" "mtg/utils"
"github.com/9seconds/mtg/wrappers/stream" "mtg/wrappers/stream"
) )
const clientProtocolHandshakeTimeout = 10 * time.Second const clientProtocolHandshakeTimeout = 10 * time.Second
@@ -45,14 +45,14 @@ func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conn
} }
decHasher := sha256.New() decHasher := sha256.New()
decHasher.Write(fm.Key()) decHasher.Write(fm.Key()) // nolint: errcheck
decHasher.Write(config.C.Secret) decHasher.Write(config.C.Secret) // nolint: errcheck
decryptor := utils.MakeStreamCipher(decHasher.Sum(nil), fm.IV()) decryptor := utils.MakeStreamCipher(decHasher.Sum(nil), fm.IV())
invertedFrame := fm.Invert() invertedFrame := fm.Invert()
encHasher := sha256.New() encHasher := sha256.New()
encHasher.Write(invertedFrame.Key()) encHasher.Write(invertedFrame.Key()) // nolint: errcheck
encHasher.Write(config.C.Secret) encHasher.Write(config.C.Secret) // nolint: errcheck
encryptor := utils.MakeStreamCipher(encHasher.Sum(nil), invertedFrame.IV()) encryptor := utils.MakeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
decryptedFrame := Frame{} decryptedFrame := Frame{}
@@ -84,7 +84,6 @@ func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conn
replayKey := decryptedFrame.Unique() replayKey := decryptedFrame.Unique()
if antireplay.Cache.HasObfuscated2(replayKey) { if antireplay.Cache.HasObfuscated2(replayKey) {
stats.Stats.ReplayDetected() stats.Stats.ReplayDetected()
return nil, errors.New("replay attack is detected") return nil, errors.New("replay attack is detected")
} }
@@ -106,7 +105,7 @@ type handshakeReader struct {
} }
func (h handshakeReader) Read(p []byte) (int, error) { func (h handshakeReader) Read(p []byte) (int, error) {
return h.parent.ReadTimeout(p, clientProtocolHandshakeTimeout) // nolint: wrapcheck return h.parent.ReadTimeout(p, clientProtocolHandshakeTimeout)
} }
func MakeClientProtocol() protocol.ClientProtocol { func MakeClientProtocol() protocol.ClientProtocol {
+1 -1
View File
@@ -15,7 +15,7 @@ const (
frameLen = 64 frameLen = 64
) )
// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]. // [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
type Frame struct { type Frame struct {
data [frameLen]byte data [frameLen]byte
} }
+5 -5
View File
@@ -4,11 +4,11 @@ import (
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
"github.com/9seconds/mtg/protocol" "mtg/protocol"
"github.com/9seconds/mtg/telegram" "mtg/telegram"
"github.com/9seconds/mtg/utils" "mtg/utils"
"github.com/9seconds/mtg/wrappers/stream" "mtg/wrappers/stream"
) )
func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.StreamReadWriteCloser, error) { func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.StreamReadWriteCloser, error) {
+1 -1
View File
@@ -1,6 +1,6 @@
package protocol package protocol
import "github.com/9seconds/mtg/conntypes" import "mtg/conntypes"
type ClientProtocol interface { type ClientProtocol interface {
Handshake(conntypes.StreamReadWriteCloser) (conntypes.StreamReadWriteCloser, error) Handshake(conntypes.StreamReadWriteCloser) (conntypes.StreamReadWriteCloser, error)
+2 -1
View File
@@ -3,8 +3,9 @@ package protocol
import ( import (
"context" "context"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
) )
type TelegramRequest struct { type TelegramRequest struct {
+13 -10
View File
@@ -4,27 +4,31 @@ import (
"io" "io"
"sync" "sync"
"github.com/9seconds/mtg/obfuscated2"
"github.com/9seconds/mtg/protocol"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
"mtg/obfuscated2"
"mtg/protocol"
) )
const directPipeBufferSize = 1024 const directPipeBufferSize = 1024 * 1024
func directConnection(request *protocol.TelegramRequest) error { func directConnection(request *protocol.TelegramRequest) error {
telegramConnRaw, err := obfuscated2.TelegramProtocol(request) telegramConnRaw, err := obfuscated2.TelegramProtocol(request)
if err != nil { if err != nil {
return err // nolint: wrapcheck return err
} }
defer telegramConnRaw.Close() telegramConn := telegramConnRaw.(conntypes.StreamReadWriteCloser)
defer telegramConn.Close()
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
wg.Add(2) wg.Add(2)
go directPipe(telegramConnRaw, request.ClientConn, wg, request.Logger) go directPipe(telegramConn, request.ClientConn, wg, request.Logger)
go directPipe(request.ClientConn, telegramConnRaw, wg, request.Logger) go directPipe(request.ClientConn, telegramConn, wg, request.Logger)
wg.Wait() wg.Wait()
@@ -38,9 +42,8 @@ func directPipe(dst io.WriteCloser, src io.ReadCloser, wg *sync.WaitGroup, logge
wg.Done() wg.Done()
}() }()
buf := [directPipeBufferSize]byte{} buf := make([]byte, directPipeBufferSize)
if _, err := io.CopyBuffer(dst, src, buf); err != nil {
if _, err := io.CopyBuffer(dst, src, buf[:]); err != nil {
logger.Debugw("Cannot pump sockets", "error", err) logger.Debugw("Cannot pump sockets", "error", err)
} }
} }
+6 -8
View File
@@ -3,17 +3,17 @@ package proxy
import ( import (
"sync" "sync"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/protocol"
"github.com/9seconds/mtg/wrappers/packetack"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
"mtg/protocol"
"mtg/wrappers/packetack"
) )
func middleConnection(request *protocol.TelegramRequest) { func middleConnection(request *protocol.TelegramRequest) {
telegramConn, err := packetack.NewProxy(request) telegramConn, err := packetack.NewProxy(request)
if err != nil { if err != nil {
request.Logger.Debugw("Cannot dial to Telegram", "error", err) request.Logger.Debugw("Cannot dial to Telegram", "error", err)
return return
} }
defer telegramConn.Close() defer telegramConn.Close()
@@ -27,7 +27,7 @@ func middleConnection(request *protocol.TelegramRequest) {
clientConn = packetack.NewClientIntermediate(request.ClientConn) clientConn = packetack.NewClientIntermediate(request.ClientConn)
case conntypes.ConnectionTypeSecure: case conntypes.ConnectionTypeSecure:
clientConn = packetack.NewClientIntermediateSecure(request.ClientConn) clientConn = packetack.NewClientIntermediateSecure(request.ClientConn)
case conntypes.ConnectionTypeUnknown: default:
panic("unknown connection type") panic("unknown connection type")
} }
@@ -53,17 +53,15 @@ func middlePipe(dst conntypes.PacketAckWriteCloser,
for { for {
acks := conntypes.ConnectionAcks{} acks := conntypes.ConnectionAcks{}
packet, err := src.Read(&acks) packet, err := src.Read(&acks)
if err != nil { if err != nil {
logger.Debugw("Cannot read packet", "error", err) logger.Debugw("Cannot read packet", "error", err)
return return
} }
if err = dst.Write(packet, &acks); err != nil { if err = dst.Write(packet, &acks); err != nil {
logger.Debugw("Cannot send packet", "error", err) logger.Debugw("Cannot send packet", "error", err)
return return
} }
} }
+13 -15
View File
@@ -4,13 +4,14 @@ import (
"context" "context"
"net" "net"
"github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/protocol"
"github.com/9seconds/mtg/stats"
"github.com/9seconds/mtg/utils"
"github.com/9seconds/mtg/wrappers/stream"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/config"
"mtg/conntypes"
"mtg/protocol"
"mtg/stats"
"mtg/utils"
"mtg/wrappers/stream"
) )
type Proxy struct { type Proxy struct {
@@ -29,7 +30,8 @@ func (p *Proxy) Serve(listener net.Listener) {
case <-doneChan: case <-doneChan:
return return
default: default:
p.Logger.Fatalw("Cannot allocate incoming connection", "error", err) p.Logger.Errorw("Cannot allocate incoming connection", "error", err)
continue
} }
} }
@@ -40,7 +42,6 @@ func (p *Proxy) Serve(listener net.Listener) {
func (p *Proxy) accept(conn net.Conn) { func (p *Proxy) accept(conn net.Conn) {
defer func() { defer func() {
conn.Close() conn.Close()
if err := recover(); err != nil { if err := recover(); err != nil {
stats.Stats.Crash() stats.Stats.Crash()
p.Logger.Errorw("Crash of accept handler", "error", err) p.Logger.Errorw("Crash of accept handler", "error", err)
@@ -50,9 +51,8 @@ func (p *Proxy) accept(conn net.Conn) {
connID := conntypes.NewConnID() connID := conntypes.NewConnID()
logger := p.Logger.With("connection_id", connID) logger := p.Logger.With("connection_id", connID)
if err := utils.InitTCP(conn, config.C.ClientReadBuffer(), config.C.ClientWriteBuffer()); err != nil { if err := utils.InitTCP(conn); err != nil {
logger.Errorw("Cannot initialize client TCP connection", "error", err) logger.Errorw("Cannot initialize client TCP connection", "error", err)
return return
} }
@@ -66,12 +66,10 @@ func (p *Proxy) accept(conn net.Conn) {
defer clientConn.Close() defer clientConn.Close()
clientProtocol := p.ClientProtocolMaker() clientProtocol := p.ClientProtocolMaker()
clientConn, err := clientProtocol.Handshake(clientConn) clientConn, err := clientProtocol.Handshake(clientConn)
if err != nil {
stats.Stats.AuthenticationFailed()
logger.Warnw("Cannot perform client handshake", "error", err)
if err != nil {
logger.Warnw("Cannot perform client handshake", "error", err)
return return
} }
@@ -90,7 +88,7 @@ func (p *Proxy) accept(conn net.Conn) {
err = nil err = nil
if config.C.MiddleProxyMode() { if len(config.C.AdTag) > 0 {
middleConnection(req) middleConnection(req)
} else { } else {
err = directConnection(req) err = directConnection(req)
+12 -22
View File
@@ -11,58 +11,48 @@
# export MTG_IMAGENAME="nineseconds/mtg:latest" # export MTG_IMAGENAME="nineseconds/mtg:latest"
# curl -sfL --compressed https://raw.githubusercontent.com/9seconds/mtg/master/run.sh | bash # curl -sfL --compressed https://raw.githubusercontent.com/9seconds/mtg/master/run.sh | bash
set -eu set -eu -o pipefail
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
export MTG_CONFIG="${MTG_CONFIG:-$XDG_CONFIG_HOME/mtg}" export MTG_CONFIG="${MTG_CONFIG:-$XDG_CONFIG_HOME/mtg}"
if ! [ -x "$(command -v docker)" ]; then
echo 'Error: docker is not installed.' >&2
exit 1
fi
id -Gn "$USER" | grep -qw 'docker' > /dev/null
if [ $? -eq 0 ] || [ "$(id -u)" -eq '0' ]; then
DOCKER_CMD="$(command -v docker)"
else
DOCKER_CMD="sudo $(command -v docker)"
fi
mkdir -p "$MTG_CONFIG" || true mkdir -p "$MTG_CONFIG" || true
MTG_SECRET="$MTG_CONFIG/secret" MTG_SECRET="$MTG_CONFIG/secret"
MTG_ENV="$MTG_CONFIG/env" MTG_ENV="$MTG_CONFIG/env"
if [ ! -f "$MTG_ENV" ]; then if [ ! -f "$MTG_ENV" ]; then
MTG_IMAGENAME="${MTG_IMAGENAME:-nineseconds/mtg:stable}" MTG_IMAGENAME="${MTG_IMAGENAME:-nineseconds/mtg:latest}"
MTG_PORT="${MTG_PORT:-3128}" MTG_PORT="${MTG_PORT:-3128}"
MTG_CONTAINER="${MTG_CONTAINER:-mtg}" MTG_CONTAINER="${MTG_CONTAINER:-mtg}"
echo "MTG_IMAGENAME=$MTG_IMAGENAME" > "$MTG_ENV" echo "MTG_IMAGENAME=${MTG_IMAGENAME}" > "$MTG_ENV"
echo "MTG_PORT=$MTG_PORT" >> "$MTG_ENV" echo "MTG_PORT=${MTG_PORT}" >> "$MTG_ENV"
echo "MTG_CONTAINER=$MTG_CONTAINER" >> "$MTG_ENV" echo "MTG_CONTAINER=${MTG_CONTAINER}" >> "$MTG_ENV"
fi fi
set -a set -a
source "$MTG_ENV" source "$MTG_ENV"
set +a set +a
$DOCKER_CMD pull "$MTG_IMAGENAME" > /dev/null docker pull "$MTG_IMAGENAME"
if [ ! -f "$MTG_SECRET" ]; then if [ ! -f "$MTG_SECRET" ]; then
$DOCKER_CMD run \ docker run \
--rm \ --rm \
"$MTG_IMAGENAME" \ "$MTG_IMAGENAME" \
generate-secret tls -c "$(openssl rand -hex 16).com" \ generate-secret tls -c "$(openssl rand -hex 16).com" \
> "$MTG_SECRET" > "$MTG_SECRET"
fi fi
echo
echo "Proxy secret is $(cat "$MTG_SECRET"). Port is $MTG_PORT." echo "Proxy secret is $(cat "$MTG_SECRET"). Port is $MTG_PORT."
echo
$DOCKER_CMD ps --filter "Name=$MTG_CONTAINER" -aq | xargs -r $DOCKER_CMD rm -fv > /dev/null docker ps --filter "Name=$MTG_CONTAINER" -aq | xargs -r docker rm -fv
$DOCKER_CMD run \ docker run \
-d \ -d \
--restart=unless-stopped \ --restart=unless-stopped \
--name "$MTG_CONTAINER" \ --name "$MTG_CONTAINER" \
--ulimit nofile=51200:51200 \ --ulimit nofile=51200:51200 \
-p "$MTG_PORT:3128" \ -p "$MTG_PORT:3128" \
"$MTG_IMAGENAME" run "$(cat "$MTG_SECRET")" > /dev/null "$MTG_IMAGENAME" run "$(cat "$MTG_SECRET")"
+1 -11
View File
@@ -3,7 +3,7 @@ package stats
import ( import (
"net" "net"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
) )
type IngressTrafficInterface interface { type IngressTrafficInterface interface {
@@ -38,14 +38,6 @@ type ReplayDetectedInterface interface {
ReplayDetected() ReplayDetected()
} }
type AuthenticationFailedInterface interface {
AuthenticationFailed()
}
type CloakedRequestInterface interface {
CloakedRequest()
}
type Interface interface { type Interface interface {
IngressTrafficInterface IngressTrafficInterface
EgressTrafficInterface EgressTrafficInterface
@@ -55,6 +47,4 @@ type Interface interface {
TelegramDisconnectedInterface TelegramDisconnectedInterface
CrashInterface CrashInterface
ReplayDetectedInterface ReplayDetectedInterface
AuthenticationFailedInterface
CloakedRequestInterface
} }
+1 -13
View File
@@ -3,7 +3,7 @@ package stats
import ( import (
"net" "net"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
) )
type multiStats []Interface type multiStats []Interface
@@ -55,15 +55,3 @@ func (m multiStats) ReplayDetected() {
go m[i].ReplayDetected() go m[i].ReplayDetected()
} }
} }
func (m multiStats) AuthenticationFailed() {
for i := range m {
go m[i].AuthenticationFailed()
}
}
func (m multiStats) CloakedRequest() {
for i := range m {
go m[i].CloakedRequest()
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"net" "net"
"net/http" "net/http"
"github.com/9seconds/mtg/config" "mtg/config"
) )
var Stats Interface var Stats Interface
+4 -29
View File
@@ -5,10 +5,11 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/conntypes"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"mtg/config"
"mtg/conntypes"
) )
type statsPrometheus struct { type statsPrometheus struct {
@@ -17,8 +18,6 @@ type statsPrometheus struct {
traffic *prometheus.GaugeVec traffic *prometheus.GaugeVec
crashes prometheus.Counter crashes prometheus.Counter
replayAttacks prometheus.Counter replayAttacks prometheus.Counter
authenticationFailed prometheus.Counter
cloakedRequests prometheus.Counter
} }
func (s *statsPrometheus) IngressTraffic(traffic int) { func (s *statsPrometheus) IngressTraffic(traffic int) {
@@ -50,14 +49,10 @@ func (s *statsPrometheus) changeConnections(connectionType conntypes.ConnectionT
labels[0] = "abridged" labels[0] = "abridged"
case conntypes.ConnectionTypeSecure: case conntypes.ConnectionTypeSecure:
labels[0] = "secured" labels[0] = "secured"
case conntypes.ConnectionTypeIntermediate:
labels[0] = "intermediate"
case conntypes.ConnectionTypeUnknown:
panic("unknown connection type")
} }
if addr.IP.To4() == nil { if addr.IP.To4() == nil {
labels[1] = "ipv6" labels[1] = "ipv6" // nolint: goconst
} }
s.connections.WithLabelValues(labels[:]...).Add(increment) s.connections.WithLabelValues(labels[:]...).Add(increment)
@@ -92,14 +87,6 @@ func (s *statsPrometheus) ReplayDetected() {
s.replayAttacks.Inc() s.replayAttacks.Inc()
} }
func (s *statsPrometheus) AuthenticationFailed() {
s.authenticationFailed.Inc()
}
func (s *statsPrometheus) CloakedRequest() {
s.cloakedRequests.Inc()
}
func newStatsPrometheus(mux *http.ServeMux) Interface { func newStatsPrometheus(mux *http.ServeMux) Interface {
registry := prometheus.NewPedanticRegistry() registry := prometheus.NewPedanticRegistry()
@@ -129,16 +116,6 @@ func newStatsPrometheus(mux *http.ServeMux) Interface {
Name: "replay_attacks", Name: "replay_attacks",
Help: "How many replay attacks were prevented.", Help: "How many replay attacks were prevented.",
}), }),
authenticationFailed: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: config.C.StatsNamespace,
Name: "authentication_failed",
Help: "How many authentication failed events we've seen.",
}),
cloakedRequests: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: config.C.StatsNamespace,
Name: "cloaked_requests",
Help: "How many requests were proxified during cloaking.",
}),
} }
registry.MustRegister(instance.connections) registry.MustRegister(instance.connections)
@@ -146,8 +123,6 @@ func newStatsPrometheus(mux *http.ServeMux) Interface {
registry.MustRegister(instance.traffic) registry.MustRegister(instance.traffic)
registry.MustRegister(instance.crashes) registry.MustRegister(instance.crashes)
registry.MustRegister(instance.replayAttacks) registry.MustRegister(instance.replayAttacks)
registry.MustRegister(instance.authenticationFailed)
registry.MustRegister(instance.cloakedRequests)
handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{}) handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
mux.Handle("/", handler) mux.Handle("/", handler)
+7 -17
View File
@@ -8,10 +8,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/conntypes"
statsd "github.com/smira/go-statsd" statsd "github.com/smira/go-statsd"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/config"
"mtg/conntypes"
) )
var ( var (
@@ -90,10 +91,8 @@ func (s *statsStatsd) changeConnections(connectionType conntypes.ConnectionType,
tags = append(tags, tagConnectionTypeAbridged) tags = append(tags, tagConnectionTypeAbridged)
case conntypes.ConnectionTypeIntermediate: case conntypes.ConnectionTypeIntermediate:
tags = append(tags, tagConnectionTypeIntermediate) tags = append(tags, tagConnectionTypeIntermediate)
case conntypes.ConnectionTypeSecure: default:
tags = append(tags, tagConnectionTypeSecured) tags = append(tags, tagConnectionTypeSecured)
case conntypes.ConnectionTypeUnknown:
panic("Unknown connection type")
} }
if addr.IP.To4() == nil { if addr.IP.To4() == nil {
@@ -138,14 +137,6 @@ func (s *statsStatsd) ReplayDetected() {
s.gauge("replay_attacks", 1) s.gauge("replay_attacks", 1)
} }
func (s *statsStatsd) AuthenticationFailed() {
s.gauge("authentication_failed", 1)
}
func (s *statsStatsd) CloakedRequest() {
s.gauge("cloaked_requests", 1)
}
func (s *statsStatsd) gauge(metric string, value int64, tags ...*statsStatsdTag) { func (s *statsStatsd) gauge(metric string, value int64, tags ...*statsStatsdTag) {
key, tagList := s.prepareVals(metric, tags) key, tagList := s.prepareVals(metric, tags)
s.initGauge(metric, key, tagList) s.initGauge(metric, key, tagList)
@@ -168,18 +159,17 @@ func (s *statsStatsd) prepareVals(metric string, tags []*statsStatsdTag) (string
func (s *statsStatsd) initGauge(metric, key string, tags []statsd.Tag) { func (s *statsStatsd) initGauge(metric, key string, tags []statsd.Tag) {
s.seenMutex.RLock() s.seenMutex.RLock()
if _, ok := s.seen[key]; ok { _, ok := s.seen[key]
s.seenMutex.RUnlock() s.seenMutex.RUnlock()
if ok {
return return
} else { // nolint: golint,revive
s.seenMutex.RUnlock()
} }
s.seenMutex.Lock() s.seenMutex.Lock()
defer s.seenMutex.Unlock() defer s.seenMutex.Unlock()
if _, ok := s.seen[key]; !ok { if _, ok = s.seen[key]; !ok {
s.seen[key] = struct{}{} s.seen[key] = struct{}{}
s.client.Gauge(metric, 0, tags...) s.client.Gauge(metric, 0, tags...)
} }
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
) )
const ( const (
+3 -7
View File
@@ -1,7 +1,6 @@
package api package api
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@@ -10,7 +9,7 @@ import (
) )
const ( const (
apiUserAgent = "github.com/9seconds/mtg" apiUserAgent = "mtg"
apiHTTPTimeout = 30 * time.Second apiHTTPTimeout = 30 * time.Second
) )
@@ -19,10 +18,7 @@ var httpClient = http.Client{
} }
func request(url string) (io.ReadCloser, error) { func request(url string) (io.ReadCloser, error) {
ctx, cancel := context.WithTimeout(context.Background(), apiHTTPTimeout) req, err := http.NewRequest("GET", url, nil)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -40,5 +36,5 @@ func request(url string) (io.ReadCloser, error) {
return nil, fmt.Errorf("cannot perform a request: %w", err) return nil, fmt.Errorf("cannot perform a request: %w", err)
} }
return resp.Body, err // nolint: wrapcheck return resp.Body, err
} }
+18 -44
View File
@@ -1,24 +1,21 @@
package telegram package telegram
import ( import (
"errors" "fmt"
"math/rand" "math/rand"
"net" "net"
"github.com/9seconds/mtg/config" "mtg/conntypes"
"github.com/9seconds/mtg/conntypes" "mtg/utils"
"github.com/9seconds/mtg/utils" "mtg/wrappers/stream"
"github.com/9seconds/mtg/wrappers/stream"
"go.uber.org/zap"
) )
type baseTelegram struct { type baseTelegram struct {
dialer net.Dialer dialer net.Dialer
logger *zap.SugaredLogger
secret []byte secret []byte
v4DefaultDC conntypes.DC v4DefaultDC conntypes.DC
v6DefaultDC conntypes.DC V6DefaultDC conntypes.DC
v4Addresses map[conntypes.DC][]string v4Addresses map[conntypes.DC][]string
v6Addresses map[conntypes.DC][]string v6Addresses map[conntypes.DC][]string
} }
@@ -29,50 +26,27 @@ func (b *baseTelegram) Secret() []byte {
func (b *baseTelegram) dial(dc conntypes.DC, func (b *baseTelegram) dial(dc conntypes.DC,
protocol conntypes.ConnectionProtocol) (conntypes.StreamReadWriteCloser, error) { protocol conntypes.ConnectionProtocol) (conntypes.StreamReadWriteCloser, error) {
for _, addr := range b.getAddresses(dc, protocol) { addr := ""
conn, err := b.dialer.Dial("tcp", addr)
if err != nil {
b.logger.Infow("Cannot dial to Telegram", "address", addr, "error", err)
continue switch protocol {
case conntypes.ConnectionProtocolIPv4:
addr = b.chooseAddress(b.v4Addresses, dc, b.v4DefaultDC)
default:
addr = b.chooseAddress(b.v6Addresses, dc, b.V6DefaultDC)
} }
if err := utils.InitTCP(conn, config.C.ProxyReadBuffer(), config.C.ProxyWriteBuffer()); err != nil { conn, err := b.dialer.Dial("tcp", addr)
b.logger.Infow("Cannot initialize TCP socket", "address", addr, "error", err) if err != nil {
return nil, fmt.Errorf("dial has failed: %w", err)
}
continue if err := utils.InitTCP(conn); err != nil {
return nil, fmt.Errorf("cannot initialize tcp socket: %w", err)
} }
return stream.NewTelegramConn(dc, conn), nil return stream.NewTelegramConn(dc, conn), nil
} }
return nil, errors.New("cannot dial to the chosen DC")
}
func (b *baseTelegram) getAddresses(dc conntypes.DC, protocol conntypes.ConnectionProtocol) []string {
addresses := make([]string, 0, 2)
protos := []conntypes.ConnectionProtocol{
conntypes.ConnectionProtocolIPv6,
conntypes.ConnectionProtocolIPv4,
}
if config.C.PreferIP == config.PreferIPv4 {
protos[0], protos[1] = protos[1], protos[0]
}
for _, proto := range protos {
switch {
case proto&protocol == 0:
case proto&conntypes.ConnectionProtocolIPv6 != 0:
addresses = append(addresses, b.chooseAddress(b.v6Addresses, dc, b.v6DefaultDC))
case proto&conntypes.ConnectionProtocolIPv4 != 0:
addresses = append(addresses, b.chooseAddress(b.v4Addresses, dc, b.v4DefaultDC))
}
}
return addresses
}
func (b *baseTelegram) chooseAddress(addresses map[conntypes.DC][]string, func (b *baseTelegram) chooseAddress(addresses map[conntypes.DC][]string,
dc, defaultDC conntypes.DC) string { dc, defaultDC conntypes.DC) string {
addrs, ok := addresses[dc] addrs, ok := addresses[dc]
@@ -84,7 +58,7 @@ func (b *baseTelegram) chooseAddress(addresses map[conntypes.DC][]string,
case len(addrs) == 1: case len(addrs) == 1:
return addrs[0] return addrs[0]
case len(addrs) > 1: case len(addrs) > 1:
return addrs[rand.Intn(len(addrs))] // nolint: gosec return addrs[rand.Intn(len(addrs))]
} }
return "" return ""
+2 -2
View File
@@ -1,6 +1,6 @@
package telegram package telegram
import "github.com/9seconds/mtg/conntypes" import "mtg/conntypes"
const ( const (
directV4DefaultIdx conntypes.DC = 1 directV4DefaultIdx conntypes.DC = 1
@@ -37,5 +37,5 @@ func (d *directTelegram) Dial(dc conntypes.DC,
dc = conntypes.DCDefaultIdx dc = conntypes.DCDefaultIdx
} }
return d.baseTelegram.dial(dc-1, conntypes.ConnectionProtocolAny) return d.baseTelegram.dial(dc-1, protocol)
} }
+1 -7
View File
@@ -4,8 +4,6 @@ import (
"net" "net"
"sync" "sync"
"time" "time"
"go.uber.org/zap"
) )
const telegramDialTimeout = 10 * time.Second const telegramDialTimeout = 10 * time.Second
@@ -19,14 +17,11 @@ var (
func Init() { func Init() {
initOnce.Do(func() { initOnce.Do(func() {
logger := zap.S().Named("telegram")
Direct = &directTelegram{ Direct = &directTelegram{
baseTelegram: baseTelegram{ baseTelegram: baseTelegram{
dialer: net.Dialer{Timeout: telegramDialTimeout}, dialer: net.Dialer{Timeout: telegramDialTimeout},
logger: logger.Named("direct"),
v4DefaultDC: directV4DefaultIdx, v4DefaultDC: directV4DefaultIdx,
v6DefaultDC: directV6DefaultIdx, V6DefaultDC: directV6DefaultIdx,
v4Addresses: directV4Addresses, v4Addresses: directV4Addresses,
v6Addresses: directV6Addresses, v6Addresses: directV6Addresses,
}, },
@@ -35,7 +30,6 @@ func Init() {
tg := &middleTelegram{ tg := &middleTelegram{
baseTelegram: baseTelegram{ baseTelegram: baseTelegram{
dialer: net.Dialer{Timeout: telegramDialTimeout}, dialer: net.Dialer{Timeout: telegramDialTimeout},
logger: logger.Named("middle"),
}, },
} }
if err := tg.update(); err != nil { if err := tg.update(); err != nil {
+1 -1
View File
@@ -1,6 +1,6 @@
package telegram package telegram
import "github.com/9seconds/mtg/conntypes" import "mtg/conntypes"
type Telegram interface { type Telegram interface {
Dial(conntypes.DC, conntypes.ConnectionProtocol) (conntypes.StreamReadWriteCloser, error) Dial(conntypes.DC, conntypes.ConnectionProtocol) (conntypes.StreamReadWriteCloser, error)
+4 -3
View File
@@ -5,9 +5,10 @@ import (
"sync" "sync"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/telegram/api"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
"mtg/telegram/api"
) )
const middleTelegramBackgroundUpdateEvery = time.Hour const middleTelegramBackgroundUpdateEvery = time.Hour
@@ -44,7 +45,7 @@ func (m *middleTelegram) update() error {
m.mutex.Lock() m.mutex.Lock()
m.secret = secret m.secret = secret
m.v4DefaultDC = v4DefaultDC m.v4DefaultDC = v4DefaultDC
m.v6DefaultDC = v6DefaultDC m.V6DefaultDC = v6DefaultDC
m.v4Addresses = v4Addresses m.v4Addresses = v4Addresses
m.v6Addresses = v6Addresses m.v6Addresses = v6Addresses
m.mutex.Unlock() m.mutex.Unlock()
+3 -3
View File
@@ -6,8 +6,8 @@ import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/9seconds/mtg/config" "mtg/config"
"github.com/9seconds/mtg/utils" "mtg/utils"
) )
type ClientHello struct { type ClientHello struct {
@@ -25,7 +25,7 @@ func (c ClientHello) Digest() []byte {
} }
mac := hmac.New(sha256.New, config.C.Secret) mac := hmac.New(sha256.New, config.C.Secret)
rec.WriteBytes(mac) mac.Write(rec.Bytes()) // nolint: errcheck
computedDigest := mac.Sum(nil) computedDigest := mac.Sum(nil)
for i := range computedDigest { for i := range computedDigest {
+9 -20
View File
@@ -1,7 +1,5 @@
package tlstypes package tlstypes
import "io"
type RecordType uint8 type RecordType uint8
const ( const (
@@ -20,9 +18,9 @@ const (
type CipherSuiteType uint8 type CipherSuiteType uint8
const ( const (
CipherSuiteType_TLS_AES_128_GCM_SHA256 CipherSuiteType = iota // nolint: stylecheck,golint,revive CipherSuiteType_TLS_AES_128_GCM_SHA256 CipherSuiteType = iota // nolint: stylecheck, golint
CipherSuiteType_TLS_AES_256_GCM_SHA384 // nolint: stylecheck,golint,revive CipherSuiteType_TLS_AES_256_GCM_SHA384 // nolint: stylecheck, golint
CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256 // nolint: stylecheck,golint,revive CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256 // nolint: stylecheck, golint
) )
func (c CipherSuiteType) Bytes() []byte { func (c CipherSuiteType) Bytes() []byte {
@@ -31,8 +29,6 @@ func (c CipherSuiteType) Bytes() []byte {
return CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes return CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes
case CipherSuiteType_TLS_AES_256_GCM_SHA384: case CipherSuiteType_TLS_AES_256_GCM_SHA384:
return CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes return CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes
case CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256:
return CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes
} }
return CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes return CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes
@@ -48,8 +44,6 @@ func (v Version) Bytes() []byte {
return Version12Bytes return Version12Bytes
case Version11: case Version11:
return Version11Bytes return Version11Bytes
case Version10, VersionUnknown:
return Version10Bytes
} }
return Version10Bytes return Version10Bytes
@@ -69,22 +63,17 @@ var (
Version12Bytes = []byte{0x03, 0x03} Version12Bytes = []byte{0x03, 0x03}
Version13Bytes = []byte{0x03, 0x04} Version13Bytes = []byte{0x03, 0x04}
CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes = []byte{0x13, 0x01} // nolint: stylecheck,golint,revive CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes = []byte{0x13, 0x01} // nolint: stylecheck, golint
CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes = []byte{0x13, 0x02} // nolint: stylecheck,golint,revive CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes = []byte{0x13, 0x02} // nolint: stylecheck, golint
CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes = []byte{0x13, 0x03} // nolint: stylecheck,golint,revive CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes = []byte{0x13, 0x03} // nolint; stylecheck, golint
) )
type Byter interface { type Byter interface {
WriteBytes(io.Writer) Bytes() []byte
Len() int
} }
type RawBytes []byte type RawBytes []byte
func (r RawBytes) WriteBytes(writer io.Writer) { func (r RawBytes) Bytes() []byte {
writer.Write(r) // nolint: errcheck return []byte(r)
}
func (r RawBytes) Len() int {
return len(r)
} }
+8 -14
View File
@@ -2,9 +2,8 @@ package tlstypes
import ( import (
"bytes" "bytes"
"io"
"github.com/9seconds/mtg/utils" "mtg/utils"
) )
type Handshake struct { type Handshake struct {
@@ -15,29 +14,24 @@ type Handshake struct {
Tail Byter Tail Byter
} }
func (h *Handshake) WriteBytes(writer io.Writer) { func (h *Handshake) Bytes() []byte {
buf := bytes.Buffer{}
packetBuf := bytes.Buffer{} packetBuf := bytes.Buffer{}
writer.Write([]byte{byte(h.Type)}) // nolint: errcheck buf.WriteByte(byte(h.Type))
packetBuf.Write(h.Version.Bytes()) packetBuf.Write(h.Version.Bytes())
packetBuf.Write(h.Random[:]) packetBuf.Write(h.Random[:])
packetBuf.WriteByte(byte(len(h.SessionID))) packetBuf.WriteByte(byte(len(h.SessionID)))
packetBuf.Write(h.SessionID) packetBuf.Write(h.SessionID)
h.Tail.WriteBytes(&packetBuf) packetBuf.Write(h.Tail.Bytes())
sizeUint24 := utils.ToUint24(uint32(packetBuf.Len())) sizeUint24 := utils.ToUint24(uint32(packetBuf.Len()))
sizeUint24Bytes := sizeUint24[:] sizeUint24Bytes := sizeUint24[:]
sizeUint24Bytes[0], sizeUint24Bytes[2] = sizeUint24Bytes[2], sizeUint24Bytes[0] sizeUint24Bytes[0], sizeUint24Bytes[2] = sizeUint24Bytes[2], sizeUint24Bytes[0]
writer.Write(sizeUint24Bytes) // nolint: errcheck buf.Write(sizeUint24Bytes)
packetBuf.WriteTo(writer) // nolint: errcheck packetBuf.WriteTo(&buf) // nolint: errcheck
}
func (h *Handshake) Len() int { return buf.Bytes()
buf := bytes.Buffer{}
h.WriteBytes(&buf)
return buf.Len()
} }
+9 -8
View File
@@ -15,15 +15,16 @@ type Record struct {
Data Byter Data Byter
} }
func (r Record) WriteBytes(writer io.Writer) { func (r Record) Bytes() []byte {
writer.Write([]byte{byte(r.Type)}) // nolint: errcheck buf := bytes.Buffer{}
writer.Write(r.Version.Bytes()) // nolint: errcheck data := r.Data.Bytes()
binary.Write(writer, binary.BigEndian, uint16(r.Data.Len())) // nolint: errcheck
r.Data.WriteBytes(writer)
}
func (r Record) Len() int { buf.WriteByte(byte(r.Type))
return 1 + 2 + 2 + r.Data.Len() buf.Write(r.Version.Bytes())
binary.Write(&buf, binary.BigEndian, uint16(len(data))) // nolint: errcheck
buf.Write(data)
return buf.Bytes()
} }
func ReadRecord(reader io.Reader) (Record, error) { func ReadRecord(reader io.Reader) (Record, error) {
+8 -10
View File
@@ -8,8 +8,9 @@ import (
"io" "io"
mrand "math/rand" mrand "math/rand"
"github.com/9seconds/mtg/config"
"golang.org/x/crypto/curve25519" "golang.org/x/crypto/curve25519"
"mtg/config"
) )
type ServerHello struct { type ServerHello struct {
@@ -19,24 +20,22 @@ type ServerHello struct {
} }
func (s ServerHello) WelcomePacket() []byte { func (s ServerHello) WelcomePacket() []byte {
buf := &bytes.Buffer{}
s.Random = [32]byte{} s.Random = [32]byte{}
rec := Record{ rec := Record{
Type: RecordTypeHandshake, Type: RecordTypeHandshake,
Version: Version12, Version: Version12,
Data: &s, Data: &s,
} }
rec.WriteBytes(buf) buf := bytes.NewBuffer(rec.Bytes())
recChangeCipher := Record{ recChangeCipher := Record{
Type: RecordTypeChangeCipherSpec, Type: RecordTypeChangeCipherSpec,
Version: Version12, Version: Version12,
Data: RawBytes([]byte{0x01}), Data: RawBytes([]byte{0x01}),
} }
recChangeCipher.WriteBytes(buf) buf.Write(recChangeCipher.Bytes())
hostCert := make([]byte, 1024+mrand.Intn(3092)) // nolint: gosec hostCert := make([]byte, 1024+mrand.Intn(3092))
rand.Read(hostCert) // nolint: errcheck rand.Read(hostCert) // nolint: errcheck
recData := Record{ recData := Record{
@@ -44,13 +43,12 @@ func (s ServerHello) WelcomePacket() []byte {
Version: Version12, Version: Version12,
Data: RawBytes(hostCert), Data: RawBytes(hostCert),
} }
recData.WriteBytes(buf) buf.Write(recData.Bytes())
packet := buf.Bytes() packet := buf.Bytes()
mac := hmac.New(sha256.New, config.C.Secret) mac := hmac.New(sha256.New, config.C.Secret)
mac.Write(s.clientHello.Random[:]) mac.Write(s.clientHello.Random[:]) // nolint: errcheck
mac.Write(packet) mac.Write(packet) // nolint: errcheck
copy(packet[11:], mac.Sum(nil)) copy(packet[11:], mac.Sum(nil))
return packet return packet
+6 -8
View File
@@ -3,26 +3,24 @@ package utils
import ( import (
"fmt" "fmt"
"net" "net"
"mtg/config"
) )
func InitTCP(conn net.Conn, readBufferSize int, writeBufferSize int) error { func InitTCP(conn net.Conn) error {
tcpConn := conn.(*net.TCPConn) // nolint: forcetypeassert tcpConn := conn.(*net.TCPConn)
if err := tcpConn.SetNoDelay(true); err != nil { if err := tcpConn.SetNoDelay(true); err != nil {
return fmt.Errorf("cannot set TCP_NO_DELAY: %w", err) return fmt.Errorf("cannot set TCP_NO_DELAY: %w", err)
} }
if err := tcpConn.SetReadBuffer(readBufferSize); err != nil { if err := tcpConn.SetReadBuffer(config.C.ReadBuffer); err != nil {
return fmt.Errorf("cannot set read buffer size: %w", err) return fmt.Errorf("cannot set read buffer size: %w", err)
} }
if err := tcpConn.SetWriteBuffer(writeBufferSize); err != nil { if err := tcpConn.SetWriteBuffer(config.C.WriteBuffer); err != nil {
return fmt.Errorf("cannot set write buffer size: %w", err) return fmt.Errorf("cannot set write buffer size: %w", err)
} }
if err := tcpConn.SetKeepAlive(true); err != nil {
return fmt.Errorf("cannot enable keep-alive: %w", err)
}
return nil return nil
} }
+1 -1
View File
@@ -11,7 +11,7 @@ func ReadFull(src io.Reader) (rv []byte, err error) {
for n == len(buf) { for n == len(buf) {
n, err = src.Read(buf) n, err = src.Read(buf)
if err != nil { if err != nil {
return nil, err // nolint: wrapcheck return nil, err
} }
rv = append(rv, buf[:n]...) rv = append(rv, buf[:n]...)
-1
View File
@@ -1,4 +1,3 @@
//go:build !windows
// +build !windows // +build !windows
package utils package utils
-1
View File
@@ -1,4 +1,3 @@
//go:build windows
// +build windows // +build windows
package utils package utils
-1
View File
@@ -1,4 +1,3 @@
//go:build !windows
// +build !windows // +build !windows
package utils package utils
-1
View File
@@ -1,4 +1,3 @@
//go:build windows
// +build windows // +build windows
package utils package utils
+1 -2
View File
@@ -6,7 +6,6 @@ import (
) )
func MakeStreamCipher(key, iv []byte) cipher.Stream { func MakeStreamCipher(key, iv []byte) cipher.Stream {
block, _ := aes.NewCipher(key) block, _ := aes.NewCipher(key) // nolint: gosec
return cipher.NewCTR(block, iv) return cipher.NewCTR(block, iv)
} }
+8 -6
View File
@@ -10,8 +10,9 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
) )
const ( const (
@@ -40,9 +41,8 @@ type wrapperMtprotoFrame struct {
writeSeqNo int32 writeSeqNo int32
} }
func (w *wrapperMtprotoFrame) Read() (conntypes.Packet, error) { // nolint: funlen, cyclop func (w *wrapperMtprotoFrame) Read() (conntypes.Packet, error) { // nolint: funlen
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
sum := crc32.NewIEEE() sum := crc32.NewIEEE()
writer := io.MultiWriter(buf, sum) writer := io.MultiWriter(buf, sum)
@@ -71,6 +71,7 @@ func (w *wrapperMtprotoFrame) Read() (conntypes.Packet, error) { // nolint: funl
} }
buf.Reset() buf.Reset()
buf.Grow(int(messageLength) - 4 - 4)
if _, err := io.CopyN(writer, w.parent, int64(messageLength)-4-4); err != nil { if _, err := io.CopyN(writer, w.parent, int64(messageLength)-4-4); err != nil {
return nil, fmt.Errorf("cannot read the message frame: %w", err) return nil, fmt.Errorf("cannot read the message frame: %w", err)
@@ -84,7 +85,7 @@ func (w *wrapperMtprotoFrame) Read() (conntypes.Packet, error) { // nolint: funl
return nil, fmt.Errorf("unexpected sequence number %d (wait for %d)", seqNo, w.readSeqNo) return nil, fmt.Errorf("unexpected sequence number %d (wait for %d)", seqNo, w.readSeqNo)
} }
data, _ := ioutil.ReadAll(buf) data, _ := ioutil.ReadAll(buf) // nolint: gosec
buf.Reset() buf.Reset()
// write to buf, not to writer. This is because we are going to fetch // write to buf, not to writer. This is because we are going to fetch
// crc32 checksum. // crc32 checksum.
@@ -113,6 +114,7 @@ func (w *wrapperMtprotoFrame) Write(p conntypes.Packet) error {
paddingLength := (aes.BlockSize - messageLength%aes.BlockSize) % aes.BlockSize paddingLength := (aes.BlockSize - messageLength%aes.BlockSize) % aes.BlockSize
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.Grow(messageLength + paddingLength)
binary.Write(buf, binary.LittleEndian, uint32(messageLength)) // nolint: errcheck binary.Write(buf, binary.LittleEndian, uint32(messageLength)) // nolint: errcheck
binary.Write(buf, binary.LittleEndian, w.writeSeqNo) // nolint: errcheck binary.Write(buf, binary.LittleEndian, w.writeSeqNo) // nolint: errcheck
@@ -132,11 +134,11 @@ func (w *wrapperMtprotoFrame) Write(p conntypes.Packet) error {
_, err := w.parent.Write(buf.Bytes()) _, err := w.parent.Write(buf.Bytes())
return err // nolint: wrapcheck return err
} }
func (w *wrapperMtprotoFrame) Close() error { func (w *wrapperMtprotoFrame) Close() error {
return w.parent.Close() // nolint: wrapcheck return w.parent.Close()
} }
func (w *wrapperMtprotoFrame) Conn() net.Conn { func (w *wrapperMtprotoFrame) Conn() net.Conn {
+4 -3
View File
@@ -6,9 +6,10 @@ import (
"io" "io"
"net" "net"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/utils"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
"mtg/utils"
) )
const ( const (
@@ -104,7 +105,7 @@ func (w *wrapperClientAbridged) Write(packet conntypes.Packet, acks *conntypes.C
} }
func (w *wrapperClientAbridged) Close() error { func (w *wrapperClientAbridged) Close() error {
return w.parent.Close() // nolint: wrapcheck return w.parent.Close()
} }
func (w *wrapperClientAbridged) Conn() net.Conn { func (w *wrapperClientAbridged) Conn() net.Conn {
+3 -2
View File
@@ -7,8 +7,9 @@ import (
"io" "io"
"net" "net"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
) )
const clientIntermediateQuickAckLength = 0x80000000 const clientIntermediateQuickAckLength = 0x80000000
@@ -63,7 +64,7 @@ func (w *wrapperClientIntermediate) Write(packet conntypes.Packet, acks *conntyp
} }
func (w *wrapperClientIntermediate) Close() error { func (w *wrapperClientIntermediate) Close() error {
return w.parent.Close() // nolint: wrapcheck return w.parent.Close()
} }
func (w *wrapperClientIntermediate) Conn() net.Conn { func (w *wrapperClientIntermediate) Conn() net.Conn {
@@ -6,8 +6,9 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
) )
type wrapperClientIntermediateSecure struct { type wrapperClientIntermediateSecure struct {
@@ -34,12 +35,11 @@ func (w *wrapperClientIntermediateSecure) Write(packet conntypes.Packet, acks *c
return nil return nil
} }
buf := &bytes.Buffer{} buf := bytes.Buffer{}
paddingLength := rand.Intn(4) // nolint: gosec paddingLength := rand.Intn(4)
buf.Grow(4 + len(packet) + paddingLength) buf.Grow(4 + len(packet) + paddingLength)
binary.Write(buf, binary.LittleEndian, uint32(len(packet)+paddingLength)) // nolint: errcheck binary.Write(&buf, binary.LittleEndian, uint32(len(packet)+paddingLength)) // nolint: errcheck
buf.Write(packet) buf.Write(packet)
buf.Write(make([]byte, paddingLength)) buf.Write(make([]byte, paddingLength))
+8 -10
View File
@@ -6,11 +6,11 @@ import (
"fmt" "fmt"
"net" "net"
"github.com/9seconds/mtg/config" "mtg/config"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
"github.com/9seconds/mtg/hub" "mtg/hub"
"github.com/9seconds/mtg/mtproto/rpc" "mtg/mtproto/rpc"
"github.com/9seconds/mtg/protocol" "mtg/protocol"
) )
type wrapperProxy struct { type wrapperProxy struct {
@@ -23,8 +23,8 @@ type wrapperProxy struct {
func (w *wrapperProxy) Write(packet conntypes.Packet, acks *conntypes.ConnectionAcks) error { func (w *wrapperProxy) Write(packet conntypes.Packet, acks *conntypes.ConnectionAcks) error {
buf := bytes.Buffer{} buf := bytes.Buffer{}
flags := w.flags
flags := w.flags
if acks.Quick { if acks.Quick {
flags |= rpc.ProxyRequestFlagsQuickAck flags |= rpc.ProxyRequestFlagsQuickAck
} }
@@ -43,10 +43,9 @@ func (w *wrapperProxy) Write(packet conntypes.Packet, acks *conntypes.Connection
buf.WriteByte(byte(len(config.C.AdTag))) buf.WriteByte(byte(len(config.C.AdTag)))
buf.Write(config.C.AdTag) buf.Write(config.C.AdTag)
buf.Write(make([]byte, (4-buf.Len()%4)%4)) buf.Write(make([]byte, (4-buf.Len()%4)%4))
buf.Grow(len(packet))
buf.Write(packet) buf.Write(packet)
return w.proxy.Write(buf.Bytes()) // nolint: wrapcheck return w.proxy.Write(buf.Bytes())
} }
func (w *wrapperProxy) Read(acks *conntypes.ConnectionAcks) (conntypes.Packet, error) { func (w *wrapperProxy) Read(acks *conntypes.ConnectionAcks) (conntypes.Packet, error) {
@@ -64,7 +63,6 @@ func (w *wrapperProxy) Read(acks *conntypes.ConnectionAcks) (conntypes.Packet, e
func (w *wrapperProxy) Close() error { func (w *wrapperProxy) Close() error {
w.proxy.Close() w.proxy.Close()
return nil return nil
} }
@@ -78,7 +76,7 @@ func NewProxy(request *protocol.TelegramRequest) (conntypes.PacketAckReadWriteCl
flags |= rpc.ProxyRequestFlagsIntermediate flags |= rpc.ProxyRequestFlagsIntermediate
case conntypes.ConnectionTypeSecure: case conntypes.ConnectionTypeSecure:
flags |= rpc.ProxyRequestFlagsIntermediate | rpc.ProxyRequestFlagsPad flags |= rpc.ProxyRequestFlagsIntermediate | rpc.ProxyRequestFlagsPad
case conntypes.ConnectionTypeUnknown: default:
panic("unknown connection type") panic("unknown connection type")
} }
-48
View File
@@ -1,48 +0,0 @@
package rwc
import (
"context"
"io"
)
type wrapperPing struct {
parent io.ReadWriteCloser
ctx context.Context
channelPing chan<- struct{}
}
func (w *wrapperPing) Read(p []byte) (int, error) {
n, err := w.parent.Read(p)
if err == nil {
select {
case <-w.ctx.Done():
case w.channelPing <- struct{}{}:
}
}
return n, err // nolint: wrapcheck
}
func (w *wrapperPing) Write(p []byte) (int, error) {
n, err := w.parent.Write(p)
if err == nil {
select {
case <-w.ctx.Done():
case w.channelPing <- struct{}{}:
}
}
return n, err // nolint: wrapcheck
}
func (w *wrapperPing) Close() error {
return w.parent.Close() // nolint: wrapcheck
}
func NewPing(ctx context.Context, parent io.ReadWriteCloser, channelPing chan<- struct{}) io.ReadWriteCloser {
return &wrapperPing{
parent: parent,
ctx: ctx,
channelPing: channelPing,
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ package stream
import ( import (
"net" "net"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
) )
func NewClientConn(parent net.Conn, connID conntypes.ConnID) conntypes.StreamReadWriteCloser { func NewClientConn(parent net.Conn, connID conntypes.ConnID) conntypes.StreamReadWriteCloser {
+6 -5
View File
@@ -7,9 +7,10 @@ import (
"net" "net"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/utils"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
"mtg/utils"
) )
type wrapperBlockCipher struct { type wrapperBlockCipher struct {
@@ -26,7 +27,7 @@ func (w *wrapperBlockCipher) Write(p []byte) (int, error) {
return 0, err return 0, err
} }
return w.parent.Write(encrypted) // nolint: wrapcheck return w.parent.Write(encrypted)
} }
func (w *wrapperBlockCipher) WriteTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperBlockCipher) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
@@ -35,7 +36,7 @@ func (w *wrapperBlockCipher) WriteTimeout(p []byte, timeout time.Duration) (int,
return 0, err return 0, err
} }
return w.parent.WriteTimeout(encrypted, timeout) // nolint: wrapcheck return w.parent.WriteTimeout(encrypted, timeout)
} }
func (w *wrapperBlockCipher) encrypt(p []byte) ([]byte, error) { func (w *wrapperBlockCipher) encrypt(p []byte) ([]byte, error) {
@@ -50,7 +51,7 @@ func (w *wrapperBlockCipher) encrypt(p []byte) ([]byte, error) {
} }
func (w *wrapperBlockCipher) Close() error { func (w *wrapperBlockCipher) Close() error {
return w.parent.Close() // nolint: wrapcheck return w.parent.Close()
} }
func (w *wrapperBlockCipher) Conn() net.Conn { func (w *wrapperBlockCipher) Conn() net.Conn {
+1 -1
View File
@@ -33,7 +33,7 @@ func (b *bufferedReader) ReadTimeout(p []byte, _ time.Duration) (int, error) {
func (b *bufferedReader) flush(p []byte) (int, error) { func (b *bufferedReader) flush(p []byte) (int, error) {
if b.buf.Len() > len(p) { if b.buf.Len() > len(p) {
return b.buf.Read(p) // nolint: wrapcheck return b.buf.Read(p)
} }
sizeToReturn := b.buf.Len() sizeToReturn := b.buf.Len()
+7 -9
View File
@@ -5,9 +5,10 @@ import (
"net" "net"
"time" "time"
"github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/config"
"mtg/conntypes"
) )
type connPurpose uint8 type connPurpose uint8
@@ -28,7 +29,6 @@ type wrapperConn struct {
func (w *wrapperConn) WriteTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperConn) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
if err := w.parent.SetWriteDeadline(time.Now().Add(timeout)); err != nil { if err := w.parent.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
w.Close() w.Close()
return 0, fmt.Errorf("cannot set write deadline to the socket: %w", err) return 0, fmt.Errorf("cannot set write deadline to the socket: %w", err)
} }
@@ -40,16 +40,15 @@ func (w *wrapperConn) Write(p []byte) (int, error) {
w.logger.Debugw("write to stream", "bytes", n, "error", err) w.logger.Debugw("write to stream", "bytes", n, "error", err)
if err != nil { if err != nil {
w.Close() w.Close() // nolint: gosec
} }
return n, err // nolint: wrapcheck return n, err
} }
func (w *wrapperConn) ReadTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperConn) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
if err := w.parent.SetReadDeadline(time.Now().Add(timeout)); err != nil { if err := w.parent.SetReadDeadline(time.Now().Add(timeout)); err != nil {
w.Close() w.Close()
return 0, fmt.Errorf("cannot set read deadline to the socket: %w", err) return 0, fmt.Errorf("cannot set read deadline to the socket: %w", err)
} }
@@ -64,13 +63,12 @@ func (w *wrapperConn) Read(p []byte) (int, error) {
w.Close() w.Close()
} }
return n, err // nolint: wrapcheck return n, err
} }
func (w *wrapperConn) Close() error { func (w *wrapperConn) Close() error {
w.logger.Debugw("Close connection") w.logger.Debugw("Close connection")
return w.parent.Close()
return w.parent.Close() // nolint: wrapcheck
} }
func (w *wrapperConn) Conn() net.Conn { func (w *wrapperConn) Conn() net.Conn {
+7 -11
View File
@@ -6,8 +6,9 @@ import (
"net" "net"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
) )
type wrapperCtx struct { type wrapperCtx struct {
@@ -20,10 +21,9 @@ func (w *wrapperCtx) WriteTimeout(p []byte, timeout time.Duration) (int, error)
select { select {
case <-w.ctx.Done(): case <-w.ctx.Done():
w.Close() w.Close()
return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err()) return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err())
default: default:
return w.parent.WriteTimeout(p, timeout) // nolint: wrapcheck return w.parent.WriteTimeout(p, timeout)
} }
} }
@@ -31,10 +31,9 @@ func (w *wrapperCtx) Write(p []byte) (int, error) {
select { select {
case <-w.ctx.Done(): case <-w.ctx.Done():
w.Close() w.Close()
return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err()) return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err())
default: default:
return w.parent.Write(p) // nolint: wrapcheck return w.parent.Write(p)
} }
} }
@@ -42,10 +41,9 @@ func (w *wrapperCtx) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
select { select {
case <-w.ctx.Done(): case <-w.ctx.Done():
w.Close() w.Close()
return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err()) return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err())
default: default:
return w.parent.ReadTimeout(p, timeout) // nolint: wrapcheck return w.parent.ReadTimeout(p, timeout)
} }
} }
@@ -53,17 +51,15 @@ func (w *wrapperCtx) Read(p []byte) (int, error) {
select { select {
case <-w.ctx.Done(): case <-w.ctx.Done():
w.Close() w.Close()
return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err()) return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err())
default: default:
return w.parent.Read(p) // nolint: wrapcheck return w.parent.Read(p)
} }
} }
func (w *wrapperCtx) Close() error { func (w *wrapperCtx) Close() error {
w.cancel() w.cancel()
return w.parent.Close()
return w.parent.Close() // nolint: wrapcheck
} }
func (w *wrapperCtx) Conn() net.Conn { func (w *wrapperCtx) Conn() net.Conn {
+10 -20
View File
@@ -1,15 +1,15 @@
package stream package stream
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"net" "net"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/tlstypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
"mtg/tlstypes"
) )
type wrapperFakeTLS struct { type wrapperFakeTLS struct {
@@ -20,7 +20,7 @@ type wrapperFakeTLS struct {
func (w *wrapperFakeTLS) Write(p []byte) (int, error) { func (w *wrapperFakeTLS) Write(p []byte) (int, error) {
return w.write(p, func(b []byte) (int, error) { return w.write(p, func(b []byte) (int, error) {
return w.parent.Write(b) // nolint: wrapcheck return w.parent.Write(b)
}) })
} }
@@ -30,27 +30,22 @@ func (w *wrapperFakeTLS) WriteTimeout(p []byte, timeout time.Duration) (int, err
return w.write(p, func(b []byte) (int, error) { return w.write(p, func(b []byte) (int, error) {
elapsed := time.Since(startTime) elapsed := time.Since(startTime)
if elapsed > timeout { if elapsed > timeout {
return w.parent.WriteTimeout(b, timeout-elapsed) // nolint: wrapcheck return w.parent.WriteTimeout(b, timeout-elapsed)
} }
return 0, errors.New("timeout") return 0, errors.New("timeout")
}) })
} }
func (w *wrapperFakeTLS) write(p []byte, writeFunc func([]byte) (int, error)) (int, error) { func (w *wrapperFakeTLS) write(p []byte, writeFunc func([]byte) (int, error)) (int, error) {
sum := 0 sum := 0
buf := bytes.Buffer{}
for _, v := range tlstypes.MakeRecords(p) { for _, v := range tlstypes.MakeRecords(p) {
buf.Reset() _, err := writeFunc(v.Bytes())
v.WriteBytes(&buf)
_, err := writeFunc(buf.Bytes())
if err != nil { if err != nil {
return sum, err return sum, err
} }
sum += v.Data.Len() sum += len(v.Data.Bytes())
} }
return sum, nil return sum, nil
@@ -73,7 +68,7 @@ func (w *wrapperFakeTLS) RemoteAddr() *net.TCPAddr {
} }
func (w *wrapperFakeTLS) Close() error { func (w *wrapperFakeTLS) Close() error {
return w.parent.Close() // nolint: wrapcheck return w.parent.Close()
} }
func NewFakeTLS(socket conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser { func NewFakeTLS(socket conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser {
@@ -85,18 +80,13 @@ func NewFakeTLS(socket conntypes.StreamReadWriteCloser) conntypes.StreamReadWrit
for { for {
rec, err := tlstypes.ReadRecord(faketls.parent) rec, err := tlstypes.ReadRecord(faketls.parent)
if err != nil { if err != nil {
return nil, err // nolint: wrapcheck return nil, err
} }
switch rec.Type { switch rec.Type {
case tlstypes.RecordTypeChangeCipherSpec: case tlstypes.RecordTypeChangeCipherSpec:
case tlstypes.RecordTypeApplicationData: case tlstypes.RecordTypeApplicationData:
buf := &bytes.Buffer{} return rec.Data.Bytes(), nil
rec.Data.WriteBytes(buf)
return buf.Bytes(), nil
case tlstypes.RecordTypeHandshake:
return nil, errors.New("unsupported record type handshake")
default: default:
return nil, fmt.Errorf("unsupported record type %v", rec.Type) return nil, fmt.Errorf("unsupported record type %v", rec.Type)
} }
+19 -20
View File
@@ -4,14 +4,14 @@ import (
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/md5" "crypto/md5" // nolint: gosec
"crypto/sha1" "crypto/sha1" // nolint: gosec
"encoding/binary" "encoding/binary"
"net" "net"
"github.com/9seconds/mtg/conntypes" "mtg/conntypes"
"github.com/9seconds/mtg/mtproto/rpc" "mtg/mtproto/rpc"
"github.com/9seconds/mtg/utils" "mtg/utils"
) )
type mtprotoCipherPurpose uint8 type mtprotoCipherPurpose uint8
@@ -55,10 +55,9 @@ func mtprotoDeriveKeys(purpose mtprotoCipherPurpose,
client, remote *net.TCPAddr, client, remote *net.TCPAddr,
secret []byte) ([]byte, []byte) { secret []byte) ([]byte, []byte) {
message := bytes.Buffer{} message := bytes.Buffer{}
message.Write(resp.Nonce) // nolint: gosec
message.Write(resp.Nonce) message.Write(req.Nonce) // nolint: gosec
message.Write(req.Nonce) message.Write(req.CryptoTS) // nolint: gosec
message.Write(req.CryptoTS)
clientIPv4 := mtprotoEmptyIP[:] clientIPv4 := mtprotoEmptyIP[:]
serverIPv4 := mtprotoEmptyIP[:] serverIPv4 := mtprotoEmptyIP[:]
@@ -68,34 +67,34 @@ func mtprotoDeriveKeys(purpose mtprotoCipherPurpose,
serverIPv4 = utils.ReverseBytes(remote.IP.To4()) serverIPv4 = utils.ReverseBytes(remote.IP.To4())
} }
message.Write(serverIPv4) message.Write(serverIPv4) // nolint: gosec
var port [2]byte var port [2]byte
binary.LittleEndian.PutUint16(port[:], uint16(client.Port)) binary.LittleEndian.PutUint16(port[:], uint16(client.Port))
message.Write(port[:]) message.Write(port[:]) // nolint: gosec
switch purpose { switch purpose {
case mtprotoCipherPurposeClient: case mtprotoCipherPurposeClient:
message.WriteString("CLIENT") message.WriteString("CLIENT") // nolint: gosec
case mtprotoCipherPurposeServer: case mtprotoCipherPurposeServer:
message.WriteString("SERVER") message.WriteString("SERVER") // nolint: gosec
default: default:
panic("Unexpected cipher purpose") panic("Unexpected cipher purpose")
} }
message.Write(clientIPv4) message.Write(clientIPv4) // nolint: gosec
binary.LittleEndian.PutUint16(port[:], uint16(remote.Port)) binary.LittleEndian.PutUint16(port[:], uint16(remote.Port))
message.Write(port[:]) message.Write(port[:]) // nolint: gosec
message.Write(secret) message.Write(secret) // nolint: gosec
message.Write(resp.Nonce) message.Write(resp.Nonce) // nolint: gosec
if client.IP.To4() == nil { if client.IP.To4() == nil {
message.Write(client.IP.To16()) message.Write(client.IP.To16()) // nolint: gosec
message.Write(remote.IP.To16()) message.Write(remote.IP.To16()) // nolint: gosec
} }
message.Write(req.Nonce) message.Write(req.Nonce) // nolint: gosec
data := message.Bytes() data := message.Bytes()
md5sum := md5.Sum(data[1:]) // nolint: gas md5sum := md5.Sum(data[1:]) // nolint: gas
+10 -18
View File
@@ -1,14 +1,14 @@
package stream package stream
import ( import (
"bytes"
"crypto/cipher" "crypto/cipher"
"fmt" "fmt"
"net" "net"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
) )
type wrapperObfuscated2 struct { type wrapperObfuscated2 struct {
@@ -31,7 +31,7 @@ func (w *wrapperObfuscated2) ReadTimeout(p []byte, timeout time.Duration) (int,
func (w *wrapperObfuscated2) Read(p []byte) (int, error) { func (w *wrapperObfuscated2) Read(p []byte) (int, error) {
n, err := w.parent.Read(p) n, err := w.parent.Read(p)
if err != nil { if err != nil {
return n, err // nolint: wrapcheck return n, err
} }
w.decryptor.XORKeyStream(p, p[:n]) w.decryptor.XORKeyStream(p, p[:n])
@@ -40,27 +40,19 @@ func (w *wrapperObfuscated2) Read(p []byte) (int, error) {
} }
func (w *wrapperObfuscated2) WriteTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperObfuscated2) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
buffer := bytes.Buffer{} buf := make([]byte, len(p))
copy(buf, p)
buffer.Write(p)
buf := buffer.Bytes()
w.encryptor.XORKeyStream(buf, buf) w.encryptor.XORKeyStream(buf, buf)
return w.parent.WriteTimeout(buf, timeout) // nolint: wrapcheck return w.parent.WriteTimeout(buf, timeout)
} }
func (w *wrapperObfuscated2) Write(p []byte) (int, error) { func (w *wrapperObfuscated2) Write(p []byte) (int, error) {
buffer := bytes.Buffer{} buf := make([]byte, len(p))
copy(buf, p)
buffer.Write(p)
buf := buffer.Bytes()
w.encryptor.XORKeyStream(buf, buf) w.encryptor.XORKeyStream(buf, buf)
return w.parent.Write(buf) // nolint: wrapcheck return w.parent.Write(buf)
} }
func (w *wrapperObfuscated2) Conn() net.Conn { func (w *wrapperObfuscated2) Conn() net.Conn {
@@ -80,7 +72,7 @@ func (w *wrapperObfuscated2) RemoteAddr() *net.TCPAddr {
} }
func (w *wrapperObfuscated2) Close() error { func (w *wrapperObfuscated2) Close() error {
return w.parent.Close() // nolint: wrapcheck return w.parent.Close()
} }
func NewObfuscated2(socket conntypes.StreamReadWriteCloser, func NewObfuscated2(socket conntypes.StreamReadWriteCloser,
+35 -14
View File
@@ -7,8 +7,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
) )
type ReadWriteCloseRewinder interface { type ReadWriteCloseRewinder interface {
@@ -18,31 +19,55 @@ type ReadWriteCloseRewinder interface {
type wrapperRewind struct { type wrapperRewind struct {
parent conntypes.StreamReadWriteCloser parent conntypes.StreamReadWriteCloser
activeReader io.Reader
buf bytes.Buffer buf bytes.Buffer
mutex sync.Mutex mutex sync.Mutex
rewinded bool
} }
func (w *wrapperRewind) Write(p []byte) (int, error) { func (w *wrapperRewind) Write(p []byte) (int, error) {
return w.parent.Write(p) // nolint: wrapcheck return w.parent.Write(p)
} }
func (w *wrapperRewind) WriteTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperRewind) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
return w.parent.WriteTimeout(p, timeout) // nolint: wrapcheck return w.parent.WriteTimeout(p, timeout)
} }
func (w *wrapperRewind) Read(p []byte) (int, error) { func (w *wrapperRewind) Read(p []byte) (int, error) {
w.mutex.Lock() w.mutex.Lock()
defer w.mutex.Unlock() defer w.mutex.Unlock()
return w.activeReader.Read(p) // nolint: wrapcheck if w.rewinded {
if n, err := w.buf.Read(p); err != io.EOF {
return n, err
}
} }
func (w *wrapperRewind) ReadTimeout(p []byte, _ time.Duration) (int, error) { n, err := w.parent.Read(p)
if !w.rewinded {
w.buf.Write(p[:n])
}
return n, err
}
func (w *wrapperRewind) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
w.mutex.Lock() w.mutex.Lock()
defer w.mutex.Unlock() defer w.mutex.Unlock()
return w.activeReader.Read(p) // nolint: wrapcheck if w.rewinded {
if n, err := w.buf.Read(p); err != io.EOF {
return n, err
}
}
n, err := w.parent.ReadTimeout(p, timeout)
if !w.rewinded {
w.buf.Write(p[:n])
}
return n, err
} }
func (w *wrapperRewind) Conn() net.Conn { func (w *wrapperRewind) Conn() net.Conn {
@@ -63,21 +88,17 @@ func (w *wrapperRewind) RemoteAddr() *net.TCPAddr {
func (w *wrapperRewind) Close() error { func (w *wrapperRewind) Close() error {
w.buf.Reset() w.buf.Reset()
return w.parent.Close()
return w.parent.Close() // nolint: wrapcheck
} }
func (w *wrapperRewind) Rewind() { func (w *wrapperRewind) Rewind() {
w.mutex.Lock() w.mutex.Lock()
w.activeReader = io.MultiReader(&w.buf, w.parent) w.rewinded = true
w.mutex.Unlock() w.mutex.Unlock()
} }
func NewRewind(parent conntypes.StreamReadWriteCloser) ReadWriteCloseRewinder { func NewRewind(parent conntypes.StreamReadWriteCloser) ReadWriteCloseRewinder {
rv := &wrapperRewind{ return &wrapperRewind{
parent: parent, parent: parent,
} }
rv.activeReader = io.TeeReader(parent, &rv.buf)
return rv
} }
+8 -7
View File
@@ -5,9 +5,10 @@ import (
"sync" "sync"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/stats"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
"mtg/stats"
) )
type wrapperTelegramStats struct { type wrapperTelegramStats struct {
@@ -17,19 +18,19 @@ type wrapperTelegramStats struct {
} }
func (w *wrapperTelegramStats) Write(p []byte) (int, error) { func (w *wrapperTelegramStats) Write(p []byte) (int, error) {
return w.parent.Write(p) // nolint: wrapcheck return w.parent.Write(p)
} }
func (w *wrapperTelegramStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperTelegramStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
return w.parent.WriteTimeout(p, timeout) // nolint: wrapcheck return w.parent.WriteTimeout(p, timeout)
} }
func (w *wrapperTelegramStats) Read(p []byte) (int, error) { func (w *wrapperTelegramStats) Read(p []byte) (int, error) {
return w.parent.Read(p) // nolint: wrapcheck return w.parent.Read(p)
} }
func (w *wrapperTelegramStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperTelegramStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
return w.parent.ReadTimeout(p, timeout) // nolint: wrapcheck return w.parent.ReadTimeout(p, timeout)
} }
func (w *wrapperTelegramStats) Conn() net.Conn { func (w *wrapperTelegramStats) Conn() net.Conn {
@@ -56,7 +57,7 @@ func (w *wrapperTelegramStats) Close() error {
stats.Stats.TelegramDisconnected(w.dc, w.RemoteAddr()) stats.Stats.TelegramDisconnected(w.dc, w.RemoteAddr())
}) })
return err // nolint: wrapcheck return err
} }
func NewTelegramStats(dc conntypes.DC, parent conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser { func NewTelegramStats(dc conntypes.DC, parent conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser {
+8 -7
View File
@@ -4,9 +4,10 @@ import (
"net" "net"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/stats"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
"mtg/stats"
) )
type wrapperTrafficStats struct { type wrapperTrafficStats struct {
@@ -17,28 +18,28 @@ func (w *wrapperTrafficStats) Write(p []byte) (int, error) {
n, err := w.parent.Write(p) n, err := w.parent.Write(p)
stats.Stats.EgressTraffic(n) stats.Stats.EgressTraffic(n)
return n, err // nolint: wrapcheck return n, err
} }
func (w *wrapperTrafficStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperTrafficStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
n, err := w.parent.WriteTimeout(p, timeout) n, err := w.parent.WriteTimeout(p, timeout)
stats.Stats.EgressTraffic(n) stats.Stats.EgressTraffic(n)
return n, err // nolint: wrapcheck return n, err
} }
func (w *wrapperTrafficStats) Read(p []byte) (int, error) { func (w *wrapperTrafficStats) Read(p []byte) (int, error) {
n, err := w.parent.Read(p) n, err := w.parent.Read(p)
stats.Stats.IngressTraffic(n) stats.Stats.IngressTraffic(n)
return n, err // nolint: wrapcheck return n, err
} }
func (w *wrapperTrafficStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperTrafficStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
n, err := w.parent.ReadTimeout(p, timeout) n, err := w.parent.ReadTimeout(p, timeout)
stats.Stats.IngressTraffic(n) stats.Stats.IngressTraffic(n)
return n, err // nolint: wrapcheck return n, err
} }
func (w *wrapperTrafficStats) Conn() net.Conn { func (w *wrapperTrafficStats) Conn() net.Conn {
@@ -58,7 +59,7 @@ func (w *wrapperTrafficStats) RemoteAddr() *net.TCPAddr {
} }
func (w *wrapperTrafficStats) Close() error { func (w *wrapperTrafficStats) Close() error {
return w.parent.Close() // nolint: wrapcheck return w.parent.Close()
} }
func NewTrafficStats(parent conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser { func NewTrafficStats(parent conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser {
+7 -6
View File
@@ -4,8 +4,9 @@ import (
"net" "net"
"time" "time"
"github.com/9seconds/mtg/conntypes"
"go.uber.org/zap" "go.uber.org/zap"
"mtg/conntypes"
) )
const ( const (
@@ -18,23 +19,23 @@ type wrapperTimeout struct {
} }
func (w *wrapperTimeout) WriteTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperTimeout) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
return w.parent.WriteTimeout(p, timeout) // nolint: wrapcheck return w.parent.WriteTimeout(p, timeout)
} }
func (w *wrapperTimeout) Write(p []byte) (int, error) { func (w *wrapperTimeout) Write(p []byte) (int, error) {
return w.parent.WriteTimeout(p, timeoutWrite) // nolint: wrapcheck return w.parent.WriteTimeout(p, timeoutWrite)
} }
func (w *wrapperTimeout) ReadTimeout(p []byte, timeout time.Duration) (int, error) { func (w *wrapperTimeout) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
return w.parent.ReadTimeout(p, timeout) // nolint: wrapcheck return w.parent.ReadTimeout(p, timeout)
} }
func (w *wrapperTimeout) Read(p []byte) (int, error) { func (w *wrapperTimeout) Read(p []byte) (int, error) {
return w.parent.ReadTimeout(p, timeoutRead) // nolint: wrapcheck return w.parent.ReadTimeout(p, timeoutRead)
} }
func (w *wrapperTimeout) Close() error { func (w *wrapperTimeout) Close() error {
return w.parent.Close() // nolint: wrapcheck return w.parent.Close()
} }
func (w *wrapperTimeout) Conn() net.Conn { func (w *wrapperTimeout) Conn() net.Conn {