REPOSITORY / ScuroNeko/mtg

Compare commits

DIFF REPOSITORY

Compare commits

...
61 Commits
Author SHA1 Message Date
9seconds fee133a62f Merge remote-tracking branch 'origin/master' into stable 2021-12-03 09:28:40 +03:00
9seconds 7d38fec74e Update dependencies 2021-12-03 09:28:05 +03:00
9seconds 15bb5be6c4 Update go mod tidy command 2021-12-03 09:27:58 +03:00
9seconds ad8c09a2a3 Fix gofumpt cli 2021-12-03 09:26:31 +03:00
Sergey ArkhipovandGitHub 2f626e2138 Merge pull request #234 from 9seconds/cant-send-attachment 2021-12-03 09:31:54 +04:00
9seconds 93bed24a0b Remove all cleverness that broke uploads 2021-12-03 07:39:45 +03:00
9seconds 219235e181 Merge remote-tracking branch 'origin/stable' 2021-12-02 07:29:59 +03:00
9seconds 79f54a4e67 Merge remote-tracking branch 'origin/master' into stable 2021-12-02 07:08:30 +03:00
9seconds 4b78e83be7 Update dependencies 2021-12-02 06:55:14 +03:00
Sergey ArkhipovandGitHub ad30eca406 Merge pull request #232 from 9seconds/golangcilint-1.43.0
Golangcilint 1.43.0
2021-12-02 07:53:40 +04:00
9seconds ecf947b86c Ignore noisy linters 2021-12-02 06:51:09 +03:00
9seconds 5be581154e Update golangci-lint to 1.43.0 2021-12-02 06:03:45 +03:00
9seconds bc3b517e8b Remove redundant setting of TCP keepalive since it is default 2021-12-02 05:47:45 +03:00
Sergey ArkhipovandGitHub 3f8f96b91f Merge pull request #230 from 9seconds/simplify-sockopts
Simplify sockopts
2021-12-01 16:03:52 +04:00
9seconds ef55fbba15 Add documentation for essentials 2021-12-01 14:56:39 +03:00
9seconds e7416bc04d Fix lint issues 2021-12-01 11:07:32 +03:00
9seconds 9a6264a89f Tidy deps 2021-12-01 10:52:08 +03:00
9seconds 33e0509c5a Optimize for a fast flush 2021-12-01 10:51:13 +03:00
9seconds ffad717829 Use CloseRead and CloseWrites 2021-12-01 10:37:31 +03:00
9seconds 7b1f86b75d wip 2021-11-30 15:19:37 +03:00
9seconds a5e59d9ef7 Add syncPair 2021-11-30 14:37:25 +03:00
Sergey ArkhipovandGitHub 16c06f247c Merge pull request #231 from 9seconds/whitelists
Whitelist support
2021-11-29 18:26:39 +04:00
9seconds 0ddaabb136 Add whitelist support 2021-11-29 17:02:53 +03:00
9seconds 558fec60de Refactor firehol 2021-11-29 16:25:33 +03:00
9seconds cc101c9a47 Rename rwMutex to updateMutex 2021-11-29 15:58:14 +03:00
9seconds e6fa69d288 Add tests for HTTP file abstraction 2021-11-29 15:56:53 +03:00
9seconds c14a2329c5 Add local file abstraction 2021-11-29 07:26:27 +03:00
9seconds 4c75066ef8 Update configuration 2021-11-29 05:35:37 +03:00
9seconds ce8163d1b7 Minor simplification 2021-11-28 18:45:24 +03:00
9seconds ca77157fd5 Deprecate and ignore simple-run setting for tcp-buffer 2021-11-28 18:15:05 +03:00
9seconds 66f4d967e7 Get rid of buffersize everywhere 2021-11-28 18:08:40 +03:00
9seconds d19cfb1df4 Deprecate bufferSize 2021-11-28 17:58:14 +03:00
Sergey ArkhipovandGitHub 3540408adf Merge pull request #224 from dariubs/patch-1 2021-11-04 22:40:14 +03:00
Dariush AbbasiandGitHub 7917434a37 fix git clone url
replace : with /
2021-11-03 14:09:18 +03:30
Sergey ArkhipovandGitHub 853395106b Merge pull request #223 from boypt/fixdohv6
fix ipv6 doh-ip
2021-11-02 18:17:42 +03:00
Bot Gitandboypt d1b0d1f133 fix ipv6 doh-ip 2021-11-02 20:56:58 +08:00
Sergey ArkhipovandGitHub 73bd7287f0 Merge pull request #221 from boypt/fixtimeskew 2021-10-31 21:07:31 +03:00
boypt 31b8ab4482 fix TolerateTimeSkewness not being passed 2021-10-30 22:06:51 +08:00
9seconds bae5407372 Merge remote-tracking branch 'origin/stable' 2021-10-05 12:09:05 +03:00
9seconds 4814b0fcc1 Merge remote-tracking branch 'origin/master' into stable 2021-10-05 11:33:57 +03:00
9seconds 3ce549bb16 Update dependencies 2021-10-05 11:16:57 +03:00
Sergey ArkhipovandGitHub 5c636a68dc Merge pull request #217 from 9seconds/golangcilint-1.42.1
golangci-lint 1.42.1
2021-10-05 11:10:08 +03:00
9seconds 9f6f906786 Add go:build comments 2021-10-05 10:51:41 +03:00
9seconds eb32766c1f Update to golangci-lint 1.42.1 2021-10-05 10:51:15 +03:00
9seconds 787d72cf52 Use install, not go get 2021-10-05 10:50:58 +03:00
Sergey ArkhipovandGitHub ce8f1ebb6c Merge pull request #216 from 9seconds/configure-fallback-dc
Add configuration option allow-fallback-on-unknown-dc
2021-10-04 15:09:47 +03:00
9seconds cd29f3e20b Add configuration option allow-fallback-on-unknown-dc 2021-10-04 14:42:26 +03:00
Sergey ArkhipovandGitHub 3e105f2beb Merge pull request #215 from 9seconds/9seconds-patch-1
Update Actions permissions
2021-10-04 14:31:58 +03:00
Sergey ArkhipovandGitHub 6bbdd99e7f Update codeql-analysis.yml 2021-10-04 14:18:50 +03:00
Sergey Arkhipovand9seconds 894c68019e Update permissions in files 2021-10-04 14:16:29 +03:00
Sergey ArkhipovandGitHub 98f18fc22b Merge pull request #214 from 9seconds/log-json
Add new BindJSON method to a logger
2021-10-04 12:22:43 +03:00
Sergey ArkhipovandGitHub 01b739aa7c Merge pull request #213 from 9seconds/fix-windows-build
Fix windows build
2021-10-04 10:20:13 +03:00
9seconds dbaa743e03 Add new BindJSON method to a logger 2021-10-04 10:10:14 +03:00
9seconds d1e5f9d145 Fix windows build 2021-10-04 09:46:49 +03:00
9seconds 686f177ab9 Correct error for non-getting of underlying connection 2021-10-04 09:34:10 +03:00
Sergey ArkhipovandGitHub 706aef8ca1 Merge pull request #211 from 9seconds/docker-build-timelimit 2021-09-24 22:00:06 +03:00
9seconds 14dfb9506a Bump docker build timeout to 20 minutes 2021-09-24 16:59:11 +03:00
Sergey ArkhipovandGitHub f742066c54 Merge pull request #210 from 9seconds/fallback-to-random-dc
Fallback to another DC if given is unknown
2021-09-24 12:18:05 +03:00
9seconds fbe4d32590 Fallback to another DC if given is unknown 2021-09-24 11:47:35 +03:00
9seconds d0f18be91d Merge remote-tracking branch 'origin/stable' 2021-08-30 15:50:27 +03:00
9seconds 929b73e2eb Merge remote-tracking branch 'origin/master' into stable 2021-08-30 15:50:15 +03:00
71 changed files with 1030 additions and 562 deletions
+14 -2
View File
@@ -2,6 +2,18 @@
name: CI
permissions:
actions: read
checks: read
contents: read
deployments: read
issues: read
discussions: read
pull-requests: read
repository-projects: read
security-events: read
statuses: read
on:
push:
tags:
@@ -71,12 +83,12 @@ jobs:
- name: Run linter
uses: golangci/golangci-lint-action@v2
with:
version: v1.42.0
version: v1.43.0
docker:
name: Docker
runs-on: ubuntu-latest
timeout-minutes: 15
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v2
+8
View File
@@ -11,6 +11,14 @@
#
name: "CodeQL"
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#permissions
# https://github.com/github/codeql-action/issues/572
permissions:
actions: read
contents: read
pull-requests: read
security-events: write
on:
push:
branches:
+1 -1
View File
@@ -9,4 +9,4 @@ format = "colored-line-number"
[linters]
enable-all = true
disable = ["gochecknoglobals", "gas", "goerr113", "exhaustivestruct"]
disable = ["ireturn", "varnamelen", "gochecknoglobals", "gas", "goerr113", "exhaustivestruct"]
+6 -6
View File
@@ -2,7 +2,7 @@ ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
IMAGE_NAME := mtg
APP_NAME := $(IMAGE_NAME)
GOLANGCI_LINT_VERSION := v1.42.0
GOLANGCI_LINT_VERSION := v1.43.0
VERSION_GO := $(shell go version)
VERSION_DATE := $(shell date -Ru)
@@ -39,7 +39,7 @@ vendor: go.mod go.sum
.PHONY: fmt
fmt:
@$(GOTOOL) gofumpt -w -s -extra "$(ROOT_DIR)"
@$(GOTOOL) gofumpt -w -extra "$(ROOT_DIR)"
.PHONY: test
test:
@@ -83,16 +83,16 @@ install-tools-lint: .bin
.PHONY: install-tools-godoc
install-tools-godoc: .bin
@$(GOTOOL) go get -u golang.org/x/tools/cmd/godoc
@$(GOTOOL) go install golang.org/x/tools/cmd/godoc@latest
.PHONY: install-tools-gofumpt
install-tools-gofumpt: .bin
@$(GOTOOL) go get -u mvdan.cc/gofumpt
@$(GOTOOL) go install mvdan.cc/gofumpt@latest
.PHONY: goreleaser
install-tools-goreleaser: .bin
@$(GOTOOL) go get -u github.com/goreleaser/goreleaser
@$(GOTOOL) go install github.com/goreleaser/goreleaser@latest
.PHONY: update-deps
update-deps:
@go get -u && go mod tidy
@go get -u && go mod tidy -go=1.17
+1 -1
View File
@@ -196,7 +196,7 @@ go get github.com/9seconds/mtg/v2
#### Build from sources
```console
git clone https://github.com:9seconds/mtg.git
git clone https://github.com/9seconds/mtg.git
cd mtg
make static
```
+26
View File
@@ -0,0 +1,26 @@
package essentials
import (
"io"
"net"
)
// CloseableReader is a reader interface that can close its reading end.
type CloseableReader interface {
io.Reader
CloseRead() error
}
// CloseableWriter is a writer that can close its writing end.
type CloseableWriter interface {
io.Writer
CloseWrite() error
}
// Conn is an extension of net.Conn that can close its ends. This mostly
// implies TCP connections.
type Conn interface {
net.Conn
CloseableReader
CloseableWriter
}
+6
View File
@@ -0,0 +1,6 @@
// This is a minimal package that contains _essentials_ of mtglib and its
// complimentary packages. This is mostly required to comply some interfaces
// between mtglib and its internals to avoid circular dependencies.
//
// This package should contain only bare minimum and mostly technical.
package essentials
+37 -1
View File
@@ -30,7 +30,9 @@ concurrency = 8192
# A size of user-space buffer for TCP to use. Since we do 2 connections,
# then we have tcp-buffer * (4 + 2) per each connection: read/write for
# each connection + 2 copy buffers to pump the data between sockets.
tcp-buffer = "4kb"
#
# Deprecated: this setting is no longer makes any effect.
# tcp-buffer = "4kb"
# Sometimes you want to enforce mtg to use some types of
# IP connectivity to Telegram. We have 4 modes:
@@ -56,6 +58,19 @@ domain-fronting-port = 443
# time range of this parameter.
tolerate-time-skewness = "5s"
# Telegram has a concept of DC. You can think about DC as a number of a cluster
# with a certain purpose. Some clusters serve media, some - messages, some rule
# channels and so on. But sometimes unknown DC number is requested by client.
# It could be a bug or some global reconfiguration of the Telegram.
#
# By default, proxy rejects such requests. But it is also possible to fallback
# this request to any DC. Telegram works in a way that any DC is able to serve
# any request but sacrificing a latency.
#
# If this setting is disabled (default), mtg will reject a connection.
# Otherwise, chose a new DC.
allow-fallback-on-unknown-dc = false
# network defines different network-related settings
[network]
# please be aware that mtg needs to do some external requests. For
@@ -161,6 +176,27 @@ urls = [
# How often do we need to update a blocklist set.
update-each = "24h"
# Allowlist is an opposite to a blocklist. Only those IPs that are coming from
# subnets defined in these lists are allowed. All others will be rejected.
#
# If this feature is disabled, then there won't be any check performed by this
# validator. It is possible to combine both blocklist and whitelist.
[defense.allowlist]
# You can enable/disable this feature.
enabled = false
# This is a limiter for concurrency. In order to protect website
# from overloading, we download files in this number of threads.
download-concurrency = 2
# A list of URLs in FireHOL format (https://iplists.firehol.org/)
# You can provider links here (starts with https:// or http://) or
# path to a local file, but in this case it should be absolute.
urls = [
# "https://iplists.firehol.org/files/firehol_level1.netset",
# "/local.file"
]
update-each = "24h"
# statsd statistics integration.
[stats.statsd]
# enabled/disabled
+15 -10
View File
@@ -4,32 +4,34 @@ go 1.17
require (
github.com/OneOfOne/xxhash v1.2.8
github.com/alecthomas/kong v0.2.17
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15
github.com/alecthomas/kong v0.2.19
github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
github.com/d4l3k/messagediff v1.2.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gotd/td v0.34.0
github.com/jarcoal/httpmock v1.0.8
github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1
github.com/kentik/patricia v0.0.0-20210909164817-21603333b70e
github.com/mccutchen/go-httpbin v1.1.1
github.com/panjf2000/ants/v2 v2.4.6
github.com/pelletier/go-toml v1.9.3
github.com/panjf2000/ants/v2 v2.4.7
github.com/pelletier/go-toml v1.9.4
github.com/prometheus/client_golang v1.11.0
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/rs/zerolog v1.24.0
github.com/rs/zerolog v1.26.0
github.com/smira/go-statsd v1.3.2
github.com/stretchr/objx v0.3.0 // indirect
github.com/stretchr/testify v1.7.0
github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881
google.golang.org/protobuf v1.27.1 // indirect
)
require github.com/txthinking/socks5 v0.0.0-20211121111206-e03c1217a50b
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
@@ -38,9 +40,12 @@ require (
github.com/gotd/ige v0.1.5 // indirect
github.com/gotd/xor v0.1.1 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0 // indirect
+33 -27
View File
@@ -37,15 +37,15 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/alecthomas/kong v0.2.17 h1:URDISCI96MIgcIlQyoCAlhOmrSw6pZScBNkctg8r0W0=
github.com/alecthomas/kong v0.2.17/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ=
github.com/alecthomas/kong v0.2.19 h1:qBDfByO5XgWUXyNB4D6OOhGh5Z1eNOwWayDPQJFNWdc=
github.com/alecthomas/kong v0.2.19/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/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/alecthomas/units v0.0.0-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc=
github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -169,8 +169,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/pp v2.4.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1 h1:D7qhJP3R49ZjUzpzKQ6B2H3lgejPs6DTO5gRomhhOpE=
github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1/go.mod h1:2OfLA+0esiUJpwMjrH39pEk79cb8MvGTBS9YlZpejJ4=
github.com/kentik/patricia v0.0.0-20210909164817-21603333b70e h1:1wAVuGu1c+lsdaOPQN+9xoP9+gaIMJV6H0ehGc+K5iA=
github.com/kentik/patricia v0.0.0-20210909164817-21603333b70e/go.mod h1:2OfLA+0esiUJpwMjrH39pEk79cb8MvGTBS9YlZpejJ4=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -193,10 +193,12 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
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-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/panjf2000/ants/v2 v2.4.6 h1:drmj9mcygn2gawZ155dRbo+NfXEfAssjZNU1qoIb4gQ=
github.com/panjf2000/ants/v2 v2.4.6/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/panjf2000/ants/v2 v2.4.7 h1:MZnw2JRyTJxFwtaMtUJcwE618wKD04POWk2gwwP4E2M=
github.com/panjf2000/ants/v2 v2.4.7/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -217,8 +219,8 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug=
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -229,8 +231,8 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.2/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQP
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.24.0 h1:76ivFxmVSRs1u2wUwJVg5VZDYQgeH1JpoS6ndgr9Wy8=
github.com/rs/zerolog v1.24.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI=
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@@ -250,12 +252,18 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0=
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM=
github.com/txthinking/socks5 v0.0.0-20211121111206-e03c1217a50b h1:6J/38A0Xmdnjacfie0Udams7OP/GdoExyTipKwuQWjY=
github.com/txthinking/socks5 v0.0.0-20211121111206-e03c1217a50b/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg=
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A=
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4=
github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43 h1:QEePdg0ty2r0t1+qwfZmQ4OOl/MB2UXIeJSpIZv56lg=
github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -277,8 +285,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8=
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -342,11 +350,10 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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=
@@ -400,13 +407,12 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
@@ -460,8 +466,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
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=
+2 -1
View File
@@ -13,6 +13,7 @@ import (
"strings"
"sync"
"github.com/9seconds/mtg/v2/essentials"
"github.com/9seconds/mtg/v2/internal/config"
"github.com/9seconds/mtg/v2/internal/utils"
"github.com/9seconds/mtg/v2/mtglib"
@@ -106,7 +107,7 @@ func (a *Access) Run(cli *CLI, version string) error {
}
func (a *Access) getIP(ntw mtglib.Network, protocol string) net.IP {
client := ntw.MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error) {
client := ntw.MakeHTTPClient(func(ctx context.Context, network, address string) (essentials.Conn, error) {
return ntw.DialContext(ctx, protocol, address) // nolint: wrapcheck
})
+24 -11
View File
@@ -38,10 +38,9 @@ func makeNetwork(conf *config.Config, version string) (mtglib.Network, error) {
tcpTimeout := conf.Network.Timeout.TCP.Get(network.DefaultTimeout)
httpTimeout := conf.Network.Timeout.HTTP.Get(network.DefaultHTTPTimeout)
dohIP := conf.Network.DOHIP.Get(net.ParseIP(network.DefaultDOHHostname)).String()
bufferSize := conf.TCPBuffer.Get(network.DefaultBufferSize)
userAgent := "mtg/" + version
baseDialer, err := network.NewDefaultDialer(tcpTimeout, int(bufferSize))
baseDialer, err := network.NewDefaultDialer(tcpTimeout, 0)
if err != nil {
return nil, fmt.Errorf("cannot build a default dialer: %w", err)
}
@@ -86,15 +85,15 @@ func makeAntiReplayCache(conf *config.Config) mtglib.AntiReplayCache {
)
}
func makeIPBlocklist(conf *config.Config, logger mtglib.Logger, ntw mtglib.Network) (mtglib.IPBlocklist, error) {
if !conf.Defense.Blocklist.Enabled.Get(false) {
func makeIPBlocklist(conf config.ListConfig, logger mtglib.Logger, ntw mtglib.Network) (mtglib.IPBlocklist, error) {
if !conf.Enabled.Get(false) {
return ipblocklist.NewNoop(), nil
}
remoteURLs := []string{}
localFiles := []string{}
for _, v := range conf.Defense.Blocklist.URLs {
for _, v := range conf.URLs {
if v.IsRemote() {
remoteURLs = append(remoteURLs, v.String())
} else {
@@ -104,7 +103,7 @@ func makeIPBlocklist(conf *config.Config, logger mtglib.Logger, ntw mtglib.Netwo
firehol, err := ipblocklist.NewFirehol(logger.Named("ipblockist"),
ntw,
conf.Defense.Blocklist.DownloadConcurrency.Get(1),
conf.DownloadConcurrency.Get(1),
remoteURLs,
localFiles)
if err != nil {
@@ -153,21 +152,32 @@ func makeEventStream(conf *config.Config, logger mtglib.Logger) (mtglib.EventStr
return events.NewNoopStream(), nil
}
func runProxy(conf *config.Config, version string) error {
func runProxy(conf *config.Config, version string) error { // nolint: funlen
logger := makeLogger(conf)
logger.BindStr("configuration", conf.String()).Debug("configuration")
logger.BindJSON("configuration", conf.String()).Debug("configuration")
ntw, err := makeNetwork(conf, version)
if err != nil {
return fmt.Errorf("cannot build network: %w", err)
}
blocklist, err := makeIPBlocklist(conf, logger, ntw)
blocklist, err := makeIPBlocklist(conf.Defense.Blocklist, logger, ntw)
if err != nil {
return fmt.Errorf("cannot build ip blocklist: %w", err)
}
var whitelist mtglib.IPBlocklist
if conf.Defense.Allowlist.Enabled.Get(false) {
whlist, err := makeIPBlocklist(conf.Defense.Allowlist, logger, ntw)
if err != nil {
return fmt.Errorf("cannot build ip blocklist: %w", err)
}
whitelist = whlist
}
eventStream, err := makeEventStream(conf, logger)
if err != nil {
return fmt.Errorf("cannot build event stream: %w", err)
@@ -178,12 +188,15 @@ func runProxy(conf *config.Config, version string) error {
Network: ntw,
AntiReplayCache: makeAntiReplayCache(conf),
IPBlocklist: blocklist,
IPWhitelist: whitelist,
EventStream: eventStream,
Secret: conf.Secret,
BufferSize: conf.TCPBuffer.Get(mtglib.DefaultBufferSize),
DomainFrontingPort: conf.DomainFrontingPort.Get(mtglib.DefaultDomainFrontingPort),
PreferIP: conf.PreferIP.Get(mtglib.DefaultPreferIP),
AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
TolerateTimeSkewness: conf.TolerateTimeSkewness.Value,
}
proxy, err := mtglib.NewProxy(opts)
@@ -191,7 +204,7 @@ func runProxy(conf *config.Config, version string) error {
return fmt.Errorf("cannot create a proxy: %w", err)
}
listener, err := utils.NewListener(conf.BindTo.Get(""), int(opts.BufferSize))
listener, err := utils.NewListener(conf.BindTo.Get(""), 0)
if err != nil {
return fmt.Errorf("cannot start proxy: %w", err)
}
+2 -5
View File
@@ -15,7 +15,7 @@ type SimpleRun struct {
Debug bool `kong:"name='debug',short='d',help='Run in debug mode.'"` // nolint: lll
Concurrency uint64 `kong:"name='concurrency',short='c',default='8192',help='Max number of concurrent connection to proxy.'"` // nolint: lll
TCPBuffer string `kong:"name='tcp-buffer',short='b',default='4KB',help='Size of TCP buffer to use.'"` // nolint: lll
TCPBuffer string `kong:"name='tcp-buffer',short='b',default='4KB',help='Deprecated and ignored'"` // nolint: lll
PreferIP string `kong:"name='prefer-ip',short='i',default='prefer-ipv6',help='IP preference. By default we prefer IPv6 with fallback to IPv4.'"` // nolint: lll
DomainFrontingPort uint64 `kong:"name='domain-fronting-port',short='p',default='443',help='A port to access for domain fronting.'"` // nolint: lll
DOHIP net.IP `kong:"name='doh-ip',short='n',default='9.9.9.9',help='IP address of DNS-over-HTTP to use.'"` // nolint: lll
@@ -38,10 +38,6 @@ func (s *SimpleRun) Run(cli *CLI, version string) error { // nolint: cyclop
return fmt.Errorf("incorrect concurrency: %w", err)
}
if err := conf.TCPBuffer.Set(s.TCPBuffer); err != nil {
return fmt.Errorf("incorrect tcp-buffer: %w", err)
}
if err := conf.PreferIP.Set(s.PreferIP); err != nil {
return fmt.Errorf("incorrect prefer-ip: %w", err)
}
@@ -71,6 +67,7 @@ func (s *SimpleRun) Run(cli *CLI, version string) error { // nolint: cyclop
}
conf.Debug.Value = s.Debug
conf.AllowFallbackOnUnknownDC.Value = true
conf.Defense.AntiReplay.Enabled.Value = true
if err := conf.Validate(); err != nil {
+29 -18
View File
@@ -8,27 +8,36 @@ import (
"github.com/9seconds/mtg/v2/mtglib"
)
type Optional struct {
Enabled TypeBool `json:"enabled"`
}
type ListConfig struct {
Optional
DownloadConcurrency TypeConcurrency `json:"downloadConcurrency"`
URLs []TypeBlocklistURI `json:"urls"`
UpdateEach TypeDuration `json:"updateEach"`
}
type Config struct {
Debug TypeBool `json:"debug"`
Secret mtglib.Secret `json:"secret"`
BindTo TypeHostPort `json:"bindTo"`
TCPBuffer TypeBytes `json:"tcpBuffer"`
PreferIP TypePreferIP `json:"preferIp"`
DomainFrontingPort TypePort `json:"domainFrontingPort"`
TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"`
Concurrency TypeConcurrency `json:"concurrency"`
Defense struct {
Debug TypeBool `json:"debug"`
AllowFallbackOnUnknownDC TypeBool `json:"allowFallbackOnUnknownDc"`
Secret mtglib.Secret `json:"secret"`
BindTo TypeHostPort `json:"bindTo"`
PreferIP TypePreferIP `json:"preferIp"`
DomainFrontingPort TypePort `json:"domainFrontingPort"`
TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"`
Concurrency TypeConcurrency `json:"concurrency"`
Defense struct {
AntiReplay struct {
Enabled TypeBool `json:"enabled"`
Optional
MaxSize TypeBytes `json:"maxSize"`
ErrorRate TypeErrorRate `json:"errorRate"`
} `json:"antiReplay"`
Blocklist struct {
Enabled TypeBool `json:"enabled"`
DownloadConcurrency TypeConcurrency `json:"downloadConcurrency"`
URLs []TypeBlocklistURI `json:"urls"`
UpdateEach TypeDuration `json:"updateEach"`
} `json:"blocklist"`
Blocklist ListConfig `json:"blocklist"`
Allowlist ListConfig `json:"allowlist"`
} `json:"defense"`
Network struct {
Timeout struct {
@@ -41,13 +50,15 @@ type Config struct {
} `json:"network"`
Stats struct {
StatsD struct {
Enabled TypeBool `json:"enabled"`
Optional
Address TypeHostPort `json:"address"`
MetricPrefix TypeMetricPrefix `json:"metricPrefix"`
TagFormat TypeStatsdTagFormat `json:"tagFormat"`
} `json:"statsd"`
Prometheus struct {
Enabled TypeBool `json:"enabled"`
Optional
BindTo TypeHostPort `json:"bindTo"`
HTTPPath TypeHTTPPath `json:"httpPath"`
MetricPrefix TypeMetricPrefix `json:"metricPrefix"`
+15 -9
View File
@@ -9,15 +9,15 @@ import (
)
type tomlConfig struct {
Debug bool `toml:"debug" json:"debug,omitempty"`
Secret string `toml:"secret" json:"secret"`
BindTo string `toml:"bind-to" json:"bindTo"`
TCPBuffer string `toml:"tcp-buffer" json:"tcpBuffer,omitempty"`
PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"`
DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"`
TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"`
Concurrency uint `toml:"concurrency" json:"concurrency,omitempty"`
Defense struct {
Debug bool `toml:"debug" json:"debug,omitempty"`
AllowFallbackOnUnknownDC bool `toml:"allow-fallback-on-unknown-dc" json:"allowFallbackOnUnknownDc,omitempty"`
Secret string `toml:"secret" json:"secret"`
BindTo string `toml:"bind-to" json:"bindTo"`
PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"`
DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"`
TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"`
Concurrency uint `toml:"concurrency" json:"concurrency,omitempty"`
Defense struct {
AntiReplay struct {
Enabled bool `toml:"enabled" json:"enabled,omitempty"`
MaxSize string `toml:"max-size" json:"maxSize,omitempty"`
@@ -29,6 +29,12 @@ type tomlConfig struct {
URLs []string `toml:"urls" json:"urls,omitempty"`
UpdateEach string `toml:"update-each" json:"updateEach,omitempty"`
} `toml:"blocklist" json:"blocklist,omitempty"`
Allowlist struct {
Enabled bool `toml:"enabled" json:"enabled,omitempty"`
DownloadConcurrency uint `toml:"download-concurrency" json:"downloadConcurrency,omitempty"`
URLs []string `toml:"urls" json:"urls,omitempty"`
UpdateEach string `toml:"update-each" json:"updateEach,omitempty"`
} `toml:"allowlist" json:"allowlist,omitempty"`
} `toml:"defense" json:"defense,omitempty"`
Network struct {
Timeout struct {
+6 -6
View File
@@ -2,9 +2,9 @@ package testlib
import (
"context"
"net"
"net/http"
"github.com/9seconds/mtg/v2/essentials"
"github.com/stretchr/testify/mock"
)
@@ -12,19 +12,19 @@ type MtglibNetworkMock struct {
mock.Mock
}
func (m *MtglibNetworkMock) Dial(network, address string) (net.Conn, error) {
func (m *MtglibNetworkMock) Dial(network, address string) (essentials.Conn, error) {
args := m.Called(network, address)
return args.Get(0).(net.Conn), args.Error(1) // nolint: wrapcheck
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
}
func (m *MtglibNetworkMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
func (m *MtglibNetworkMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
args := m.Called(ctx, network, address)
return args.Get(0).(net.Conn), args.Error(1) // nolint: wrapcheck
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
}
func (m *MtglibNetworkMock) MakeHTTPClient(dialFunc func(ctx context.Context,
network, address string) (net.Conn, error)) *http.Client {
network, address string) (essentials.Conn, error)) *http.Client {
return m.Called(dialFunc).Get(0).(*http.Client)
}
+17 -9
View File
@@ -7,42 +7,50 @@ import (
"github.com/stretchr/testify/mock"
)
type NetConnMock struct {
type EssentialsConnMock struct {
mock.Mock
}
func (n *NetConnMock) Read(b []byte) (int, error) {
func (n *EssentialsConnMock) Read(b []byte) (int, error) {
args := n.Called(b)
return args.Int(0), args.Error(1)
}
func (n *NetConnMock) Write(b []byte) (int, error) {
func (n *EssentialsConnMock) Write(b []byte) (int, error) {
args := n.Called(b)
return args.Int(0), args.Error(1)
}
func (n *NetConnMock) Close() error {
func (n *EssentialsConnMock) Close() error {
return n.Called().Error(0) // nolint: wrapcheck
}
func (n *NetConnMock) LocalAddr() net.Addr {
func (n *EssentialsConnMock) CloseRead() error {
return n.Called().Error(0) // nolint: wrapcheck
}
func (n *EssentialsConnMock) CloseWrite() error {
return n.Called().Error(0) // nolint: wrapcheck
}
func (n *EssentialsConnMock) LocalAddr() net.Addr {
return n.Called().Get(0).(net.Addr)
}
func (n *NetConnMock) RemoteAddr() net.Addr {
func (n *EssentialsConnMock) RemoteAddr() net.Addr {
return n.Called().Get(0).(net.Addr)
}
func (n *NetConnMock) SetDeadline(t time.Time) error {
func (n *EssentialsConnMock) SetDeadline(t time.Time) error {
return n.Called(t).Error(0) // nolint: wrapcheck
}
func (n *NetConnMock) SetReadDeadline(t time.Time) error {
func (n *EssentialsConnMock) SetReadDeadline(t time.Time) error {
return n.Called(t).Error(0) // nolint: wrapcheck
}
func (n *NetConnMock) SetWriteDeadline(t time.Time) error {
func (n *EssentialsConnMock) SetWriteDeadline(t time.Time) error {
return n.Called(t).Error(0) // nolint: wrapcheck
}
+2 -5
View File
@@ -9,8 +9,6 @@ import (
type Listener struct {
net.Listener
bufferSize int
}
func (l Listener) Accept() (net.Conn, error) {
@@ -19,7 +17,7 @@ func (l Listener) Accept() (net.Conn, error) {
return nil, err // nolint: wrapcheck
}
if err := network.SetClientSocketOptions(conn, l.bufferSize); err != nil {
if err := network.SetClientSocketOptions(conn, 0); err != nil {
conn.Close()
return nil, fmt.Errorf("cannot set TCP options: %w", err)
@@ -35,7 +33,6 @@ func NewListener(bindTo string, bufferSize int) (net.Listener, error) {
}
return Listener{
Listener: base,
bufferSize: bufferSize,
Listener: base,
}, nil
}
+1
View File
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package utils
+1
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package utils
+1
View File
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package utils
+1
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package utils
+63
View File
@@ -0,0 +1,63 @@
package files
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
)
type httpFile struct {
http *http.Client
url string
}
func (h httpFile) Open(ctx context.Context) (io.ReadCloser, error) {
request, err := http.NewRequestWithContext(ctx, http.MethodGet, h.url, nil)
if err != nil {
panic(err)
}
response, err := h.http.Do(request)
if err != nil {
if response != nil {
io.Copy(io.Discard, response.Body) // nolint: errcheck
response.Body.Close()
}
return nil, fmt.Errorf("cannot get url %s: %w", h.url, err)
}
if response.StatusCode >= http.StatusBadRequest {
return nil, fmt.Errorf("unexpected status code %d", response.StatusCode)
}
return response.Body, nil
}
func (h httpFile) String() string {
return h.url
}
func NewHTTP(client *http.Client, endpoint string) (File, error) {
if client == nil {
return nil, ErrBadHTTPClient
}
parsed, err := url.Parse(endpoint)
if err != nil {
return nil, fmt.Errorf("incorrect url %s: %w", endpoint, err)
}
switch parsed.Scheme {
case "http", "https":
default:
return nil, fmt.Errorf("unsupported url %s", endpoint)
}
return httpFile{
http: client,
url: endpoint,
}, nil
}
+90
View File
@@ -0,0 +1,90 @@
package files_test
import (
"context"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/9seconds/mtg/v2/ipblocklist/files"
"github.com/stretchr/testify/suite"
)
type HTTPTestSuite struct {
suite.Suite
httpClient *http.Client
httpServer *httptest.Server
ctx context.Context
ctxCancel context.CancelFunc
}
func (suite *HTTPTestSuite) makeFile(path string) (files.File, error) {
return files.NewHTTP(suite.httpClient, suite.httpServer.URL+"/"+path) // nolint: wrapcheck
}
func (suite *HTTPTestSuite) SetupSuite() {
mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir("testdata")))
suite.httpServer = httptest.NewServer(mux)
suite.httpClient = suite.httpServer.Client()
}
func (suite *HTTPTestSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithCancel(context.Background())
}
func (suite *HTTPTestSuite) TearDownTest() {
suite.ctxCancel()
suite.httpServer.CloseClientConnections()
}
func (suite *HTTPTestSuite) TearDownSuite() {
suite.httpServer.Close()
}
func (suite *HTTPTestSuite) TestBadURL() {
_, err := files.NewHTTP(suite.httpClient, "sdfsdf")
suite.Error(err)
}
func (suite *HTTPTestSuite) TestBadSchema() {
_, err := files.NewHTTP(suite.httpClient, "gopher://lala")
suite.Error(err)
}
func (suite *HTTPTestSuite) TestNilHTTPClient() {
_, err := files.NewHTTP(nil, "")
suite.Error(err)
}
func (suite *HTTPTestSuite) TestAbsentFile() {
file, err := suite.makeFile("absent")
suite.NoError(err)
_, err = file.Open(suite.ctx)
suite.Error(err)
}
func (suite *HTTPTestSuite) TestOk() {
file, err := suite.makeFile("readable")
suite.NoError(err)
readCloser, err := file.Open(suite.ctx)
suite.NoError(err)
defer readCloser.Close()
data, err := io.ReadAll(readCloser)
suite.NoError(err)
suite.Equal("Hooray!", strings.TrimSpace(string(data)))
}
func TestHTTP(t *testing.T) {
t.Parallel()
suite.Run(t, &HTTPTestSuite{})
}
+14
View File
@@ -0,0 +1,14 @@
package files
import (
"context"
"errors"
"io"
)
var ErrBadHTTPClient = errors.New("incorrect http client")
type File interface {
Open(context.Context) (io.ReadCloser, error)
String() string
}
+30
View File
@@ -0,0 +1,30 @@
package files
import (
"context"
"fmt"
"io"
"os"
)
type localFile struct {
path string
}
func (l localFile) Open(ctx context.Context) (io.ReadCloser, error) {
return os.Open(l.path) // nolint: wrapcheck
}
func (l localFile) String() string {
return l.path
}
func NewLocal(path string) (File, error) {
if stat, err := os.Stat(path); os.IsNotExist(err) || stat.IsDir() || stat.Mode().Perm()&0o400 == 0 {
return nil, fmt.Errorf("%s is not a readable file", path)
}
return localFile{
path: path,
}, nil
}
+55
View File
@@ -0,0 +1,55 @@
package files_test
import (
"context"
"io"
"path/filepath"
"strings"
"testing"
"github.com/9seconds/mtg/v2/ipblocklist/files"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type LocalTestSuite struct {
suite.Suite
}
func (suite *LocalTestSuite) getLocalFile(name string) string {
return filepath.Join("testdata", name)
}
func (suite *LocalTestSuite) TestIncorrect() {
names := []string{
"absent",
"directory",
}
for _, v := range names {
value := v
suite.T().Run(v, func(t *testing.T) {
_, err := files.NewLocal(suite.getLocalFile(value))
assert.Error(t, err)
})
}
}
func (suite *LocalTestSuite) TestOk() {
file, err := files.NewLocal(suite.getLocalFile("readable"))
suite.NoError(err)
reader, err := file.Open(context.Background())
suite.NoError(err)
data, err := io.ReadAll(reader)
suite.NoError(err)
suite.Equal("Hooray!", strings.TrimSpace(string(data)))
}
func TestLocal(t *testing.T) {
t.Parallel()
suite.Run(t, &LocalTestSuite{})
}
View File
+1
View File
@@ -0,0 +1 @@
Hooray!
+66 -154
View File
@@ -4,16 +4,13 @@ import (
"bufio"
"context"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"regexp"
"strings"
"sync"
"time"
"github.com/9seconds/mtg/v2/ipblocklist/files"
"github.com/9seconds/mtg/v2/mtglib"
"github.com/kentik/patricia"
"github.com/kentik/patricia/bool_tree"
@@ -41,20 +38,16 @@ var fireholRegexpComment = regexp.MustCompile(`\s*#.*?$`)
// 127.0.0.1 # you can specify an IP
// 10.0.0.0/8 # or cidr
type Firehol struct {
ctx context.Context
ctxCancel context.CancelFunc
logger mtglib.Logger
ctx context.Context
ctxCancel context.CancelFunc
logger mtglib.Logger
updateMutex sync.RWMutex
rwMutex sync.RWMutex
blocklists []files.File
remoteURLs []string
localFiles []string
httpClient *http.Client
workerPool *ants.Pool
treeV4 *bool_tree.TreeV4
treeV6 *bool_tree.TreeV6
treeV4 *bool_tree.TreeV4
treeV6 *bool_tree.TreeV6
}
// Shutdown stop a background update process.
@@ -68,8 +61,8 @@ func (f *Firehol) Contains(ip net.IP) bool {
return true
}
f.rwMutex.RLock()
defer f.rwMutex.RUnlock()
f.updateMutex.RLock()
defer f.updateMutex.RUnlock()
if ip4 := ip.To4(); ip4 != nil {
return f.containsIPv4(ip4)
@@ -98,22 +91,14 @@ func (f *Firehol) Run(updateEach time.Duration) {
}
}()
if err := f.update(); err != nil {
f.logger.WarningError("cannot update blocklist", err)
} else {
f.logger.Info("blocklist was updated")
}
f.update()
for {
select {
case <-f.ctx.Done():
return
case <-ticker.C:
if err := f.update(); err != nil {
f.logger.WarningError("cannot update blocklist", err)
} else {
f.logger.Info("blocklist was updated")
}
f.update()
}
}
}
@@ -121,7 +106,7 @@ func (f *Firehol) Run(updateEach time.Duration) {
func (f *Firehol) containsIPv4(addr net.IP) bool {
ip := patricia.NewIPv4AddressFromBytes(addr, 32) // nolint: gomnd
if ok, _, err := f.treeV4.FindDeepestTag(ip); ok && err == nil {
if ok, _ := f.treeV4.FindDeepestTag(ip); ok {
return true
}
@@ -131,128 +116,60 @@ func (f *Firehol) containsIPv4(addr net.IP) bool {
func (f *Firehol) containsIPv6(addr net.IP) bool {
ip := patricia.NewIPv6Address(addr, 128) // nolint: gomnd
if ok, _, err := f.treeV6.FindDeepestTag(ip); ok && err == nil {
if ok, _ := f.treeV6.FindDeepestTag(ip); ok {
return true
}
return false
}
func (f *Firehol) update() error { // nolint: funlen, cyclop
func (f *Firehol) update() {
ctx, cancel := context.WithCancel(f.ctx)
defer cancel()
wg := &sync.WaitGroup{}
wg.Add(len(f.remoteURLs) + len(f.localFiles))
wg.Add(len(f.blocklists))
treeMutex := &sync.Mutex{}
v4tree := bool_tree.NewTreeV4()
v6tree := bool_tree.NewTreeV6()
errorChan := make(chan error, 1)
defer close(errorChan)
for _, v := range f.localFiles {
go func(filename string) {
for _, v := range f.blocklists {
go func(file files.File) {
defer wg.Done()
if err := f.updateLocalFile(ctx, filename, treeMutex, v4tree, v6tree); err != nil {
cancel()
f.logger.BindStr("filename", filename).WarningError("cannot update", err)
logger := f.logger.BindStr("filename", file.String())
select {
case errorChan <- err:
default:
}
fileContent, err := file.Open(ctx)
if err != nil {
logger.WarningError("update has failed", err)
return
}
defer fileContent.Close()
if err := f.updateFromFile(treeMutex, v4tree, v6tree, bufio.NewScanner(fileContent)); err != nil {
logger.WarningError("update has failed", err)
}
}(v)
}
for _, v := range f.remoteURLs {
value := v
f.workerPool.Submit(func() { // nolint: errcheck
defer wg.Done()
if err := f.updateRemoteURL(ctx, value, treeMutex, v4tree, v6tree); err != nil {
cancel()
f.logger.BindStr("url", value).WarningError("cannot update", err)
select {
case errorChan <- err:
default:
}
}
})
}
wg.Wait()
select {
case err := <-errorChan:
return fmt.Errorf("cannot update trees: %w", err)
default:
}
f.rwMutex.Lock()
defer f.rwMutex.Unlock()
f.updateMutex.Lock()
defer f.updateMutex.Unlock()
f.treeV4 = v4tree
f.treeV6 = v6tree
return nil
f.logger.Info("blocklist was updated")
}
func (f *Firehol) updateLocalFile(ctx context.Context, filename string,
mutex sync.Locker,
v4tree *bool_tree.TreeV4, v6tree *bool_tree.TreeV6) error {
filefp, err := os.Open(filename)
if err != nil {
return fmt.Errorf("cannot open file: %w", err)
}
go func(ctx context.Context, closer io.Closer) {
<-ctx.Done()
closer.Close()
}(ctx, filefp)
defer filefp.Close()
return f.updateTrees(mutex, filefp, v4tree, v6tree)
}
func (f *Firehol) updateRemoteURL(ctx context.Context, url string,
mutex sync.Locker,
v4tree *bool_tree.TreeV4, v6tree *bool_tree.TreeV6) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("cannot build a request: %w", err)
}
resp, err := f.httpClient.Do(req) // nolint: bodyclose
if err != nil {
return fmt.Errorf("cannot request a remote URL %s: %w", url, err)
}
go func(ctx context.Context, closer io.Closer) {
<-ctx.Done()
closer.Close()
}(ctx, resp.Body)
defer func(rc io.ReadCloser) {
io.Copy(io.Discard, rc) // nolint: errcheck
rc.Close()
}(resp.Body)
return f.updateTrees(mutex, resp.Body, v4tree, v6tree)
}
func (f *Firehol) updateTrees(mutex sync.Locker,
reader io.Reader,
func (f *Firehol) updateFromFile(mutex sync.Locker,
v4tree *bool_tree.TreeV4,
v6tree *bool_tree.TreeV6) error {
scanner := bufio.NewScanner(reader)
v6tree *bool_tree.TreeV6,
scanner *bufio.Scanner) error {
for scanner.Scan() {
text := scanner.Text()
text = fireholRegexpComment.ReplaceAllLiteralString(text, "")
@@ -267,13 +184,11 @@ func (f *Firehol) updateTrees(mutex sync.Locker,
return fmt.Errorf("cannot parse a line: %w", err)
}
if err := f.updateAddToTrees(ip, cidr, mutex, v4tree, v6tree); err != nil {
return fmt.Errorf("cannot add a node to the tree: %w", err)
}
f.updateAddToTrees(ip, cidr, mutex, v4tree, v6tree)
}
if scanner.Err() != nil {
return fmt.Errorf("cannot parse a response: %w", scanner.Err())
return fmt.Errorf("cannot parse a file: %w", scanner.Err())
}
return nil
@@ -302,25 +217,15 @@ func (f *Firehol) updateParseLine(text string) (net.IP, uint, error) {
func (f *Firehol) updateAddToTrees(ip net.IP, cidr uint,
mutex sync.Locker,
v4tree *bool_tree.TreeV4, v6tree *bool_tree.TreeV6) error {
v4tree *bool_tree.TreeV4, v6tree *bool_tree.TreeV6) {
mutex.Lock()
defer mutex.Unlock()
if ip.To4() != nil {
addr := patricia.NewIPv4AddressFromBytes(ip, cidr)
if _, _, err := v4tree.Set(addr, true); err != nil {
return err // nolint: wrapcheck
}
v4tree.Set(patricia.NewIPv4AddressFromBytes(ip, cidr), true)
} else {
addr := patricia.NewIPv6Address(ip, cidr)
if _, _, err := v6tree.Set(addr, true); err != nil {
return err // nolint: wrapcheck
}
v6tree.Set(patricia.NewIPv6Address(ip, cidr), true)
}
return nil
}
// NewFirehol creates a new instance of FireHOL IP blocklist.
@@ -329,27 +234,36 @@ func (f *Firehol) updateAddToTrees(ip net.IP, cidr uint,
// when it is necessary.
func NewFirehol(logger mtglib.Logger, network mtglib.Network,
downloadConcurrency uint,
remoteURLs []string,
urls []string,
localFiles []string) (*Firehol, error) {
for _, v := range remoteURLs {
parsed, err := url.Parse(v)
if err != nil {
return nil, fmt.Errorf("incorrect url %s: %w", v, err)
}
switch parsed.Scheme {
case "http", "https":
default:
return nil, fmt.Errorf("unsupported url %s", v)
}
}
blocklists := []files.File{}
for _, v := range localFiles {
if stat, err := os.Stat(v); os.IsNotExist(err) || stat.IsDir() || stat.Mode().Perm()&0o400 == 0 {
return nil, fmt.Errorf("%s is not a readable file", v)
file, err := files.NewLocal(v)
if err != nil {
return nil, fmt.Errorf("cannot create a local file %s: %w", v, err)
}
blocklists = append(blocklists, file)
}
httpClient := network.MakeHTTPClient(nil)
for _, v := range urls {
file, err := files.NewHTTP(httpClient, v)
if err != nil {
return nil, fmt.Errorf("cannot create a HTTP file %s: %w", v, err)
}
blocklists = append(blocklists, file)
}
return NewFireholFromFiles(logger, downloadConcurrency, blocklists)
}
func NewFireholFromFiles(logger mtglib.Logger,
downloadConcurrency uint,
blocklists []files.File) (*Firehol, error) {
if downloadConcurrency == 0 {
downloadConcurrency = DefaultFireholDownloadConcurrency
}
@@ -361,11 +275,9 @@ func NewFirehol(logger mtglib.Logger, network mtglib.Network,
ctx: ctx,
ctxCancel: cancel,
logger: logger.Named("firehol"),
httpClient: network.MakeHTTPClient(nil),
treeV4: bool_tree.NewTreeV4(),
treeV6: bool_tree.NewTreeV6(),
workerPool: workerPool,
remoteURLs: remoteURLs,
localFiles: localFiles,
blocklists: blocklists,
}, nil
}
+1
View File
@@ -7,6 +7,7 @@ type noopLogger struct{}
func (n noopLogger) Named(_ string) mtglib.Logger { return n }
func (n noopLogger) BindInt(_ string, _ int) mtglib.Logger { return n }
func (n noopLogger) BindStr(_, _ string) mtglib.Logger { return n }
func (n noopLogger) BindJSON(_, _ string) mtglib.Logger { return n }
func (n noopLogger) Printf(_ string, _ ...interface{}) {}
func (n noopLogger) Info(_ string) {}
func (n noopLogger) Warning(_ string) {}
+14
View File
@@ -15,6 +15,7 @@ const (
zeroLogContextVarTypeUnknown zeroLogContextVarType = iota
zeroLogContextVarTypeStr
zeroLogContextVarTypeInt
zeroLogContextVarTypeJSON
)
type zeroLogContext struct {
@@ -66,6 +67,17 @@ func (z *zeroLogContext) BindStr(name, value string) mtglib.Logger {
}
}
func (z *zeroLogContext) BindJSON(name, value string) mtglib.Logger {
return &zeroLogContext{
name: z.name,
log: z.log,
ctxVarType: zeroLogContextVarTypeJSON,
ctxVarName: name,
ctxVarStr: value,
parent: z,
}
}
func (z *zeroLogContext) Printf(format string, args ...interface{}) {
z.Debug(fmt.Sprintf(format, args...))
}
@@ -110,6 +122,8 @@ func (z *zeroLogContext) attachCtx(evt *zerolog.Event) {
evt.Str(z.ctxVarName, z.ctxVarStr)
case zeroLogContextVarTypeInt:
evt.Int(z.ctxVarName, z.ctxVarInt)
case zeroLogContextVarTypeJSON:
evt.RawJSON(z.ctxVarName, []byte(z.ctxVarStr))
case zeroLogContextVarTypeUnknown:
}
}
+5 -4
View File
@@ -4,12 +4,13 @@ import (
"bytes"
"context"
"io"
"net"
"sync"
"github.com/9seconds/mtg/v2/essentials"
)
type connTraffic struct {
net.Conn
essentials.Conn
streamID string
stream EventStream
@@ -37,7 +38,7 @@ func (c connTraffic) Write(b []byte) (int, error) {
}
type connRewind struct {
net.Conn
essentials.Conn
active io.Reader
buf bytes.Buffer
@@ -58,7 +59,7 @@ func (c *connRewind) Rewind() {
c.active = io.MultiReader(&c.buf, c.Conn)
}
func newConnRewind(conn net.Conn) *connRewind {
func newConnRewind(conn essentials.Conn) *connRewind {
rv := &connRewind{
Conn: conn,
}
+3 -3
View File
@@ -14,7 +14,7 @@ import (
)
type ConnRewindBaseConn struct {
testlib.NetConnMock
testlib.EssentialsConnMock
readBuffer bytes.Buffer
}
@@ -29,13 +29,13 @@ type ConnTrafficTestSuite struct {
suite.Suite
eventStreamMock *EventStreamMock
connMock *testlib.NetConnMock
connMock *testlib.EssentialsConnMock
conn io.ReadWriter
}
func (suite *ConnTrafficTestSuite) SetupTest() {
suite.eventStreamMock = &EventStreamMock{}
suite.connMock = &testlib.NetConnMock{}
suite.connMock = &testlib.EssentialsConnMock{}
suite.conn = connTraffic{
Conn: suite.connMock,
streamID: "CONNID",
+10 -3
View File
@@ -23,6 +23,8 @@ import (
"net"
"net/http"
"time"
"github.com/9seconds/mtg/v2/essentials"
)
var (
@@ -61,6 +63,8 @@ const (
DefaultConcurrency = 4096
// DefaultBufferSize is a default size of a copy buffer.
//
// Deprecated: this setting no longer makes any effect.
DefaultBufferSize = 16 * 1024 // 16 kib
// DefaultDomainFrontingPort is a default port (HTTPS) to connect to in
@@ -114,16 +118,16 @@ const (
// 3. Doing HTTP requests (for example, for FireHOL ipblocklist).
type Network interface {
// Dial establishes context-free TCP connections.
Dial(network, address string) (net.Conn, error)
Dial(network, address string) (essentials.Conn, error)
// DialContext dials using a context. This is a preferrable
// way of establishing TCP connections.
DialContext(ctx context.Context, network, address string) (net.Conn, error)
DialContext(ctx context.Context, network, address string) (essentials.Conn, error)
// MakeHTTPClient build an HTTP client with given dial function. If
// nothing is provided, then DialContext of this interface is going
// to be used.
MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error)) *http.Client
MakeHTTPClient(func(ctx context.Context, network, address string) (essentials.Conn, error)) *http.Client
}
// AntiReplayCache is an interface that is used to detect replay attacks
@@ -245,6 +249,9 @@ type Logger interface {
// BindStr binds new string parameter to a new logger instance.
BindStr(name, value string) Logger
// BindJSON binds a new JSON-encoded string to a new logger instance.
BindJSON(name, value string) Logger
// Printf is to support log.Logger behavior.
Printf(format string, args ...interface{})
+1
View File
@@ -11,6 +11,7 @@ type NoopLogger struct{}
func (n NoopLogger) Named(_ string) Logger { return n }
func (n NoopLogger) BindInt(_ string, _ int) Logger { return n }
func (n NoopLogger) BindStr(_, _ string) Logger { return n }
func (n NoopLogger) BindJSON(_, _ string) Logger { return n }
func (n NoopLogger) Printf(_ string, _ ...interface{}) {}
func (n NoopLogger) Info(_ string) {}
func (n NoopLogger) Warning(_ string) {}
+2 -2
View File
@@ -4,13 +4,13 @@ import (
"bytes"
"fmt"
"math/rand"
"net"
"github.com/9seconds/mtg/v2/essentials"
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
)
type Conn struct {
net.Conn
essentials.Conn
readBuffer bytes.Buffer
}
+1 -1
View File
@@ -15,7 +15,7 @@ import (
)
type ConnMock struct {
testlib.NetConnMock
testlib.EssentialsConnMock
readBuffer bytes.Buffer
writeBuffer bytes.Buffer
@@ -42,7 +42,7 @@ func (suite *ClientHandshakeTestSuite) TestOk() {
writeData := make([]byte, len(snapshot.Encrypted.Text.data))
readData := make([]byte, len(snapshot.Decrypted.Text.data))
connMock := &testlib.NetConnMock{}
connMock := &testlib.EssentialsConnMock{}
connMock.On("Read", mock.Anything).
Once().
Return(len(snapshot.Decrypted.Text.data), nil).
+3 -2
View File
@@ -2,11 +2,12 @@ package obfuscated2
import (
"crypto/cipher"
"net"
"github.com/9seconds/mtg/v2/essentials"
)
type Conn struct {
net.Conn
essentials.Conn
Encryptor cipher.Stream
Decryptor cipher.Stream
@@ -16,7 +16,7 @@ import (
type ServerHandshakeTestSuite struct {
suite.Suite
connMock *testlib.NetConnMock
connMock *testlib.EssentialsConnMock
proxyConn obfuscated2.Conn
encryptor cipher.Stream
decryptor cipher.Stream
@@ -24,7 +24,7 @@ type ServerHandshakeTestSuite struct {
func (suite *ServerHandshakeTestSuite) SetupTest() {
buf := &bytes.Buffer{}
suite.connMock = &testlib.NetConnMock{}
suite.connMock = &testlib.EssentialsConnMock{}
encryptor, decryptor, err := obfuscated2.ServerHandshake(buf)
suite.NoError(err)
-19
View File
@@ -1,19 +0,0 @@
package relay
import (
"fmt"
"net"
"time"
)
type conn struct {
net.Conn
}
func (c conn) Read(p []byte) (int, error) {
if err := c.SetReadDeadline(time.Now().Add(getTimeout())); err != nil {
return 0, fmt.Errorf("cannot set read deadline: %w", err)
}
return c.Conn.Read(p) // nolint: wrapcheck
}
+1 -6
View File
@@ -1,12 +1,7 @@
package relay
import "time"
const (
ConnectionTimeToLiveMin = 2 * time.Minute
ConnectionTimeToLiveMax = 10 * time.Minute
TimeoutMin = 20 * time.Second
TimeoutMax = time.Minute
copyBufferSize = 64 * 1024
)
type Logger interface {
+8 -21
View File
@@ -2,31 +2,18 @@ package relay
import "sync"
type eastWest struct {
east []byte
west []byte
}
var eastWestPool = sync.Pool{
var copyBufferPool = sync.Pool{
New: func() interface{} {
return &eastWest{}
rv := make([]byte, copyBufferSize)
return &rv
},
}
func acquireEastWest(bufferSize int) *eastWest {
wanted := eastWestPool.Get().(*eastWest) // nolint: forcetypeassert
if len(wanted.east) != bufferSize {
wanted.east = make([]byte, bufferSize)
}
if len(wanted.west) != bufferSize {
wanted.west = make([]byte, bufferSize)
}
return wanted
func acquireCopyBuffer() *[]byte {
return copyBufferPool.Get().(*[]byte)
}
func releaseEastWest(ew *eastWest) {
eastWestPool.Put(ew)
func releaseCopyBuffer(buf *[]byte) {
copyBufferPool.Put(buf)
}
+27 -23
View File
@@ -2,17 +2,17 @@ package relay
import (
"context"
"errors"
"io"
"net"
"sync"
"github.com/9seconds/mtg/v2/essentials"
)
func Relay(ctx context.Context, log Logger, bufferSize int,
telegramConn net.Conn, clientConn io.ReadWriteCloser) {
func Relay(ctx context.Context, log Logger, telegramConn, clientConn essentials.Conn) {
defer telegramConn.Close()
defer clientConn.Close()
ctx, cancel := context.WithTimeout(ctx, getConnectionTimeToLive())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
@@ -21,30 +21,34 @@ func Relay(ctx context.Context, log Logger, bufferSize int,
clientConn.Close()
}()
buffers := acquireEastWest(bufferSize)
defer releaseEastWest(buffers)
closeChan := make(chan struct{})
telegramConn = conn{
Conn: telegramConn,
}
go func() {
defer close(closeChan)
wg := &sync.WaitGroup{}
wg.Add(2) // nolint: gomnd
pump(log, telegramConn, clientConn, "client -> telegram")
}()
go pump(log, telegramConn, clientConn, wg, buffers.east, "east -> west")
pump(log, clientConn, telegramConn, "telegram -> client")
pump(log, clientConn, telegramConn, wg, buffers.west, "west -> east")
wg.Wait()
<-closeChan
}
func pump(log Logger, src io.ReadCloser, dst io.WriteCloser, wg *sync.WaitGroup,
buf []byte, direction string) {
defer wg.Done()
defer src.Close()
defer dst.Close()
func pump(log Logger, src, dst essentials.Conn, direction string) {
defer src.CloseRead() // nolint: errcheck
defer dst.CloseWrite() // nolint: errcheck
if n, err := io.CopyBuffer(dst, src, buf); err != nil {
log.Printf("cannot pump %s (written %d bytes): %w", direction, n, err)
copyBuffer := acquireCopyBuffer()
defer releaseCopyBuffer(copyBuffer)
n, err := io.CopyBuffer(src, dst, *copyBuffer)
switch {
case err == nil:
log.Printf("%s has been finished", direction)
case errors.Is(err, io.EOF):
log.Printf("%s has been finished because of EOF. Written %d bytes", direction, n)
default:
log.Printf("%s has been finished (written %d bytes): %v", direction, n, err)
}
}
+9 -7
View File
@@ -17,8 +17,8 @@ type RelayTestSuite struct {
loggerMock relay.Logger
ctx context.Context
ctxCancel context.CancelFunc
telegramConnMock *testlib.NetConnMock
clientConnMock *testlib.NetConnMock
telegramConnMock *testlib.EssentialsConnMock
clientConnMock *testlib.EssentialsConnMock
}
func (suite *RelayTestSuite) SetupTest() {
@@ -26,8 +26,8 @@ func (suite *RelayTestSuite) SetupTest() {
suite.ctx = ctx
suite.ctxCancel = cancel
suite.loggerMock = &loggerMock{}
suite.telegramConnMock = &testlib.NetConnMock{}
suite.clientConnMock = &testlib.NetConnMock{}
suite.telegramConnMock = &testlib.EssentialsConnMock{}
suite.clientConnMock = &testlib.EssentialsConnMock{}
}
func (suite *RelayTestSuite) TearDownTest() {
@@ -37,17 +37,19 @@ func (suite *RelayTestSuite) TearDownTest() {
}
func (suite *RelayTestSuite) TestExit() {
suite.telegramConnMock.On("SetReadDeadline", mock.Anything).Return(nil)
suite.telegramConnMock.On("Close").Return(nil)
suite.telegramConnMock.On("CloseRead").Return(nil).Once()
suite.telegramConnMock.On("CloseWrite").Return(nil).Once()
suite.telegramConnMock.On("Read", mock.Anything).Return(10, io.EOF).Once()
suite.telegramConnMock.On("Write", mock.Anything).Return(10, io.EOF).Maybe()
suite.clientConnMock.On("Read", mock.Anything).Return(0, io.EOF).Once()
suite.clientConnMock.On("Write", mock.Anything).Return(10, io.EOF).Maybe()
suite.clientConnMock.On("Close").Return(nil)
suite.clientConnMock.On("CloseRead").Return(nil).Once()
suite.clientConnMock.On("CloseWrite").Return(nil).Once()
relay.Relay(suite.ctx, suite.loggerMock, 1024,
suite.telegramConnMock, suite.clientConnMock)
relay.Relay(suite.ctx, suite.loggerMock, suite.telegramConnMock, suite.clientConnMock)
}
func TestRelay(t *testing.T) {
-22
View File
@@ -1,22 +0,0 @@
package relay
import (
"math/rand"
"time"
)
func getConnectionTimeToLive() time.Duration {
return getTime(ConnectionTimeToLiveMin, ConnectionTimeToLiveMax)
}
func getTimeout() time.Duration {
return getTime(TimeoutMin, TimeoutMax)
}
func getTime(minDuration, maxDuration time.Duration) time.Duration {
minDurationInSeconds := int(minDuration.Seconds())
maxDurationInSeconds := int(maxDuration.Seconds())
number := minDurationInSeconds + rand.Intn(maxDurationInSeconds-minDurationInSeconds)
return time.Duration(number) * time.Second
}
@@ -1,37 +0,0 @@
package relay
import (
"fmt"
"testing"
"github.com/stretchr/testify/suite"
)
type TimeoutsTestSuite struct {
suite.Suite
}
func (suite *TimeoutsTestSuite) TestGetConnectionTimeToLive() {
for i := 0; i < 100; i++ {
value := getConnectionTimeToLive()
message := fmt.Sprintf("generated value is %v", value)
suite.GreaterOrEqual(value, ConnectionTimeToLiveMin, message)
suite.LessOrEqual(value, ConnectionTimeToLiveMax, message)
}
}
func (suite *TimeoutsTestSuite) TestGetTimeout() {
for i := 0; i < 100; i++ {
value := getTimeout()
message := fmt.Sprintf("generated value is %v", value)
suite.GreaterOrEqual(value, TimeoutMin, message)
suite.LessOrEqual(value, TimeoutMax, message)
}
}
func TestTimeouts(t *testing.T) {
t.Parallel()
suite.Run(t, &TimeoutsTestSuite{})
}
+8
View File
@@ -7,6 +7,14 @@ type addressPool struct {
v6 [][]tgAddr
}
func (a addressPool) isValidDC(dc int) bool {
return dc > 0 && dc <= len(a.v4) && dc <= len(a.v6)
}
func (a addressPool) getRandomDC() int {
return 1 + rand.Intn(len(a.v4))
}
func (a addressPool) getV4(dc int) []tgAddr {
return a.get(a.v4, dc-1)
}
+3 -2
View File
@@ -2,7 +2,8 @@ package telegram
import (
"context"
"net"
"github.com/9seconds/mtg/v2/essentials"
)
type preferIP uint8
@@ -82,5 +83,5 @@ var (
)
type Dialer interface {
DialContext(ctx context.Context, network, address string) (net.Conn, error)
DialContext(ctx context.Context, network, address string) (essentials.Conn, error)
}
+12 -3
View File
@@ -3,8 +3,9 @@ package telegram
import (
"context"
"fmt"
"net"
"strings"
"github.com/9seconds/mtg/v2/essentials"
)
type Telegram struct {
@@ -13,7 +14,7 @@ type Telegram struct {
pool addressPool
}
func (t Telegram) Dial(ctx context.Context, dc int) (net.Conn, error) {
func (t Telegram) Dial(ctx context.Context, dc int) (essentials.Conn, error) {
var addresses []tgAddr
switch t.preferIP {
@@ -28,7 +29,7 @@ func (t Telegram) Dial(ctx context.Context, dc int) (net.Conn, error) {
}
var (
conn net.Conn
conn essentials.Conn
err error
)
@@ -42,6 +43,14 @@ func (t Telegram) Dial(ctx context.Context, dc int) (net.Conn, error) {
return nil, fmt.Errorf("cannot dial to %d dc: %w", dc, err)
}
func (t Telegram) IsKnownDC(dc int) bool {
return t.pool.isValidDC(dc)
}
func (t Telegram) GetFallbackDC() int {
return t.pool.getRandomDC()
}
func New(dialer Dialer, ipPreference string, useTestDCs bool) (*Telegram, error) {
var pref preferIP
@@ -44,6 +44,7 @@ func (suite *TelegramTestSuite) TestUnknownDC() {
suite.T().Run(strconv.Itoa(value), func(t *testing.T) {
_, err := suite.t.Dial(context.Background(), value)
assert.Error(t, err)
assert.False(t, suite.t.IsKnownDC(value))
})
}
}
@@ -71,6 +72,7 @@ func (suite *TelegramTestSuite) TestDialToCorrectIPs() {
_, err := suite.t.Dial(context.Background(), idx)
assert.True(t, errors.Is(err, io.EOF))
assert.True(t, suite.t.IsKnownDC(idx))
})
}
}
@@ -135,6 +137,22 @@ func (suite *TelegramTestSuite) TestUnknownPreferIP() {
suite.Error(err)
}
func (suite *TelegramTestSuite) TestFallbackDC() {
dcs := make([]int, 10)
for i := 0; i < len(dcs); i++ {
dcs[i] = suite.t.GetFallbackDC()
}
for _, v := range dcs {
value := v
suite.T().Run(strconv.Itoa(value), func(t *testing.T) {
assert.True(t, suite.t.IsKnownDC(value))
})
}
}
func TestTelegram(t *testing.T) {
t.Parallel()
suite.Run(t, &TelegramTestSuite{})
+43 -25
View File
@@ -9,6 +9,7 @@ import (
"sync"
"time"
"github.com/9seconds/mtg/v2/essentials"
"github.com/9seconds/mtg/v2/mtglib/internal/faketls"
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2"
@@ -23,16 +24,17 @@ type Proxy struct {
ctxCancel context.CancelFunc
streamWaitGroup sync.WaitGroup
tolerateTimeSkewness time.Duration
bufferSize int
domainFrontingPort int
workerPool *ants.PoolWithFunc
telegram *telegram.Telegram
allowFallbackOnUnknownDC bool
tolerateTimeSkewness time.Duration
domainFrontingPort int
workerPool *ants.PoolWithFunc
telegram *telegram.Telegram
secret Secret
network Network
antiReplayCache AntiReplayCache
ipBlocklist IPBlocklist
blocklist IPBlocklist
whitelist IPBlocklist
eventStream EventStream
logger Logger
}
@@ -44,7 +46,7 @@ func (p *Proxy) DomainFrontingAddress() string {
// ServeConn serves a connection. We do not check IP blocklist and
// concurrency limit here.
func (p *Proxy) ServeConn(conn net.Conn) {
func (p *Proxy) ServeConn(conn essentials.Conn) {
p.streamWaitGroup.Add(1)
defer p.streamWaitGroup.Done()
@@ -83,14 +85,13 @@ func (p *Proxy) ServeConn(conn net.Conn) {
relay.Relay(
ctx,
ctx.logger.Named("relay"),
p.bufferSize,
ctx.telegramConn,
ctx.clientConn,
)
}
// Serve starts a proxy on a given listener.
func (p *Proxy) Serve(listener net.Listener) error {
func (p *Proxy) Serve(listener net.Listener) error { // nolint: cyclop
p.streamWaitGroup.Add(1)
defer p.streamWaitGroup.Done()
@@ -108,7 +109,15 @@ func (p *Proxy) Serve(listener net.Listener) error {
ipAddr := conn.RemoteAddr().(*net.TCPAddr).IP
logger := p.logger.BindStr("ip", ipAddr.String())
if p.ipBlocklist.Contains(ipAddr) {
if p.whitelist != nil && !p.whitelist.Contains(ipAddr) {
conn.Close()
logger.Info("ip was rejected by whitelist")
p.eventStream.Send(p.ctx, NewEventIPBlocklisted(ipAddr))
continue
}
if p.blocklist.Contains(ipAddr) {
conn.Close()
logger.Info("ip was blacklisted")
p.eventStream.Send(p.ctx, NewEventIPBlocklisted(ipAddr))
@@ -207,7 +216,16 @@ func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error {
}
func (p *Proxy) doTelegramCall(ctx *streamContext) error {
conn, err := p.telegram.Dial(ctx, ctx.dc)
dc := ctx.dc
if p.allowFallbackOnUnknownDC && !p.telegram.IsKnownDC(dc) {
dc = p.telegram.GetFallbackDC()
ctx.logger = ctx.logger.BindInt("fallback_dc", dc)
ctx.logger.Warning("unknown DC, fallbacks")
}
conn, err := p.telegram.Dial(ctx, dc)
if err != nil {
return fmt.Errorf("cannot dial to Telegram: %w", err)
}
@@ -257,7 +275,6 @@ func (p *Proxy) doDomainFronting(ctx *streamContext, conn *connRewind) {
relay.Relay(
ctx,
ctx.logger.Named("domain-fronting"),
p.bufferSize,
frontConn,
conn,
)
@@ -276,23 +293,24 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
ctx, cancel := context.WithCancel(context.Background())
proxy := &Proxy{
ctx: ctx,
ctxCancel: cancel,
secret: opts.Secret,
network: opts.Network,
antiReplayCache: opts.AntiReplayCache,
ipBlocklist: opts.IPBlocklist,
eventStream: opts.EventStream,
logger: opts.getLogger("proxy"),
domainFrontingPort: opts.getDomainFrontingPort(),
tolerateTimeSkewness: opts.getTolerateTimeSkewness(),
bufferSize: opts.getBufferSize(),
telegram: tg,
ctx: ctx,
ctxCancel: cancel,
secret: opts.Secret,
network: opts.Network,
antiReplayCache: opts.AntiReplayCache,
blocklist: opts.IPBlocklist,
whitelist: opts.IPWhitelist,
eventStream: opts.EventStream,
logger: opts.getLogger("proxy"),
domainFrontingPort: opts.getDomainFrontingPort(),
tolerateTimeSkewness: opts.getTolerateTimeSkewness(),
allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC,
telegram: tg,
}
pool, err := ants.NewPoolWithFunc(opts.getConcurrency(),
func(arg interface{}) {
proxy.ServeConn(arg.(net.Conn))
proxy.ServeConn(arg.(essentials.Conn))
},
ants.WithLogger(opts.getLogger("ants")),
ants.WithNonblocking(true))
+17 -8
View File
@@ -28,6 +28,11 @@ type ProxyOpts struct {
// This is a mandatory setting.
IPBlocklist IPBlocklist
// IPWhitelist defines a whitelist of IPs to allow to use proxy.
//
// This is an optional setting, ignored by default (no restrictions).
IPWhitelist IPBlocklist
// EventStream defines an instance of event stream.
//
// This ia a mandatory setting.
@@ -45,6 +50,8 @@ type ProxyOpts struct {
// buffers: to and from.
//
// This is an optional setting.
//
// Deprecated: this setting is no longer makes any effect.
BufferSize uint
// Concurrency is a size of the worker pool for connection management.
@@ -90,6 +97,16 @@ type ProxyOpts struct {
// This is an optional setting.
DomainFrontingPort uint
// AllowFallbackOnUnknownDC defines how proxy behaves if unknown DC was
// requested. If this setting is set to false, then such connection
// will be rejected. Otherwise, proxy will chose any DC.
//
// Telegram is designed in a way that any DC can serve any request,
// the problem is a latency.
//
// This is an optional setting.
AllowFallbackOnUnknownDC bool
// UseTestDCs defines if we have to connect to production or to staging
// DCs of Telegram.
//
@@ -119,14 +136,6 @@ func (p ProxyOpts) valid() error {
return nil
}
func (p ProxyOpts) getBufferSize() int {
if p.BufferSize < 1 {
return DefaultBufferSize
}
return int(p.BufferSize)
}
func (p ProxyOpts) getConcurrency() int {
if p.Concurrency == 0 {
return DefaultConcurrency
+5 -3
View File
@@ -6,13 +6,15 @@ import (
"encoding/base64"
"net"
"time"
"github.com/9seconds/mtg/v2/essentials"
)
type streamContext struct {
ctx context.Context
ctxCancel context.CancelFunc
clientConn net.Conn
telegramConn net.Conn
clientConn essentials.Conn
telegramConn essentials.Conn
streamID string
dc int
logger Logger
@@ -50,7 +52,7 @@ func (s *streamContext) ClientIP() net.IP {
return s.clientConn.RemoteAddr().(*net.TCPAddr).IP
}
func newStreamContext(ctx context.Context, logger Logger, clientConn net.Conn) *streamContext {
func newStreamContext(ctx context.Context, logger Logger, clientConn essentials.Conn) *streamContext {
connIDBytes := make([]byte, ConnectionIDBytesLength)
if _, err := rand.Read(connIDBytes); err != nil {
+3 -3
View File
@@ -12,7 +12,7 @@ import (
type StreamContextTestSuite struct {
suite.Suite
connMock *testlib.NetConnMock
connMock *testlib.EssentialsConnMock
logger NoopLogger
ctx *streamContext
ctxCancel context.CancelFunc
@@ -27,7 +27,7 @@ func (suite *StreamContextTestSuite) SetupTest() {
ctx = context.WithValue(ctx, "key", "value") // nolint: golint, revive, staticcheck
suite.ctxCancel = cancel
suite.connMock = &testlib.NetConnMock{}
suite.connMock = &testlib.EssentialsConnMock{}
addr := &net.TCPAddr{
IP: net.ParseIP("10.0.0.10"),
@@ -73,7 +73,7 @@ func (suite *StreamContextTestSuite) TestClientIP() {
func (suite *StreamContextTestSuite) TestClose() {
suite.connMock.On("Close").Once().Return(nil)
tgConnMock := &testlib.NetConnMock{}
tgConnMock := &testlib.EssentialsConnMock{}
tgConnMock.On("Close").Once().Return(nil)
suite.ctx.telegramConn = tgConnMock
+7 -5
View File
@@ -2,9 +2,10 @@ package network
import (
"context"
"net"
"sync/atomic"
"time"
"github.com/9seconds/mtg/v2/essentials"
)
const (
@@ -30,12 +31,12 @@ type circuitBreakerDialer struct {
resetFailuresTimeout time.Duration
}
func (c *circuitBreakerDialer) Dial(network, address string) (net.Conn, error) {
func (c *circuitBreakerDialer) Dial(network, address string) (essentials.Conn, error) {
return c.DialContext(context.Background(), network, address)
}
func (c *circuitBreakerDialer) DialContext(ctx context.Context,
network, address string) (net.Conn, error) {
network, address string) (essentials.Conn, error) {
switch atomic.LoadUint32(&c.state) {
case circuitBreakerStateClosed:
return c.doClosed(ctx, network, address)
@@ -47,7 +48,7 @@ func (c *circuitBreakerDialer) DialContext(ctx context.Context,
}
func (c *circuitBreakerDialer) doClosed(ctx context.Context,
network, address string) (net.Conn, error) {
network, address string) (essentials.Conn, error) {
conn, err := c.Dialer.DialContext(ctx, network, address)
select {
@@ -78,7 +79,8 @@ func (c *circuitBreakerDialer) doClosed(ctx context.Context,
return conn, err // nolint: wrapcheck
}
func (c *circuitBreakerDialer) doHalfOpened(ctx context.Context, network, address string) (net.Conn, error) {
func (c *circuitBreakerDialer) doHalfOpened(ctx context.Context,
network, address string) (essentials.Conn, error) {
if !atomic.CompareAndSwapUint32(&c.halfOpenAttempts, 0, 1) {
return nil, ErrCircuitBreakerOpened
}
+2 -2
View File
@@ -21,7 +21,7 @@ type CircuitBreakerTestSuite struct {
mutex sync.Mutex
ctx context.Context
ctxCancel context.CancelFunc
connMock *testlib.NetConnMock
connMock *testlib.EssentialsConnMock
baseDialerMock *DialerMock
}
@@ -29,7 +29,7 @@ func (suite *CircuitBreakerTestSuite) SetupTest() {
suite.mutex = sync.Mutex{}
suite.ctx, suite.ctxCancel = context.WithCancel(context.Background())
suite.baseDialerMock = &DialerMock{}
suite.connMock = &testlib.NetConnMock{}
suite.connMock = &testlib.EssentialsConnMock{}
suite.d = newCircuitBreakerDialer(suite.baseDialerMock,
3, 100*time.Millisecond, 50*time.Millisecond)
}
+10 -16
View File
@@ -5,19 +5,19 @@ import (
"fmt"
"net"
"time"
"github.com/9seconds/mtg/v2/essentials"
)
type defaultDialer struct {
net.Dialer
bufferSize int
}
func (d *defaultDialer) Dial(network, address string) (net.Conn, error) {
func (d *defaultDialer) Dial(network, address string) (essentials.Conn, error) {
return d.DialContext(context.Background(), network, address)
}
func (d *defaultDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
func (d *defaultDialer) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
switch network {
case "tcp", "tcp4", "tcp6": // nolint: goconst
default:
@@ -30,13 +30,13 @@ func (d *defaultDialer) DialContext(ctx context.Context, network, address string
}
// we do not need to call to end user. End users call us.
if err := SetServerSocketOptions(conn, d.bufferSize); err != nil {
if err := SetServerSocketOptions(conn, 0); err != nil {
conn.Close()
return nil, fmt.Errorf("cannot set socket options: %w", err)
}
return conn, nil
return conn.(essentials.Conn), nil
}
// NewDefaultDialer build a new dialer which dials bypassing proxies
@@ -44,26 +44,20 @@ func (d *defaultDialer) DialContext(ctx context.Context, network, address string
//
// The most default one you can imagine. But it has tunes TCP
// connections and setups SO_REUSEPORT.
//
// bufferSize is deprecated and ignored. It is kept here for backward
// compatibility.
func NewDefaultDialer(timeout time.Duration, bufferSize int) (Dialer, error) {
switch {
case timeout < 0:
return nil, fmt.Errorf("timeout %v should be positive number", timeout)
case bufferSize < 0:
return nil, fmt.Errorf("buffer size %d should be positive number", bufferSize)
}
if timeout == 0 {
case timeout == 0:
timeout = DefaultTimeout
}
if bufferSize == 0 {
bufferSize = DefaultBufferSize
}
return &defaultDialer{
Dialer: net.Dialer{
Timeout: timeout,
},
bufferSize: bufferSize,
}, nil
}
-5
View File
@@ -30,11 +30,6 @@ func (suite *DefaultDialerTestSuite) TestNegativeTimeout() {
suite.Error(err)
}
func (suite *DefaultDialerTestSuite) TestNegativeBufferSize() {
_, err := network.NewDefaultDialer(0, -1)
suite.Error(err)
}
func (suite *DefaultDialerTestSuite) TestUnsupportedProtocol() {
_, err := suite.d.DialContext(context.Background(),
"udp",
+11 -2
View File
@@ -1,6 +1,8 @@
package network
import (
"fmt"
"net"
"net/http"
"sync"
"time"
@@ -83,8 +85,13 @@ func (d *dnsResolver) LookupAAAA(hostname string) []string {
return ips
}
func newDNSResolver(hostname string, httpClient *http.Client) *dnsResolver {
return &dnsResolver{
func newDNSResolver(hostname string, httpClient *http.Client) (ret *dnsResolver) {
if net.ParseIP(hostname).To4() == nil {
// the hostname is an IPv6 address
hostname = fmt.Sprintf("[%s]", hostname)
}
ret = &dnsResolver{
resolver: doh.Resolver{
Host: hostname,
Class: doh.IN,
@@ -92,4 +99,6 @@ func newDNSResolver(hostname string, httpClient *http.Client) *dnsResolver {
},
cache: map[string]dnsResolverCacheEntry{},
}
return
}
+10 -3
View File
@@ -20,8 +20,9 @@ package network
import (
"context"
"errors"
"net"
"time"
"github.com/9seconds/mtg/v2/essentials"
)
const (
@@ -33,10 +34,16 @@ const (
// request.
DefaultHTTPTimeout = 10 * time.Second
// Deprecated:
//
// DefaultBufferSize defines a TCP buffer size. Both read and write, so
// for real size, please multiply this number by 2.
DefaultBufferSize = 16 * 1024 // 16 kib
// DefaultTCPKeepAlivePeriod defines a time period between 2
// consequitive probes.
DefaultTCPKeepAlivePeriod = 10 * time.Second
// ProxyDialerOpenThreshold is used for load balancing SOCKS5 dialer
// only.
//
@@ -89,6 +96,6 @@ var (
// Dialer defines an interface which is required to bootstrap a network
// instance from.
type Dialer interface {
Dial(network, address string) (net.Conn, error)
DialContext(ctx context.Context, network, address string) (net.Conn, error)
Dial(network, address string) (essentials.Conn, error)
DialContext(ctx context.Context, network, address string) (essentials.Conn, error)
}
+5 -5
View File
@@ -2,8 +2,8 @@ package network
import (
"context"
"net"
"github.com/9seconds/mtg/v2/essentials"
"github.com/stretchr/testify/mock"
)
@@ -11,14 +11,14 @@ type DialerMock struct {
mock.Mock
}
func (d *DialerMock) Dial(network, address string) (net.Conn, error) {
func (d *DialerMock) Dial(network, address string) (essentials.Conn, error) {
args := d.Called(network, address)
return args.Get(0).(net.Conn), args.Error(1) // nolint: wrapcheck
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
}
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
args := d.Called(ctx, network, address)
return args.Get(0).(net.Conn), args.Error(1) // nolint: wrapcheck
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
}
+8 -5
View File
@@ -8,6 +8,7 @@ import (
"net/url"
"strings"
"github.com/9seconds/mtg/v2/essentials"
"github.com/9seconds/mtg/v2/network"
socks5 "github.com/armon/go-socks5"
"github.com/mccutchen/go-httpbin/httpbin"
@@ -18,16 +19,16 @@ type DialerMock struct {
mock.Mock
}
func (d *DialerMock) Dial(network, address string) (net.Conn, error) {
func (d *DialerMock) Dial(network, address string) (essentials.Conn, error) {
args := d.Called(network, address)
return args.Get(0).(net.Conn), args.Error(1) // nolint: wrapcheck
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
}
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
args := d.Called(ctx, network, address)
return args.Get(0).(net.Conn), args.Error(1) // nolint: wrapcheck
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
}
type HTTPServerTestSuite struct {
@@ -53,7 +54,9 @@ func (suite *HTTPServerTestSuite) MakeURL(path string) string {
func (suite *HTTPServerTestSuite) MakeHTTPClient(dialer network.Dialer) *http.Client {
return &http.Client{
Transport: &http.Transport{
DialContext: dialer.DialContext,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
return dialer.DialContext(ctx, network, address) // nolint: wrapcheck
},
},
}
}
+4 -3
View File
@@ -4,19 +4,20 @@ import (
"context"
"fmt"
"math/rand"
"net"
"net/url"
"github.com/9seconds/mtg/v2/essentials"
)
type loadBalancedSocks5Dialer struct {
dialers []Dialer
}
func (l loadBalancedSocks5Dialer) Dial(network, address string) (net.Conn, error) {
func (l loadBalancedSocks5Dialer) Dial(network, address string) (essentials.Conn, error) {
return l.DialContext(context.Background(), network, address)
}
func (l loadBalancedSocks5Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
func (l loadBalancedSocks5Dialer) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
length := len(l.dialers)
start := rand.Intn(length)
moved := false
+10 -6
View File
@@ -9,6 +9,7 @@ import (
"sync"
"time"
"github.com/9seconds/mtg/v2/essentials"
"github.com/9seconds/mtg/v2/mtglib"
)
@@ -30,11 +31,11 @@ type network struct {
dns *dnsResolver
}
func (n *network) Dial(protocol, address string) (net.Conn, error) {
func (n *network) Dial(protocol, address string) (essentials.Conn, error) {
return n.DialContext(context.Background(), protocol, address)
}
func (n *network) DialContext(ctx context.Context, protocol, address string) (net.Conn, error) {
func (n *network) DialContext(ctx context.Context, protocol, address string) (essentials.Conn, error) {
host, port, _ := net.SplitHostPort(address)
ips, err := n.dnsResolve(protocol, host)
@@ -46,7 +47,8 @@ func (n *network) DialContext(ctx context.Context, protocol, address string) (ne
ips[i], ips[j] = ips[j], ips[i]
})
var conn net.Conn
var conn essentials.Conn
for _, v := range ips {
conn, err = n.dialer.DialContext(ctx, protocol, net.JoinHostPort(v, port))
@@ -59,7 +61,7 @@ func (n *network) DialContext(ctx context.Context, protocol, address string) (ne
}
func (n *network) MakeHTTPClient(dialFunc func(ctx context.Context,
network, address string) (net.Conn, error)) *http.Client {
network, address string) (essentials.Conn, error)) *http.Client {
if dialFunc == nil {
dialFunc = n.DialContext
}
@@ -144,13 +146,15 @@ func NewNetwork(dialer Dialer,
func makeHTTPClient(userAgent string,
timeout time.Duration,
dialFunc func(ctx context.Context, network, address string) (net.Conn, error)) *http.Client {
dialFunc func(ctx context.Context, network, address string) (essentials.Conn, error)) *http.Client {
return &http.Client{
Timeout: timeout,
Transport: networkHTTPTransport{
userAgent: userAgent,
next: &http.Transport{
DialContext: dialFunc,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
return dialFunc(ctx, network, address)
},
},
},
}
+11 -41
View File
@@ -3,45 +3,25 @@ package network
import (
"fmt"
"net"
"golang.org/x/sys/unix"
)
// SetClientSocketOptions tunes a TCP socket that represents a connection to
// end user (not Telegram service or fronting domain).
//
// bufferSize setting is deprecated and ignored.
func SetClientSocketOptions(conn net.Conn, bufferSize int) error {
tcpConn := conn.(*net.TCPConn) // nolint: forcetypeassert
if err := tcpConn.SetNoDelay(false); err != nil {
return fmt.Errorf("cannot disable TCP_NO_DELAY: %w", err)
}
return setCommonSocketOptions(tcpConn, bufferSize)
return setCommonSocketOptions(conn.(*net.TCPConn))
}
// SetServerSocketOptions tunes a TCP socket that represents a connection to
// remote server like Telegram or fronting domain (but not end user).
func SetServerSocketOptions(conn net.Conn, bufferSize int) error {
tcpConn := conn.(*net.TCPConn) // nolint: forcetypeassert
if err := tcpConn.SetNoDelay(true); err != nil {
return fmt.Errorf("cannot enable TCP_NO_DELAY: %w", err)
}
return setCommonSocketOptions(tcpConn, bufferSize)
return setCommonSocketOptions(conn.(*net.TCPConn))
}
func setCommonSocketOptions(conn *net.TCPConn, bufferSize int) error {
if err := conn.SetReadBuffer(bufferSize); err != nil {
return fmt.Errorf("cannot set read buffer size: %w", err)
}
if err := conn.SetWriteBuffer(bufferSize); err != nil {
return fmt.Errorf("cannot set write buffer size: %w", err)
}
if err := conn.SetKeepAlive(false); err != nil {
return fmt.Errorf("cannot disable TCP keepalive probes: %w", err)
func setCommonSocketOptions(conn *net.TCPConn) error {
if err := conn.SetKeepAlivePeriod(DefaultTCPKeepAlivePeriod); err != nil {
return fmt.Errorf("cannot set time period of TCP keepalive probes: %w", err)
}
if err := conn.SetLinger(tcpLingerTimeout); err != nil {
@@ -50,22 +30,12 @@ func setCommonSocketOptions(conn *net.TCPConn, bufferSize int) error {
rawConn, err := conn.SyscallConn()
if err != nil {
return fmt.Errorf("cannot get underlying raw connection")
return fmt.Errorf("cannot get underlying raw connection: %w", err)
}
rawConn.Control(func(fd uintptr) { // nolint: errcheck
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
err = fmt.Errorf("cannot set SO_REUSEADDR: %w", err)
return
}
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if err != nil {
err = fmt.Errorf("cannot set SO_REUSEPORT: %w", err)
}
})
if err := setSocketReuseAddrPort(rawConn); err != nil {
return fmt.Errorf("cannot setup SO_REUSEADDR/PORT: %w", err)
}
return nil
}
+31
View File
@@ -0,0 +1,31 @@
//go:build !windows
// +build !windows
package network
import (
"fmt"
"syscall"
"golang.org/x/sys/unix"
)
func setSocketReuseAddrPort(conn syscall.RawConn) error {
var err error
conn.Control(func(fd uintptr) { // nolint: errcheck
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
err = fmt.Errorf("cannot set SO_REUSEADDR: %w", err)
return
}
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if err != nil {
err = fmt.Errorf("cannot set SO_REUSEPORT: %w", err)
}
})
return err
}
+10
View File
@@ -0,0 +1,10 @@
//go:build windows
// +build windows
package network
import "syscall"
func setSocketReuseAddrPort(conn syscall.RawConn) error {
return nil
}
+146 -5
View File
@@ -1,21 +1,162 @@
package network
import (
"context"
"fmt"
"io"
"net"
"net/url"
"golang.org/x/net/proxy"
"github.com/9seconds/mtg/v2/essentials"
"github.com/txthinking/socks5"
)
type socks5Dialer struct {
Dialer
username []byte
password []byte
proxyAddress string
}
func (s socks5Dialer) Dial(network, address string) (essentials.Conn, error) {
return s.DialContext(context.Background(), network, address)
}
func (s socks5Dialer) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
switch network {
case "tcp", "tcp4", "tcp6":
default:
return nil, fmt.Errorf("%s network type is not supported", network)
}
conn, err := s.Dialer.DialContext(ctx, network, s.proxyAddress)
if err != nil {
return nil, fmt.Errorf("cannot dial to the proxy: %w", err)
}
if err := s.handshake(conn); err != nil {
conn.Close()
return nil, fmt.Errorf("cannot perform a handshake: %w", err)
}
if err := s.connect(conn, address); err != nil {
conn.Close()
return nil, fmt.Errorf("cannot connect to a destination host %s: %w", address, err)
}
return conn, nil
}
func (s socks5Dialer) handshake(conn io.ReadWriter) error {
authMethod := socks5.MethodUsernamePassword
if len(s.username)+len(s.password) == 0 {
authMethod = socks5.MethodNone
}
if err := s.handshakeNegotiation(conn, authMethod); err != nil {
return fmt.Errorf("cannot perform negotiation: %w", err)
}
if authMethod == socks5.MethodNone {
return nil
}
if err := s.handshakeAuth(conn); err != nil {
return fmt.Errorf("cannot authenticate: %w", err)
}
return nil
}
func (s socks5Dialer) handshakeNegotiation(conn io.ReadWriter, authMethod byte) error {
request := socks5.NewNegotiationRequest([]byte{authMethod})
if _, err := request.WriteTo(conn); err != nil {
return fmt.Errorf("cannot send request: %w", err)
}
response, err := socks5.NewNegotiationReplyFrom(conn)
if err != nil {
return fmt.Errorf("cannot read response: %w", err)
}
if response.Method != authMethod {
return fmt.Errorf("%v is unsupported auth method", authMethod)
}
return nil
}
func (s socks5Dialer) handshakeAuth(conn io.ReadWriter) error {
request := socks5.NewUserPassNegotiationRequest(s.username, s.password)
if _, err := request.WriteTo(conn); err != nil {
return fmt.Errorf("cannot send a request: %w", err)
}
response, err := socks5.NewUserPassNegotiationReplyFrom(conn)
if err != nil {
return fmt.Errorf("cannot read a response: %w", err)
}
if response.Status != socks5.UserPassStatusSuccess {
return fmt.Errorf("authenticate has failed: %v", response.Status)
}
return nil
}
func (s socks5Dialer) connect(conn io.ReadWriter, address string) error {
addrType, host, port, err := socks5.ParseAddress(address)
if err != nil {
return fmt.Errorf("cannot parse address: %w", err)
}
if addrType == socks5.ATYPDomain {
host = host[1:]
}
request := socks5.NewRequest(socks5.CmdConnect, addrType, host, port)
if _, err := request.WriteTo(conn); err != nil {
return fmt.Errorf("cannot send a request: %w", err)
}
response, err := socks5.NewReplyFrom(conn)
if err != nil {
return fmt.Errorf("cannot read a response: %w", err)
}
if response.Rep != socks5.RepSuccess {
return fmt.Errorf("unsuccessful request: %v", response.Rep)
}
return nil
}
// NewSocks5Dialer build a new dialer from a given one (so, in theory
// you can chain here). Proxy parameters are passed with URI in a form of:
//
// socks5://[user:[password]]@host:port
func NewSocks5Dialer(baseDialer Dialer, proxyURL *url.URL) (Dialer, error) {
rv, err := proxy.FromURL(proxyURL, baseDialer)
if err != nil {
return nil, fmt.Errorf("cannot initialize socks5 proxy dialer: %w", err)
if _, _, err := net.SplitHostPort(proxyURL.Host); err != nil {
return nil, fmt.Errorf("incorrect url %s", proxyURL.Redacted())
}
return rv.(Dialer), nil
dialer := socks5Dialer{
Dialer: baseDialer,
proxyAddress: proxyURL.Host,
}
if proxyURL.User != nil {
password, isSet := proxyURL.User.Password()
if isSet {
dialer.username = []byte(proxyURL.User.Username())
dialer.password = []byte(password)
}
}
return dialer, nil
}
+1 -1
View File
@@ -55,7 +55,7 @@ func (suite *Socks5TestSuite) TestRequestOk() {
suite.Equal(http.StatusOK, resp.StatusCode)
}
func TestSocks5TestSuite(t *testing.T) {
func TestSocks5(t *testing.T) {
t.Parallel()
suite.Run(t, &Socks5TestSuite{})
}