mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-09-01 01:24:02 +03:00
REPOSITORY / ScuroNeko/mtg
Compare commits
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c07e3d573d | ||
|
|
f192a718f4 | ||
|
|
0c4523d2c8 | ||
|
|
b0063a4a25 | ||
|
|
6dc494e7d0 | ||
|
|
534d5b755e | ||
|
|
aa7e488a3a | ||
|
|
eba3673e27 | ||
|
|
a27facaa16 | ||
|
|
78a9ff18a5 | ||
|
|
c0899d0743 | ||
|
|
12563219e6 | ||
|
|
59b5ff4080 | ||
|
|
d7e554540d | ||
|
|
1575b82688 | ||
|
|
5df1f594cc | ||
|
|
02ad052c49 | ||
|
|
2b8c7825ca | ||
|
|
75357b3e3c | ||
|
|
d8b14dc765 | ||
|
|
e0850869ba | ||
|
|
9375552180 | ||
|
|
cbe5b8c94e | ||
|
|
63b425f245 | ||
|
|
b53ead4372 | ||
|
|
2cdd66c722 | ||
|
|
2ae0101d18 | ||
|
|
6da9c2e58d | ||
|
|
6d89f14c9b | ||
|
|
442e2da330 | ||
|
|
a9b3560b25 | ||
|
|
880dd04728 | ||
|
|
8ea1aa3f5e | ||
|
|
4e5a83cfe9 | ||
|
|
5282ca26f3 | ||
|
|
2077db1f1e | ||
|
|
1a9bc80091 | ||
|
|
30170b9413 | ||
|
|
4687a7c899 | ||
|
|
d467fba674 | ||
|
|
6e447b1d59 | ||
|
|
b15a8ec4a5 | ||
|
|
6bef4df091 | ||
|
|
0ce0c668b9 | ||
|
|
01e201365b | ||
|
|
0b52367a82 | ||
|
|
25c842daf1 | ||
|
|
4c543aaea2 |
@@ -38,12 +38,11 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
timeout-minutes: 10
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go_version:
|
go_version:
|
||||||
- ~1.16
|
- ^1.18
|
||||||
- ^1.17
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -70,6 +69,38 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
file: ./coverage.txt
|
file: ./coverage.txt
|
||||||
|
|
||||||
|
fuzz:
|
||||||
|
name: Fuzzing
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ^1.18
|
||||||
|
|
||||||
|
- name: Cache fuzz results
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cache/go-build/fuzz
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/*_fuzz_test.go', '**/*_fuzz_internal_test.go') }}
|
||||||
|
restore-keys: ${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: ${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Run fuzzing
|
||||||
|
run: make -j4 fuzz
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -80,10 +111,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Run linter
|
- name: Setup Go
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
version: v1.43.0
|
go-version: ^1.18
|
||||||
|
|
||||||
|
- name: Run linter
|
||||||
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
with:
|
||||||
|
version: v1.45.0
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
name: Docker
|
name: Docker
|
||||||
|
|||||||
+1
-1
@@ -9,4 +9,4 @@ format = "colored-line-number"
|
|||||||
|
|
||||||
[linters]
|
[linters]
|
||||||
enable-all = true
|
enable-all = true
|
||||||
disable = ["ireturn", "varnamelen", "gochecknoglobals", "gas", "goerr113", "exhaustivestruct"]
|
disable = ["thelper", "ireturn", "varnamelen", "gochecknoglobals", "gas", "goerr113", "exhaustivestruct", "containedctx"]
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# BUILD STAGE
|
# BUILD STAGE
|
||||||
|
|
||||||
FROM golang:1.17-alpine AS build
|
FROM golang:1.18-alpine AS build
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apk --no-cache --update add \
|
&& apk --no-cache --update add \
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
|||||||
IMAGE_NAME := mtg
|
IMAGE_NAME := mtg
|
||||||
APP_NAME := $(IMAGE_NAME)
|
APP_NAME := $(IMAGE_NAME)
|
||||||
|
|
||||||
GOLANGCI_LINT_VERSION := v1.43.0
|
GOLANGCI_LINT_VERSION := v1.45.0
|
||||||
|
|
||||||
VERSION_GO := $(shell go version)
|
VERSION := $(shell git describe --exact-match HEAD 2>/dev/null || git describe --tags --always)
|
||||||
VERSION_DATE := $(shell date -Ru)
|
COMMON_BUILD_FLAGS := -trimpath -mod=readonly -ldflags="-extldflags '-static' -s -w -X 'main.version=$(VERSION)'"
|
||||||
VERSION_TAG := $(shell git describe --tags --always)
|
|
||||||
COMMON_BUILD_FLAGS := -trimpath -mod=readonly -ldflags="-extldflags '-static' -s -w -X 'main.version=$(VERSION_TAG) ($(VERSION_GO)) [$(VERSION_DATE)]'"
|
FUZZ_FLAGS := -fuzztime=120s
|
||||||
|
|
||||||
GOBIN := $(ROOT_DIR)/.bin
|
GOBIN := $(ROOT_DIR)/.bin
|
||||||
GOTOOL := env "GOBIN=$(GOBIN)" "PATH=$(ROOT_DIR)/.bin:$(PATH)"
|
GOTOOL := env "GOBIN=$(GOBIN)" "PATH=$(ROOT_DIR)/.bin:$(PATH)"
|
||||||
@@ -78,7 +78,7 @@ install-tools: install-tools-lint install-tools-godoc install-tools-gofumpt inst
|
|||||||
|
|
||||||
.PHONY: install-tools-lint
|
.PHONY: install-tools-lint
|
||||||
install-tools-lint: .bin
|
install-tools-lint: .bin
|
||||||
@curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh \
|
@curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
|
||||||
| bash -s -- -b "$(GOBIN)" "$(GOLANGCI_LINT_VERSION)"
|
| bash -s -- -b "$(GOBIN)" "$(GOLANGCI_LINT_VERSION)"
|
||||||
|
|
||||||
.PHONY: install-tools-godoc
|
.PHONY: install-tools-godoc
|
||||||
@@ -95,4 +95,27 @@ install-tools-goreleaser: .bin
|
|||||||
|
|
||||||
.PHONY: update-deps
|
.PHONY: update-deps
|
||||||
update-deps:
|
update-deps:
|
||||||
@go get -u && go mod tidy -go=1.17
|
@go get -u && go mod tidy -go=1.18
|
||||||
|
|
||||||
|
.PHONY: fuzz
|
||||||
|
fuzz: fuzz-ClientHello fuzz-ServerGenerateHandshakeFrame fuzz-ClientHandshake fuzz-ServerReceive fuzz-ServerSend
|
||||||
|
|
||||||
|
.PHONY: fuzz-ClientHello
|
||||||
|
fuzz-ClientHello:
|
||||||
|
@go test -fuzz=FuzzClientHello $(FUZZ_FLAGS) "$(ROOT_DIR)/mtglib/internal/faketls"
|
||||||
|
|
||||||
|
.PHONY: fuzz-ServerGenerateHandshakeFrame
|
||||||
|
fuzz-ServerGenerateHandshakeFrame:
|
||||||
|
@go test -fuzz=FuzzServerGenerateHandshakeFrame $(FUZZ_FLAGS) "$(ROOT_DIR)/mtglib/internal/obfuscated2"
|
||||||
|
|
||||||
|
.PHONY: fuzz-ClientHandshake
|
||||||
|
fuzz-ClientHandshake:
|
||||||
|
@go test -fuzz=FuzzClientHandshake $(FUZZ_FLAGS) "$(ROOT_DIR)/mtglib/internal/obfuscated2"
|
||||||
|
|
||||||
|
.PHONY: fuzz-ServerReceive
|
||||||
|
fuzz-ServerReceive:
|
||||||
|
@go test -fuzz=FuzzServerReceive $(FUZZ_FLAGS) "$(ROOT_DIR)/mtglib/internal/obfuscated2"
|
||||||
|
|
||||||
|
.PHONY: fuzz-ServerSend
|
||||||
|
fuzz-ServerSend:
|
||||||
|
@go test -fuzz=FuzzServerSend $(FUZZ_FLAGS) "$(ROOT_DIR)/mtglib/internal/obfuscated2"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# mtg
|
# mtg
|
||||||
|
|
||||||
Highly-opionated (ex-bullshit-free) MTPROTO proxy for
|
Highly-opinionated (ex-bullshit-free) MTPROTO proxy for
|
||||||
[Telegram](https://telegram.org/).
|
[Telegram](https://telegram.org/).
|
||||||
|
|
||||||
[](https://github.com/9seconds/mtg/actions/workflows/ci.yaml)
|
[](https://github.com/9seconds/mtg/actions/workflows/ci.yaml)
|
||||||
@@ -224,6 +224,16 @@ $ mtg generate-secret --hex google.com
|
|||||||
ee473ce5d4958eb5f968c87680a23854a0676f6f676c652e636f6d
|
ee473ce5d4958eb5f968c87680a23854a0676f6f676c652e636f6d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
equivalent commands with docker:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker run --rm nineseconds/mtg:2 generate-secret google.com
|
||||||
|
7ibaERuTSGPH1RdztfYnN4tnb29nbGUuY29t
|
||||||
|
|
||||||
|
$ docker run --rm nineseconds/mtg:2 generate-secret --hex google.com
|
||||||
|
ee473ce5d4958eb5f968c87680a23854a0676f6f676c652e636f6d
|
||||||
|
```
|
||||||
|
|
||||||
This secret is a keystone for a proxy and your password for a client.
|
This secret is a keystone for a proxy and your password for a client.
|
||||||
You need to keep it secured.
|
You need to keep it secured.
|
||||||
|
|
||||||
@@ -307,12 +317,16 @@ Now you can create a systemd unit:
|
|||||||
```console
|
```console
|
||||||
$ cat /etc/systemd/system/mtg.service
|
$ cat /etc/systemd/system/mtg.service
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=mtg
|
Description=mtg - MTProto proxy server
|
||||||
|
Documentation=https://github.com/9seconds/mtg
|
||||||
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/local/bin/mtg run /etc/mtg.toml
|
ExecStart=/usr/local/bin/mtg run /etc/mtg.toml
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=3
|
RestartSec=3
|
||||||
|
DynamicUser=true
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
@@ -324,7 +338,7 @@ $ sudo systemctl start mtg
|
|||||||
or you can run a docker image
|
or you can run a docker image
|
||||||
|
|
||||||
```console
|
```console
|
||||||
docker run -d -v /etc/mtg.toml:/config.toml -p 443:3128 --restart=unless-stopped nineseconds/mtg:2
|
docker run -d -v /etc/mtg.toml:/config.toml -p 443:3128 --name mtg-proxy --restart=unless-stopped nineseconds/mtg:2
|
||||||
```
|
```
|
||||||
|
|
||||||
where _443_ is a host port (a port you want to connect to from a
|
where _443_ is a host port (a port you want to connect to from a
|
||||||
@@ -353,6 +367,12 @@ $ mtg access /etc/mtg.toml
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or if you are using docker:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker exec mtg-proxy /mtg access /config.toml
|
||||||
|
```
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
|
||||||
Out of the box, mtg works with
|
Out of the box, mtg works with
|
||||||
@@ -367,11 +387,12 @@ Here goes a list of metrics with their types but without a prefix.
|
|||||||
| client_connections | gauge | `ip_family` | Count of processing client connections. |
|
| client_connections | gauge | `ip_family` | Count of processing client connections. |
|
||||||
| telegram_connections | gauge | `telegram_ip`, `dc` | Count of connections to Telegram servers. |
|
| telegram_connections | gauge | `telegram_ip`, `dc` | Count of connections to Telegram servers. |
|
||||||
| domain_fronting_connections | gauge | `ip_family` | Count of connections to fronting domain. |
|
| domain_fronting_connections | gauge | `ip_family` | Count of connections to fronting domain. |
|
||||||
|
| iplist_size | gauge | `ip_list` | A size of either allowlist or blocklist in use. |
|
||||||
| telegram_traffic | counter | `telegram_ip`, `dc`, `direction` | Count of bytes, transmitted to/from Telegram. |
|
| telegram_traffic | counter | `telegram_ip`, `dc`, `direction` | Count of bytes, transmitted to/from Telegram. |
|
||||||
| domain_fronting_traffic | counter | `direction` | Count of bytes, transmitted to/from fronting domain. |
|
| domain_fronting_traffic | counter | `direction` | Count of bytes, transmitted to/from fronting domain. |
|
||||||
| domain_fronting | counter | – | Count of domain fronting events. |
|
| domain_fronting | counter | – | Count of domain fronting events. |
|
||||||
| concurrency_limited | counter | – | Count of events, when client connection was rejected due to concurrency limit. |
|
| concurrency_limited | counter | – | Count of events, when client connection was rejected due to concurrency limit. |
|
||||||
| ip_blocklisted | counter | – | Count of events when client connection was rejected because IP was found in the blacklist. |
|
| ip_blocklisted | counter | `ip_list` | Count of events when client connection was rejected because IP was found in the blocklist. |
|
||||||
| replay_attacks | counter | – | Count of detected replay attacks. |
|
| replay_attacks | counter | – | Count of detected replay attacks. |
|
||||||
|
|
||||||
Tag meaning:
|
Tag meaning:
|
||||||
@@ -382,3 +403,4 @@ Tag meaning:
|
|||||||
| dc | | A number of the Telegram DC for a connection. |
|
| dc | | A number of the Telegram DC for a connection. |
|
||||||
| telegram_ip | | IP address of the Telegram server. |
|
| telegram_ip | | IP address of the Telegram server. |
|
||||||
| direction | `to_client`, `from_client` | A direction of the traffic flow. |
|
| direction | `to_client`, `from_client` | A direction of the traffic flow. |
|
||||||
|
| ip_list | `allowlist`, `blocklist` | A type of the IP list. |
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ func eventStreamProcessor(ctx context.Context, eventChan <-chan mtglib.Event, ob
|
|||||||
observer.EventConcurrencyLimited(typedEvt)
|
observer.EventConcurrencyLimited(typedEvt)
|
||||||
case mtglib.EventReplayAttack:
|
case mtglib.EventReplayAttack:
|
||||||
observer.EventReplayAttack(typedEvt)
|
observer.EventReplayAttack(typedEvt)
|
||||||
|
case mtglib.EventIPListSize:
|
||||||
|
observer.EventIPListSize(typedEvt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,6 +204,27 @@ func (suite *EventStreamTestSuite) TestEventReplayAttack() {
|
|||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *EventStreamTestSuite) TestEventIPListSize() {
|
||||||
|
evt := mtglib.NewEventIPListSize(10, true)
|
||||||
|
|
||||||
|
for _, v := range []*ObserverMock{suite.observerMock1, suite.observerMock2} {
|
||||||
|
v.
|
||||||
|
On("EventIPListSize", mock.Anything).
|
||||||
|
Once().
|
||||||
|
Run(func(args mock.Arguments) {
|
||||||
|
caught, ok := args.Get(0).(mtglib.EventIPListSize)
|
||||||
|
|
||||||
|
suite.True(ok)
|
||||||
|
suite.Equal(evt.Timestamp(), caught.Timestamp())
|
||||||
|
suite.Equal(evt.Size, caught.Size)
|
||||||
|
suite.Equal(evt.IsBlockList, caught.IsBlockList)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.stream.Send(suite.ctx, evt)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *EventStreamTestSuite) TearDownTest() {
|
func (suite *EventStreamTestSuite) TearDownTest() {
|
||||||
suite.stream.Shutdown()
|
suite.stream.Shutdown()
|
||||||
suite.ctxCancel()
|
suite.ctxCancel()
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ type Observer interface {
|
|||||||
// EventReplayAttack reacts on incoming mtglib.EventReplayAttack event.
|
// EventReplayAttack reacts on incoming mtglib.EventReplayAttack event.
|
||||||
EventReplayAttack(mtglib.EventReplayAttack)
|
EventReplayAttack(mtglib.EventReplayAttack)
|
||||||
|
|
||||||
|
// EventIPListSize reacts on incoming mtglib.EventIPListSize
|
||||||
|
EventIPListSize(mtglib.EventIPListSize)
|
||||||
|
|
||||||
// Shutdown stop observer. Default event stream guarantees:
|
// Shutdown stop observer. Default event stream guarantees:
|
||||||
// 1. If shutdown is executed, it is executed only once
|
// 1. If shutdown is executed, it is executed only once
|
||||||
// 2. Observer won't receieve any new message after this
|
// 2. Observer won't receieve any new message after this
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ func (o *ObserverMock) EventReplayAttack(evt mtglib.EventReplayAttack) {
|
|||||||
o.Called(evt)
|
o.Called(evt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *ObserverMock) EventIPListSize(evt mtglib.EventIPListSize) {
|
||||||
|
o.Called(evt)
|
||||||
|
}
|
||||||
|
|
||||||
func (o *ObserverMock) Shutdown() {
|
func (o *ObserverMock) Shutdown() {
|
||||||
o.Called()
|
o.Called()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,21 @@ func (m multiObserver) EventReplayAttack(evt mtglib.EventReplayAttack) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m multiObserver) EventIPListSize(evt mtglib.EventIPListSize) {
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(len(m.observers))
|
||||||
|
|
||||||
|
for _, v := range m.observers {
|
||||||
|
go func(obs Observer) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
obs.EventIPListSize(evt)
|
||||||
|
}(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
func (m multiObserver) Shutdown() {
|
func (m multiObserver) Shutdown() {
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
v.Shutdown()
|
v.Shutdown()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ func (n noopObserver) EventFinish(_ mtglib.EventFinish)
|
|||||||
func (n noopObserver) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimited) {}
|
func (n noopObserver) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimited) {}
|
||||||
func (n noopObserver) EventIPBlocklisted(_ mtglib.EventIPBlocklisted) {}
|
func (n noopObserver) EventIPBlocklisted(_ mtglib.EventIPBlocklisted) {}
|
||||||
func (n noopObserver) EventReplayAttack(_ mtglib.EventReplayAttack) {}
|
func (n noopObserver) EventReplayAttack(_ mtglib.EventReplayAttack) {}
|
||||||
|
func (n noopObserver) EventIPListSize(_ mtglib.EventIPListSize) {}
|
||||||
func (n noopObserver) Shutdown() {}
|
func (n noopObserver) Shutdown() {}
|
||||||
|
|
||||||
// NewNoopObserver creates an observer which discards each message.
|
// NewNoopObserver creates an observer which discards each message.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ func (suite *NoopTestSuite) SetupSuite() {
|
|||||||
"concurrency-limited": mtglib.NewEventConcurrencyLimited(),
|
"concurrency-limited": mtglib.NewEventConcurrencyLimited(),
|
||||||
"ip-blacklisted": mtglib.NewEventIPBlocklisted(net.ParseIP("10.0.0.10")),
|
"ip-blacklisted": mtglib.NewEventIPBlocklisted(net.ParseIP("10.0.0.10")),
|
||||||
"replay-attack": mtglib.NewEventReplayAttack("connID"),
|
"replay-attack": mtglib.NewEventReplayAttack("connID"),
|
||||||
|
"ip-list-size": mtglib.NewEventIPListSize(10, true),
|
||||||
}
|
}
|
||||||
suite.ctx = context.Background()
|
suite.ctx = context.Background()
|
||||||
}
|
}
|
||||||
@@ -65,6 +66,8 @@ func (suite *NoopTestSuite) TestObserver() {
|
|||||||
observer.EventIPBlocklisted(typedEvt)
|
observer.EventIPBlocklisted(typedEvt)
|
||||||
case mtglib.EventReplayAttack:
|
case mtglib.EventReplayAttack:
|
||||||
observer.EventReplayAttack(typedEvt)
|
observer.EventReplayAttack(typedEvt)
|
||||||
|
case mtglib.EventIPListSize:
|
||||||
|
observer.EventIPListSize(typedEvt)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -170,7 +170,7 @@ download-concurrency = 2
|
|||||||
# You can provider links here (starts with https:// or http://) or
|
# You can provider links here (starts with https:// or http://) or
|
||||||
# path to a local file, but in this case it should be absolute.
|
# path to a local file, but in this case it should be absolute.
|
||||||
urls = [
|
urls = [
|
||||||
# "https://iplists.firehol.org/files/firehol_level1.netset",
|
"https://iplists.firehol.org/files/firehol_level1.netset",
|
||||||
# "/local.file"
|
# "/local.file"
|
||||||
]
|
]
|
||||||
# How often do we need to update a blocklist set.
|
# How often do we need to update a blocklist set.
|
||||||
|
|||||||
@@ -1,36 +1,38 @@
|
|||||||
module github.com/9seconds/mtg/v2
|
module github.com/9seconds/mtg/v2
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/OneOfOne/xxhash v1.2.8
|
github.com/OneOfOne/xxhash v1.2.8
|
||||||
github.com/alecthomas/kong v0.2.19
|
github.com/alecthomas/kong v0.5.0
|
||||||
github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||||
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
|
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
|
||||||
github.com/d4l3k/messagediff v1.2.1 // indirect
|
github.com/d4l3k/messagediff v1.2.1 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/gotd/td v0.34.0
|
github.com/gotd/td v0.34.0
|
||||||
github.com/jarcoal/httpmock v1.0.8
|
github.com/jarcoal/httpmock v1.0.8
|
||||||
github.com/kentik/patricia v0.0.0-20210909164817-21603333b70e
|
|
||||||
github.com/mccutchen/go-httpbin v1.1.1
|
github.com/mccutchen/go-httpbin v1.1.1
|
||||||
github.com/panjf2000/ants/v2 v2.4.7
|
github.com/panjf2000/ants/v2 v2.4.8
|
||||||
github.com/pelletier/go-toml v1.9.4
|
github.com/pelletier/go-toml v1.9.4
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.12.1
|
||||||
github.com/prometheus/common v0.32.1 // indirect
|
github.com/prometheus/common v0.32.1 // indirect
|
||||||
github.com/prometheus/procfs v0.7.3 // indirect
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
github.com/rs/zerolog v1.26.0
|
github.com/rs/zerolog v1.26.1
|
||||||
github.com/smira/go-statsd v1.3.2
|
github.com/smira/go-statsd v1.3.2
|
||||||
github.com/stretchr/objx v0.3.0 // indirect
|
github.com/stretchr/objx v0.3.0 // indirect
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43
|
github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43
|
||||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e
|
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/txthinking/socks5 v0.0.0-20211121111206-e03c1217a50b
|
require (
|
||||||
|
github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a
|
||||||
|
github.com/yl2chen/cidranger v1.0.2
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
@@ -44,7 +46,7 @@ require (
|
|||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
|
github.com/txthinking/runnergroup v0.0.0-20220212043759-8da8edb7dae8 // indirect
|
||||||
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect
|
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
|||||||
@@ -37,15 +37,17 @@ 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 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||||
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
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/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
github.com/alecthomas/kong v0.2.19 h1:qBDfByO5XgWUXyNB4D6OOhGh5Z1eNOwWayDPQJFNWdc=
|
github.com/alecthomas/kong v0.5.0 h1:u8Kdw+eeml93qtMZ04iei0CFYve/WPcA5IFh+9wSskE=
|
||||||
github.com/alecthomas/kong v0.2.19/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ=
|
github.com/alecthomas/kong v0.5.0/go.mod h1:uzxf/HUh0tj43x1AyJROl3JT7SgsZ5m+icOv1csRhc0=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/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-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-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-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||||
github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
|
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/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
@@ -163,14 +165,13 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX
|
|||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/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/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/k0kubun/pp v2.4.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
|
||||||
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@@ -191,10 +192,11 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/panjf2000/ants/v2 v2.4.7 h1:MZnw2JRyTJxFwtaMtUJcwE618wKD04POWk2gwwP4E2M=
|
github.com/panjf2000/ants/v2 v2.4.8 h1:JgTbolX6K6RreZ4+bfctI0Ifs+3mrE5BIHudQxUDQ9k=
|
||||||
github.com/panjf2000/ants/v2 v2.4.7/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
|
github.com/panjf2000/ants/v2 v2.4.8/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 h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
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 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||||
@@ -209,8 +211,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
|
|
||||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
|
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||||
|
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
@@ -231,8 +234,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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
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/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
|
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
|
||||||
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
|
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
|
||||||
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
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.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
@@ -245,21 +248,23 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v1.1.5-0.20170809224252-890a5c3458b4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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/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/runnergroup v0.0.0-20220212043759-8da8edb7dae8 h1:iYc+JnXtzv6sdMx9Q7OTKkDAn7FhDPDogcjeSfEQcLY=
|
||||||
github.com/txthinking/socks5 v0.0.0-20211121111206-e03c1217a50b/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg=
|
github.com/txthinking/runnergroup v0.0.0-20220212043759-8da8edb7dae8/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM=
|
||||||
|
github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a h1:BOqgJ4jku0LHPDoR51RD8Mxmo0LHxCzJT/M9MemYdHo=
|
||||||
|
github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg=
|
||||||
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A=
|
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/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 h1:QEePdg0ty2r0t1+qwfZmQ4OOl/MB2UXIeJSpIZv56lg=
|
||||||
github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM=
|
github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM=
|
||||||
|
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
||||||
|
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
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.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@@ -285,8 +290,9 @@ 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-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-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8=
|
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||||
|
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -350,6 +356,7 @@ 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-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-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-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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/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 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||||
@@ -409,10 +416,10 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210809222454-d867a43fc93e/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
|
||||||
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
|||||||
+71
-19
@@ -1,6 +1,7 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -11,11 +12,13 @@ import (
|
|||||||
"github.com/9seconds/mtg/v2/internal/config"
|
"github.com/9seconds/mtg/v2/internal/config"
|
||||||
"github.com/9seconds/mtg/v2/internal/utils"
|
"github.com/9seconds/mtg/v2/internal/utils"
|
||||||
"github.com/9seconds/mtg/v2/ipblocklist"
|
"github.com/9seconds/mtg/v2/ipblocklist"
|
||||||
|
"github.com/9seconds/mtg/v2/ipblocklist/files"
|
||||||
"github.com/9seconds/mtg/v2/logger"
|
"github.com/9seconds/mtg/v2/logger"
|
||||||
"github.com/9seconds/mtg/v2/mtglib"
|
"github.com/9seconds/mtg/v2/mtglib"
|
||||||
"github.com/9seconds/mtg/v2/network"
|
"github.com/9seconds/mtg/v2/network"
|
||||||
"github.com/9seconds/mtg/v2/stats"
|
"github.com/9seconds/mtg/v2/stats"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/yl2chen/cidranger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeLogger(conf *config.Config) mtglib.Logger {
|
func makeLogger(conf *config.Config) mtglib.Logger {
|
||||||
@@ -85,7 +88,11 @@ func makeAntiReplayCache(conf *config.Config) mtglib.AntiReplayCache {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeIPBlocklist(conf config.ListConfig, logger mtglib.Logger, ntw mtglib.Network) (mtglib.IPBlocklist, error) {
|
func makeIPBlocklist(conf config.ListConfig,
|
||||||
|
logger mtglib.Logger,
|
||||||
|
ntw mtglib.Network,
|
||||||
|
updateCallback ipblocklist.FireholUpdateCallback,
|
||||||
|
) (mtglib.IPBlocklist, error) {
|
||||||
if !conf.Enabled.Get(false) {
|
if !conf.Enabled.Get(false) {
|
||||||
return ipblocklist.NewNoop(), nil
|
return ipblocklist.NewNoop(), nil
|
||||||
}
|
}
|
||||||
@@ -101,16 +108,54 @@ func makeIPBlocklist(conf config.ListConfig, logger mtglib.Logger, ntw mtglib.Ne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
firehol, err := ipblocklist.NewFirehol(logger.Named("ipblockist"),
|
blocklist, err := ipblocklist.NewFirehol(logger.Named("ipblockist"),
|
||||||
ntw,
|
ntw,
|
||||||
conf.DownloadConcurrency.Get(1),
|
conf.DownloadConcurrency.Get(1),
|
||||||
remoteURLs,
|
remoteURLs,
|
||||||
localFiles)
|
localFiles,
|
||||||
|
updateCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("incorrect parameters for firehol: %w", err)
|
return nil, fmt.Errorf("incorrect parameters for firehol: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return firehol, nil
|
go blocklist.Run(conf.UpdateEach.Get(ipblocklist.DefaultFireholUpdateEach))
|
||||||
|
|
||||||
|
return blocklist, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeIPAllowlist(conf config.ListConfig,
|
||||||
|
logger mtglib.Logger,
|
||||||
|
ntw mtglib.Network,
|
||||||
|
updateCallback ipblocklist.FireholUpdateCallback,
|
||||||
|
) (allowlist mtglib.IPBlocklist, err error) {
|
||||||
|
if !conf.Enabled.Get(false) {
|
||||||
|
allowlist, err = ipblocklist.NewFireholFromFiles(
|
||||||
|
logger.Named("ipblocklist"),
|
||||||
|
1,
|
||||||
|
[]files.File{
|
||||||
|
files.NewMem([]*net.IPNet{
|
||||||
|
cidranger.AllIPv4,
|
||||||
|
cidranger.AllIPv6,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
updateCallback,
|
||||||
|
)
|
||||||
|
|
||||||
|
go allowlist.Run(conf.UpdateEach.Get(ipblocklist.DefaultFireholUpdateEach))
|
||||||
|
} else {
|
||||||
|
allowlist, err = makeIPBlocklist(
|
||||||
|
conf,
|
||||||
|
logger,
|
||||||
|
ntw,
|
||||||
|
updateCallback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot build allowlist: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowlist, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeEventStream(conf *config.Config, logger mtglib.Logger) (mtglib.EventStream, error) {
|
func makeEventStream(conf *config.Config, logger mtglib.Logger) (mtglib.EventStream, error) {
|
||||||
@@ -157,30 +202,37 @@ func runProxy(conf *config.Config, version string) error { // nolint: funlen
|
|||||||
|
|
||||||
logger.BindJSON("configuration", conf.String()).Debug("configuration")
|
logger.BindJSON("configuration", conf.String()).Debug("configuration")
|
||||||
|
|
||||||
|
eventStream, err := makeEventStream(conf, logger)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot build event stream: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
ntw, err := makeNetwork(conf, version)
|
ntw, err := makeNetwork(conf, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot build network: %w", err)
|
return fmt.Errorf("cannot build network: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blocklist, err := makeIPBlocklist(conf.Defense.Blocklist, logger, ntw)
|
blocklist, err := makeIPBlocklist(
|
||||||
|
conf.Defense.Blocklist,
|
||||||
|
logger.Named("blocklist"),
|
||||||
|
ntw,
|
||||||
|
func(ctx context.Context, size int) {
|
||||||
|
eventStream.Send(ctx, mtglib.NewEventIPListSize(size, true))
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot build ip blocklist: %w", err)
|
return fmt.Errorf("cannot build ip blocklist: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var whitelist mtglib.IPBlocklist
|
allowlist, err := makeIPAllowlist(
|
||||||
|
conf.Defense.Allowlist,
|
||||||
if conf.Defense.Allowlist.Enabled.Get(false) {
|
logger.Named("allowlist"),
|
||||||
whlist, err := makeIPBlocklist(conf.Defense.Allowlist, logger, ntw)
|
ntw,
|
||||||
|
func(ctx context.Context, size int) {
|
||||||
|
eventStream.Send(ctx, mtglib.NewEventIPListSize(size, false))
|
||||||
|
},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot build ip blocklist: %w", err)
|
return fmt.Errorf("cannot build ip allowlist: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
whitelist = whlist
|
|
||||||
}
|
|
||||||
|
|
||||||
eventStream, err := makeEventStream(conf, logger)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot build event stream: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := mtglib.ProxyOpts{
|
opts := mtglib.ProxyOpts{
|
||||||
@@ -188,7 +240,7 @@ func runProxy(conf *config.Config, version string) error { // nolint: funlen
|
|||||||
Network: ntw,
|
Network: ntw,
|
||||||
AntiReplayCache: makeAntiReplayCache(conf),
|
AntiReplayCache: makeAntiReplayCache(conf),
|
||||||
IPBlocklist: blocklist,
|
IPBlocklist: blocklist,
|
||||||
IPWhitelist: whitelist,
|
IPAllowlist: allowlist,
|
||||||
EventStream: eventStream,
|
EventStream: eventStream,
|
||||||
|
|
||||||
Secret: conf.Secret,
|
Secret: conf.Secret,
|
||||||
|
|||||||
@@ -15,16 +15,17 @@ type MtglibNetworkMock struct {
|
|||||||
func (m *MtglibNetworkMock) Dial(network, address string) (essentials.Conn, error) {
|
func (m *MtglibNetworkMock) Dial(network, address string) (essentials.Conn, error) {
|
||||||
args := m.Called(network, address)
|
args := m.Called(network, address)
|
||||||
|
|
||||||
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
|
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck, forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MtglibNetworkMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
|
func (m *MtglibNetworkMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
|
||||||
args := m.Called(ctx, network, address)
|
args := m.Called(ctx, network, address)
|
||||||
|
|
||||||
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
|
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck, forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MtglibNetworkMock) MakeHTTPClient(dialFunc func(ctx context.Context,
|
func (m *MtglibNetworkMock) MakeHTTPClient(dialFunc func(ctx context.Context,
|
||||||
network, address string) (essentials.Conn, error)) *http.Client {
|
network, address string) (essentials.Conn, error),
|
||||||
return m.Called(dialFunc).Get(0).(*http.Client)
|
) *http.Client {
|
||||||
|
return m.Called(dialFunc).Get(0).(*http.Client) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ func (n *EssentialsConnMock) CloseWrite() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *EssentialsConnMock) LocalAddr() net.Addr {
|
func (n *EssentialsConnMock) LocalAddr() net.Addr {
|
||||||
return n.Called().Get(0).(net.Addr)
|
return n.Called().Get(0).(net.Addr) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *EssentialsConnMock) RemoteAddr() net.Addr {
|
func (n *EssentialsConnMock) RemoteAddr() net.Addr {
|
||||||
return n.Called().Get(0).(net.Addr)
|
return n.Called().Get(0).(net.Addr) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *EssentialsConnMock) SetDeadline(t time.Time) error {
|
func (n *EssentialsConnMock) SetDeadline(t time.Time) error {
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type memFile struct {
|
||||||
|
data string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m memFile) Open(ctx context.Context) (io.ReadCloser, error) {
|
||||||
|
return io.NopCloser(strings.NewReader(m.data)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m memFile) String() string {
|
||||||
|
return "mem"
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMem(networks []*net.IPNet) File {
|
||||||
|
builder := strings.Builder{}
|
||||||
|
|
||||||
|
if len(networks) > 0 {
|
||||||
|
builder.WriteString(networks[0].String())
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < len(networks); i++ {
|
||||||
|
builder.WriteString("\n")
|
||||||
|
builder.WriteString(networks[i].String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return memFile{
|
||||||
|
data: builder.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package files_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/v2/ipblocklist/files"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MemTestSuite) TestOk() {
|
||||||
|
_, network1, _ := net.ParseCIDR("192.168.0.1/24")
|
||||||
|
_, network2, _ := net.ParseCIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/36")
|
||||||
|
|
||||||
|
file := files.NewMem([]*net.IPNet{
|
||||||
|
network1,
|
||||||
|
network2,
|
||||||
|
})
|
||||||
|
|
||||||
|
reader, err := file.Open(context.Background())
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
data, err := io.ReadAll(reader)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
strData := strings.TrimSpace(string(data))
|
||||||
|
|
||||||
|
suite.Contains(strData, "192.168.0.0/24")
|
||||||
|
suite.Contains(strData, "2001:db8:8000::/36")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMem(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
suite.Run(t, &MemTestSuite{})
|
||||||
|
}
|
||||||
+59
-74
@@ -12,17 +12,20 @@ import (
|
|||||||
|
|
||||||
"github.com/9seconds/mtg/v2/ipblocklist/files"
|
"github.com/9seconds/mtg/v2/ipblocklist/files"
|
||||||
"github.com/9seconds/mtg/v2/mtglib"
|
"github.com/9seconds/mtg/v2/mtglib"
|
||||||
"github.com/kentik/patricia"
|
|
||||||
"github.com/kentik/patricia/bool_tree"
|
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
|
"github.com/yl2chen/cidranger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
fireholIPv4DefaultCIDR = 32
|
fireholRegexpComment = regexp.MustCompile(`\s*#.*?$`)
|
||||||
fireholIPv6DefaultCIDR = 128
|
|
||||||
|
fireholIPv4DefaultCIDR = net.CIDRMask(32, 32) // nolint: gomnd
|
||||||
|
fireholIPv6DefaultCIDR = net.CIDRMask(128, 128) // nolint: gomnd
|
||||||
)
|
)
|
||||||
|
|
||||||
var fireholRegexpComment = regexp.MustCompile(`\s*#.*?$`)
|
// FireholUpdateCallback defines a signature of the callback that has to be
|
||||||
|
// execute when ip list is updated.
|
||||||
|
type FireholUpdateCallback func(context.Context, int)
|
||||||
|
|
||||||
// Firehol is IPBlocklist which uses lists from FireHOL:
|
// Firehol is IPBlocklist which uses lists from FireHOL:
|
||||||
// https://iplists.firehol.org/
|
// https://iplists.firehol.org/
|
||||||
@@ -43,11 +46,12 @@ type Firehol struct {
|
|||||||
logger mtglib.Logger
|
logger mtglib.Logger
|
||||||
updateMutex sync.RWMutex
|
updateMutex sync.RWMutex
|
||||||
|
|
||||||
|
updateCallback FireholUpdateCallback
|
||||||
|
ranger cidranger.Ranger
|
||||||
|
|
||||||
blocklists []files.File
|
blocklists []files.File
|
||||||
|
|
||||||
workerPool *ants.Pool
|
workerPool *ants.Pool
|
||||||
treeV4 *bool_tree.TreeV4
|
|
||||||
treeV6 *bool_tree.TreeV6
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown stop a background update process.
|
// Shutdown stop a background update process.
|
||||||
@@ -64,11 +68,12 @@ func (f *Firehol) Contains(ip net.IP) bool {
|
|||||||
f.updateMutex.RLock()
|
f.updateMutex.RLock()
|
||||||
defer f.updateMutex.RUnlock()
|
defer f.updateMutex.RUnlock()
|
||||||
|
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
ok, err := f.ranger.Contains(ip)
|
||||||
return f.containsIPv4(ip4)
|
if err != nil {
|
||||||
|
f.logger.BindStr("ip", ip.String()).DebugError("Cannot check if ip is present", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.containsIPv6(ip.To16())
|
return ok && err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts a background update process.
|
// Run starts a background update process.
|
||||||
@@ -103,26 +108,6 @@ func (f *Firehol) Run(updateEach time.Duration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Firehol) containsIPv4(addr net.IP) bool {
|
|
||||||
ip := patricia.NewIPv4AddressFromBytes(addr, 32) // nolint: gomnd
|
|
||||||
|
|
||||||
if ok, _ := f.treeV4.FindDeepestTag(ip); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Firehol) containsIPv6(addr net.IP) bool {
|
|
||||||
ip := patricia.NewIPv6Address(addr, 128) // nolint: gomnd
|
|
||||||
|
|
||||||
if ok, _ := f.treeV6.FindDeepestTag(ip); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Firehol) update() {
|
func (f *Firehol) update() {
|
||||||
ctx, cancel := context.WithCancel(f.ctx)
|
ctx, cancel := context.WithCancel(f.ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -130,9 +115,8 @@ func (f *Firehol) update() {
|
|||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(f.blocklists))
|
wg.Add(len(f.blocklists))
|
||||||
|
|
||||||
treeMutex := &sync.Mutex{}
|
mutex := &sync.Mutex{}
|
||||||
v4tree := bool_tree.NewTreeV4()
|
ranger := cidranger.NewPCTrieRanger()
|
||||||
v6tree := bool_tree.NewTreeV6()
|
|
||||||
|
|
||||||
for _, v := range f.blocklists {
|
for _, v := range f.blocklists {
|
||||||
go func(file files.File) {
|
go func(file files.File) {
|
||||||
@@ -149,7 +133,7 @@ func (f *Firehol) update() {
|
|||||||
|
|
||||||
defer fileContent.Close()
|
defer fileContent.Close()
|
||||||
|
|
||||||
if err := f.updateFromFile(treeMutex, v4tree, v6tree, bufio.NewScanner(fileContent)); err != nil {
|
if err := f.updateFromFile(mutex, ranger, bufio.NewScanner(fileContent)); err != nil {
|
||||||
logger.WarningError("update has failed", err)
|
logger.WarningError("update has failed", err)
|
||||||
}
|
}
|
||||||
}(v)
|
}(v)
|
||||||
@@ -160,16 +144,19 @@ func (f *Firehol) update() {
|
|||||||
f.updateMutex.Lock()
|
f.updateMutex.Lock()
|
||||||
defer f.updateMutex.Unlock()
|
defer f.updateMutex.Unlock()
|
||||||
|
|
||||||
f.treeV4 = v4tree
|
f.ranger = ranger
|
||||||
f.treeV6 = v6tree
|
|
||||||
|
|
||||||
f.logger.Info("blocklist was updated")
|
if f.updateCallback != nil {
|
||||||
|
f.updateCallback(ctx, ranger.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
f.logger.Info("ip list was updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Firehol) updateFromFile(mutex sync.Locker,
|
func (f *Firehol) updateFromFile(mutex sync.Locker,
|
||||||
v4tree *bool_tree.TreeV4,
|
ranger cidranger.Ranger,
|
||||||
v6tree *bool_tree.TreeV6,
|
scanner *bufio.Scanner,
|
||||||
scanner *bufio.Scanner) error {
|
) error {
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
text := scanner.Text()
|
text := scanner.Text()
|
||||||
text = fireholRegexpComment.ReplaceAllLiteralString(text, "")
|
text = fireholRegexpComment.ReplaceAllLiteralString(text, "")
|
||||||
@@ -179,12 +166,18 @@ func (f *Firehol) updateFromFile(mutex sync.Locker,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, cidr, err := f.updateParseLine(text)
|
ipnet, err := f.updateParseLine(text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot parse a line: %w", err)
|
return fmt.Errorf("cannot parse a line: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.updateAddToTrees(ip, cidr, mutex, v4tree, v6tree)
|
mutex.Lock()
|
||||||
|
err = ranger.Insert(cidranger.NewBasicRangerEntry(*ipnet))
|
||||||
|
mutex.Unlock()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot insert %v into ranger: %w", ipnet, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if scanner.Err() != nil {
|
if scanner.Err() != nil {
|
||||||
@@ -194,38 +187,26 @@ func (f *Firehol) updateFromFile(mutex sync.Locker,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Firehol) updateParseLine(text string) (net.IP, uint, error) {
|
func (f *Firehol) updateParseLine(text string) (*net.IPNet, error) {
|
||||||
_, ipnet, err := net.ParseCIDR(text)
|
if _, ipnet, err := net.ParseCIDR(text); err == nil {
|
||||||
if err != nil {
|
return ipnet, nil
|
||||||
|
}
|
||||||
|
|
||||||
ipaddr := net.ParseIP(text)
|
ipaddr := net.ParseIP(text)
|
||||||
if ipaddr == nil {
|
if ipaddr == nil {
|
||||||
return nil, 0, fmt.Errorf("incorrect ip address %s", text)
|
return nil, fmt.Errorf("incorrect ip address %s", text)
|
||||||
}
|
}
|
||||||
|
|
||||||
ip4 := ipaddr.To4()
|
mask := fireholIPv4DefaultCIDR
|
||||||
if ip4 != nil {
|
|
||||||
return ip4, fireholIPv4DefaultCIDR, nil
|
if ipaddr.To4() == nil {
|
||||||
|
mask = fireholIPv6DefaultCIDR
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipaddr.To16(), fireholIPv6DefaultCIDR, nil
|
return &net.IPNet{
|
||||||
}
|
IP: ipaddr,
|
||||||
|
Mask: mask,
|
||||||
ones, _ := ipnet.Mask.Size()
|
}, nil
|
||||||
|
|
||||||
return ipnet.IP, uint(ones), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Firehol) updateAddToTrees(ip net.IP, cidr uint,
|
|
||||||
mutex sync.Locker,
|
|
||||||
v4tree *bool_tree.TreeV4, v6tree *bool_tree.TreeV6) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
if ip.To4() != nil {
|
|
||||||
v4tree.Set(patricia.NewIPv4AddressFromBytes(ip, cidr), true)
|
|
||||||
} else {
|
|
||||||
v6tree.Set(patricia.NewIPv6Address(ip, cidr), true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFirehol creates a new instance of FireHOL IP blocklist.
|
// NewFirehol creates a new instance of FireHOL IP blocklist.
|
||||||
@@ -235,7 +216,9 @@ func (f *Firehol) updateAddToTrees(ip net.IP, cidr uint,
|
|||||||
func NewFirehol(logger mtglib.Logger, network mtglib.Network,
|
func NewFirehol(logger mtglib.Logger, network mtglib.Network,
|
||||||
downloadConcurrency uint,
|
downloadConcurrency uint,
|
||||||
urls []string,
|
urls []string,
|
||||||
localFiles []string) (*Firehol, error) {
|
localFiles []string,
|
||||||
|
updateCallback FireholUpdateCallback,
|
||||||
|
) (*Firehol, error) {
|
||||||
blocklists := []files.File{}
|
blocklists := []files.File{}
|
||||||
|
|
||||||
for _, v := range localFiles {
|
for _, v := range localFiles {
|
||||||
@@ -258,12 +241,14 @@ func NewFirehol(logger mtglib.Logger, network mtglib.Network,
|
|||||||
blocklists = append(blocklists, file)
|
blocklists = append(blocklists, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewFireholFromFiles(logger, downloadConcurrency, blocklists)
|
return NewFireholFromFiles(logger, downloadConcurrency, blocklists, updateCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFireholFromFiles(logger mtglib.Logger,
|
func NewFireholFromFiles(logger mtglib.Logger,
|
||||||
downloadConcurrency uint,
|
downloadConcurrency uint,
|
||||||
blocklists []files.File) (*Firehol, error) {
|
blocklists []files.File,
|
||||||
|
updateCallback FireholUpdateCallback,
|
||||||
|
) (*Firehol, error) {
|
||||||
if downloadConcurrency == 0 {
|
if downloadConcurrency == 0 {
|
||||||
downloadConcurrency = DefaultFireholDownloadConcurrency
|
downloadConcurrency = DefaultFireholDownloadConcurrency
|
||||||
}
|
}
|
||||||
@@ -275,9 +260,9 @@ func NewFireholFromFiles(logger mtglib.Logger,
|
|||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
ctxCancel: cancel,
|
ctxCancel: cancel,
|
||||||
logger: logger.Named("firehol"),
|
logger: logger.Named("firehol"),
|
||||||
treeV4: bool_tree.NewTreeV4(),
|
ranger: cidranger.NewPCTrieRanger(),
|
||||||
treeV6: bool_tree.NewTreeV6(),
|
|
||||||
workerPool: workerPool,
|
workerPool: workerPool,
|
||||||
blocklists: blocklists,
|
blocklists: blocklists,
|
||||||
|
updateCallback: updateCallback,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ func (suite *FireholTestSuite) TearDownSuite() {
|
|||||||
func (suite *FireholTestSuite) TestLocalFail() {
|
func (suite *FireholTestSuite) TestLocalFail() {
|
||||||
blocklist, err := ipblocklist.NewFirehol(logger.NewNoopLogger(),
|
blocklist, err := ipblocklist.NewFirehol(logger.NewNoopLogger(),
|
||||||
suite.networkMock, 2,
|
suite.networkMock, 2,
|
||||||
nil, []string{filepath.Join("testdata", "broken_ipset.ipset")})
|
nil, []string{filepath.Join("testdata", "broken_ipset.ipset")},
|
||||||
|
nil)
|
||||||
|
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
@@ -85,7 +86,8 @@ func (suite *FireholTestSuite) TestLocalFail() {
|
|||||||
func (suite *FireholTestSuite) TestLocalOk() {
|
func (suite *FireholTestSuite) TestLocalOk() {
|
||||||
blocklist, err := ipblocklist.NewFirehol(logger.NewNoopLogger(),
|
blocklist, err := ipblocklist.NewFirehol(logger.NewNoopLogger(),
|
||||||
suite.networkMock, 2,
|
suite.networkMock, 2,
|
||||||
nil, []string{filepath.Join("testdata", "good_ipset.ipset")})
|
nil, []string{filepath.Join("testdata", "good_ipset.ipset")},
|
||||||
|
nil)
|
||||||
|
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ func (suite *FireholTestSuite) TestLocalOk() {
|
|||||||
func (suite *FireholTestSuite) TestRemoteFail() {
|
func (suite *FireholTestSuite) TestRemoteFail() {
|
||||||
blocklist, err := ipblocklist.NewFirehol(logger.NewNoopLogger(),
|
blocklist, err := ipblocklist.NewFirehol(logger.NewNoopLogger(),
|
||||||
suite.networkMock, 2,
|
suite.networkMock, 2,
|
||||||
[]string{"https://google.com"}, nil)
|
[]string{"https://google.com"}, nil, nil)
|
||||||
|
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
@@ -127,7 +129,7 @@ func (suite *FireholTestSuite) TestMixed() {
|
|||||||
suite.httpServer.URL,
|
suite.httpServer.URL,
|
||||||
}, []string{
|
}, []string{
|
||||||
filepath.Join("testdata", "good_ipset.ipset"),
|
filepath.Join("testdata", "good_ipset.ipset"),
|
||||||
})
|
}, nil)
|
||||||
|
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ipblocklist
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/mtglib"
|
"github.com/9seconds/mtg/v2/mtglib"
|
||||||
)
|
)
|
||||||
@@ -9,6 +10,8 @@ import (
|
|||||||
type noop struct{}
|
type noop struct{}
|
||||||
|
|
||||||
func (n noop) Contains(ip net.IP) bool { return false }
|
func (n noop) Contains(ip net.IP) bool { return false }
|
||||||
|
func (n noop) Run(updateEach time.Duration) {}
|
||||||
|
func (n noop) Shutdown() {}
|
||||||
|
|
||||||
// NewNoop returns a dummy ipblocklist which allows all incoming
|
// NewNoop returns a dummy ipblocklist which allows all incoming
|
||||||
// connections.
|
// connections.
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ func (suite *NoopTestSuite) TestOp() {
|
|||||||
suite.False(ipblocklist.NewNoop().Contains(net.ParseIP("10.0.0.10")))
|
suite.False(ipblocklist.NewNoop().Contains(net.ParseIP("10.0.0.10")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *NoopTestSuite) TestRun() {
|
||||||
|
blocklist := ipblocklist.NewNoop()
|
||||||
|
|
||||||
|
blocklist.Run(0)
|
||||||
|
blocklist.Shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
func TestNoop(t *testing.T) {
|
func TestNoop(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
suite.Run(t, &NoopTestSuite{})
|
suite.Run(t, &NoopTestSuite{})
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"runtime/debug"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/internal/cli"
|
"github.com/9seconds/mtg/v2/internal/cli"
|
||||||
@@ -26,6 +29,32 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if buildInfo, ok := debug.ReadBuildInfo(); ok {
|
||||||
|
vcsCommit := "<no-commit>"
|
||||||
|
vcsDate := time.Now()
|
||||||
|
vcsDirty := ""
|
||||||
|
|
||||||
|
for _, setting := range buildInfo.Settings {
|
||||||
|
switch setting.Key {
|
||||||
|
case "vcs.time":
|
||||||
|
vcsDate, _ = time.Parse(time.RFC3339, setting.Value)
|
||||||
|
case "vcs.revision":
|
||||||
|
vcsCommit = setting.Value
|
||||||
|
case "vcs.modified":
|
||||||
|
if isDirty, _ := strconv.ParseBool(setting.Value); isDirty {
|
||||||
|
vcsDirty = " [dirty]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
version = fmt.Sprintf("%s (%s: %s on %s%s)",
|
||||||
|
version,
|
||||||
|
buildInfo.GoVersion,
|
||||||
|
vcsDate.Format(time.RFC3339),
|
||||||
|
vcsCommit,
|
||||||
|
vcsDirty)
|
||||||
|
}
|
||||||
|
|
||||||
cli := &cli.CLI{}
|
cli := &cli.CLI{}
|
||||||
ctx := kong.Parse(cli, kong.Vars{
|
ctx := kong.Parse(cli, kong.Vars{
|
||||||
"version": version,
|
"version": version,
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ type EventIPBlocklisted struct {
|
|||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
RemoteIP net.IP
|
RemoteIP net.IP
|
||||||
|
IsBlockList bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventReplayAttack is emitted when mtg detects a replay attack on a
|
// EventReplayAttack is emitted when mtg detects a replay attack on a
|
||||||
@@ -92,6 +93,15 @@ type EventReplayAttack struct {
|
|||||||
eventBase
|
eventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EventIPListSize is emitted when mtg updates a contents of the ip lists:
|
||||||
|
// allowlist or blocklist.
|
||||||
|
type EventIPListSize struct {
|
||||||
|
eventBase
|
||||||
|
|
||||||
|
Size int
|
||||||
|
IsBlockList bool
|
||||||
|
}
|
||||||
|
|
||||||
// NewEventStart creates a new EventStart event.
|
// NewEventStart creates a new EventStart event.
|
||||||
func NewEventStart(streamID string, remoteIP net.IP) EventStart {
|
func NewEventStart(streamID string, remoteIP net.IP) EventStart {
|
||||||
return EventStart{
|
return EventStart{
|
||||||
@@ -164,6 +174,19 @@ func NewEventIPBlocklisted(remoteIP net.IP) EventIPBlocklisted {
|
|||||||
timestamp: time.Now(),
|
timestamp: time.Now(),
|
||||||
},
|
},
|
||||||
RemoteIP: remoteIP,
|
RemoteIP: remoteIP,
|
||||||
|
IsBlockList: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEventIPAllowlisted creates a NewEventIPBlocklisted event with a mark that
|
||||||
|
// it is supposed to be for allow list.
|
||||||
|
func NewEventIPAllowlisted(remoteIP net.IP) EventIPBlocklisted {
|
||||||
|
return EventIPBlocklisted{
|
||||||
|
eventBase: eventBase{
|
||||||
|
timestamp: time.Now(),
|
||||||
|
},
|
||||||
|
RemoteIP: remoteIP,
|
||||||
|
IsBlockList: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,3 +199,14 @@ func NewEventReplayAttack(streamID string) EventReplayAttack {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewEventIPListSize creates a new EventIPListSize event.
|
||||||
|
func NewEventIPListSize(size int, isBlockList bool) EventIPListSize {
|
||||||
|
return EventIPListSize{
|
||||||
|
eventBase: eventBase{
|
||||||
|
timestamp: time.Now(),
|
||||||
|
},
|
||||||
|
Size: size,
|
||||||
|
IsBlockList: isBlockList,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,6 +60,15 @@ func (suite *EventsTestSuite) TestEventIPBlocklisted() {
|
|||||||
|
|
||||||
suite.Empty(evt.StreamID())
|
suite.Empty(evt.StreamID())
|
||||||
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
|
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
|
||||||
|
suite.True(evt.IsBlockList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *EventsTestSuite) TestEventIPAllowlisted() {
|
||||||
|
evt := mtglib.NewEventIPAllowlisted(net.ParseIP("10.0.0.10"))
|
||||||
|
|
||||||
|
suite.Empty(evt.StreamID())
|
||||||
|
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
|
||||||
|
suite.False(evt.IsBlockList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *EventsTestSuite) TestEventReplayAttack() {
|
func (suite *EventsTestSuite) TestEventReplayAttack() {
|
||||||
@@ -69,6 +78,15 @@ func (suite *EventsTestSuite) TestEventReplayAttack() {
|
|||||||
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
|
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *EventsTestSuite) TestEventIPListSize() {
|
||||||
|
evt := mtglib.NewEventIPListSize(10, false)
|
||||||
|
|
||||||
|
suite.Empty(evt.StreamID())
|
||||||
|
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
|
||||||
|
suite.Equal(10, evt.Size)
|
||||||
|
suite.False(evt.IsBlockList)
|
||||||
|
}
|
||||||
|
|
||||||
func TestEvents(t *testing.T) {
|
func TestEvents(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
suite.Run(t, &EventsTestSuite{})
|
suite.Run(t, &EventsTestSuite{})
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ var (
|
|||||||
// create a proxy but ip blocklist instance is not defined.
|
// create a proxy but ip blocklist instance is not defined.
|
||||||
ErrIPBlocklistIsNotDefined = errors.New("ip blocklist is not defined")
|
ErrIPBlocklistIsNotDefined = errors.New("ip blocklist is not defined")
|
||||||
|
|
||||||
|
// ErrIPAllowlistIsNotDefined is returned if you are trying to
|
||||||
|
// create a proxy but ip allowlist instance is not defined.
|
||||||
|
ErrIPAllowlistIsNotDefined = errors.New("ip allowlist is not defined")
|
||||||
|
|
||||||
// ErrEventStreamIsNotDefined is returned if you are trying to create a
|
// ErrEventStreamIsNotDefined is returned if you are trying to create a
|
||||||
// proxy but event stream instance is not defined.
|
// proxy but event stream instance is not defined.
|
||||||
ErrEventStreamIsNotDefined = errors.New("event stream is not defined")
|
ErrEventStreamIsNotDefined = errors.New("event stream is not defined")
|
||||||
@@ -176,6 +180,12 @@ type IPBlocklist interface {
|
|||||||
// Contains checks if given IP address belongs to this blocklist If.
|
// Contains checks if given IP address belongs to this blocklist If.
|
||||||
// it is, a connection is terminated .
|
// it is, a connection is terminated .
|
||||||
Contains(net.IP) bool
|
Contains(net.IP) bool
|
||||||
|
|
||||||
|
// Run starts a background update procedure for a blocklist
|
||||||
|
Run(time.Duration)
|
||||||
|
|
||||||
|
// Shutdown stops a blocklist. It is assumed that none will access it after.
|
||||||
|
Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event is a data structure which is populated during mtg request
|
// Event is a data structure which is populated during mtg request
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package faketls_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/v2/mtglib/internal/faketls"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var FuzzClientHelloSecret = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
|
||||||
|
func FuzzClientHello(f *testing.F) {
|
||||||
|
f.Add([]byte{1, 2, 3})
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, frame []byte) {
|
||||||
|
_, err := faketls.ParseClientHello(FuzzClientHelloSecret, frame)
|
||||||
|
|
||||||
|
// a probability of having != err is almost negligible
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ const (
|
|||||||
|
|
||||||
// ClientHelloMinLen is a minimal possible length of
|
// ClientHelloMinLen is a minimal possible length of
|
||||||
// ClientHello record.
|
// ClientHello record.
|
||||||
ClientHelloMinLen = 4
|
ClientHelloMinLen = 6
|
||||||
|
|
||||||
// WelcomePacketRandomOffset is an offset of random in ServerHello
|
// WelcomePacketRandomOffset is an offset of random in ServerHello
|
||||||
// packet (including record envelope).
|
// packet (including record envelope).
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ var bytesBufferPool = sync.Pool{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func acquireBytesBuffer() *bytes.Buffer {
|
func acquireBytesBuffer() *bytes.Buffer {
|
||||||
return bytesBufferPool.Get().(*bytes.Buffer)
|
return bytesBufferPool.Get().(*bytes.Buffer) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func releaseBytesBuffer(b *bytes.Buffer) {
|
func releaseBytesBuffer(b *bytes.Buffer) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ var recordPool = sync.Pool{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AcquireRecord() *Record {
|
func AcquireRecord() *Record {
|
||||||
return recordPool.Get().(*Record)
|
return recordPool.Get().(*Record) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReleaseRecord(r *Record) {
|
func ReleaseRecord(r *Record) {
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package obfuscated2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var FuzzClientHandshakeSecret = []byte{1, 2, 3}
|
||||||
|
|
||||||
|
func FuzzClientHandshake(f *testing.F) {
|
||||||
|
f.Add([]byte{1, 2, 3})
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, frame []byte) {
|
||||||
|
data := bytes.NewReader(frame)
|
||||||
|
|
||||||
|
if _, _, _, err := ClientHandshake(FuzzClientHandshakeSecret, data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handshake := clientHandhakeFrame{}
|
||||||
|
require.Len(t, frame, handshakeFrameLen)
|
||||||
|
|
||||||
|
copy(handshake.data[:], frame)
|
||||||
|
|
||||||
|
decryptor := handshake.decryptor(FuzzClientHandshakeSecret)
|
||||||
|
decryptor.XORKeyStream(handshake.data[:], handshake.data[:])
|
||||||
|
|
||||||
|
require.Equal(t, handshakeConnectionType, handshake.connectionType())
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
package obfuscated2_test
|
package obfuscated2_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/v2/internal/testlib"
|
||||||
|
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type snapshotBytes struct {
|
type snapshotBytes struct {
|
||||||
@@ -50,6 +58,14 @@ type SnapshotTestSuite struct {
|
|||||||
snapshots map[string]*Obfuscated2Snapshot
|
snapshots map[string]*Obfuscated2Snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ServerHandshakeTestData struct {
|
||||||
|
connMock *testlib.EssentialsConnMock
|
||||||
|
|
||||||
|
proxyConn obfuscated2.Conn
|
||||||
|
encryptor cipher.Stream
|
||||||
|
decryptor cipher.Stream
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *SnapshotTestSuite) IngestSnapshots(dirname, namePrefix string) error {
|
func (suite *SnapshotTestSuite) IngestSnapshots(dirname, namePrefix string) error {
|
||||||
suite.snapshots = map[string]*Obfuscated2Snapshot{}
|
suite.snapshots = map[string]*Obfuscated2Snapshot{}
|
||||||
|
|
||||||
@@ -81,3 +97,41 @@ func (suite *SnapshotTestSuite) IngestSnapshots(dirname, namePrefix string) erro
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewServerHandshakeTestData(t *testing.T) ServerHandshakeTestData {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
connMock := &testlib.EssentialsConnMock{}
|
||||||
|
|
||||||
|
handshakeEnc, handshakeDec, err := obfuscated2.ServerHandshake(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
serverEncrypted := buf.Bytes()
|
||||||
|
decBlock, _ := aes.NewCipher(serverEncrypted[8 : 8+32])
|
||||||
|
decryptor := cipher.NewCTR(decBlock, serverEncrypted[8+32:8+32+16])
|
||||||
|
|
||||||
|
serverDecrypted := make([]byte, len(serverEncrypted))
|
||||||
|
decryptor.XORKeyStream(serverDecrypted, serverEncrypted)
|
||||||
|
|
||||||
|
require.Equal(t, "3d3d3Q",
|
||||||
|
base64.RawStdEncoding.EncodeToString(serverDecrypted[8+32+16:8+32+16+4]))
|
||||||
|
|
||||||
|
serverEncryptedReverted := make([]byte, len(serverEncrypted))
|
||||||
|
|
||||||
|
for i := 0; i < 32+16; i++ {
|
||||||
|
serverEncryptedReverted[8+i] = serverEncrypted[8+32+16-1-i]
|
||||||
|
}
|
||||||
|
|
||||||
|
encBlock, _ := aes.NewCipher(serverEncryptedReverted[8 : 8+32])
|
||||||
|
encryptor := cipher.NewCTR(encBlock, serverEncryptedReverted[8+32:8+32+16])
|
||||||
|
|
||||||
|
return ServerHandshakeTestData{
|
||||||
|
connMock: connMock,
|
||||||
|
proxyConn: obfuscated2.Conn{
|
||||||
|
Conn: connMock,
|
||||||
|
Encryptor: handshakeEnc,
|
||||||
|
Decryptor: handshakeDec,
|
||||||
|
},
|
||||||
|
encryptor: encryptor,
|
||||||
|
decryptor: decryptor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func acquireSha256Hasher() hash.Hash {
|
func acquireSha256Hasher() hash.Hash {
|
||||||
return sha256HasherPool.Get().(hash.Hash)
|
return sha256HasherPool.Get().(hash.Hash) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func releaseSha256Hasher(h hash.Hash) {
|
func releaseSha256Hasher(h hash.Hash) {
|
||||||
@@ -30,7 +30,7 @@ func releaseSha256Hasher(h hash.Hash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func acquireBytesBuffer() *bytes.Buffer {
|
func acquireBytesBuffer() *bytes.Buffer {
|
||||||
return bytesBufferPool.Get().(*bytes.Buffer)
|
return bytesBufferPool.Get().(*bytes.Buffer) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func releaseBytesBuffer(buf *bytes.Buffer) {
|
func releaseBytesBuffer(buf *bytes.Buffer) {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package obfuscated2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzServerGenerateHandshakeFrame(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, arg int) {
|
||||||
|
frame := generateServerHanshakeFrame()
|
||||||
|
|
||||||
|
assert.NotEqualValues(t, 0xef, frame.data[0])
|
||||||
|
|
||||||
|
firstBytes := binary.LittleEndian.Uint32(frame.data[:4])
|
||||||
|
assert.NotEqualValues(t, 0x44414548, firstBytes)
|
||||||
|
assert.NotEqualValues(t, 0x54534f50, firstBytes)
|
||||||
|
assert.NotEqualValues(t, 0x20544547, firstBytes)
|
||||||
|
assert.NotEqualValues(t, 0x4954504f, firstBytes)
|
||||||
|
assert.NotEqualValues(t, 0xeeeeeeee, firstBytes)
|
||||||
|
|
||||||
|
assert.NotEqualValues(
|
||||||
|
t,
|
||||||
|
0,
|
||||||
|
frame.data[4]|frame.data[5]|frame.data[6]|frame.data[7])
|
||||||
|
|
||||||
|
assert.Equal(t, handshakeConnectionType, frame.connectionType())
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package obfuscated2_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzServerSend(f *testing.F) {
|
||||||
|
f.Add([]byte{1, 2, 3, 4, 5})
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
handshakeData := NewServerHandshakeTestData(t)
|
||||||
|
|
||||||
|
handshakeData.connMock.
|
||||||
|
On("Write", mock.Anything).
|
||||||
|
Return(len(data), nil).
|
||||||
|
Once().
|
||||||
|
Run(func(args mock.Arguments) {
|
||||||
|
message := make([]byte, len(data))
|
||||||
|
handshakeData.decryptor.XORKeyStream(message, args.Get(0).([]byte)) // nolint: forcetypeassert
|
||||||
|
assert.Equal(t, message, data)
|
||||||
|
})
|
||||||
|
|
||||||
|
n, err := handshakeData.proxyConn.Write(data)
|
||||||
|
|
||||||
|
assert.EqualValues(t, len(data), n)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
handshakeData.connMock.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzServerReceive(f *testing.F) {
|
||||||
|
f.Add([]byte{1, 2, 3, 4, 5})
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
handshakeData := NewServerHandshakeTestData(t)
|
||||||
|
buffer := make([]byte, len(data))
|
||||||
|
|
||||||
|
handshakeData.connMock.
|
||||||
|
On("Read", mock.Anything).
|
||||||
|
Return(len(data), nil).
|
||||||
|
Once().
|
||||||
|
Run(func(args mock.Arguments) {
|
||||||
|
message := make([]byte, len(data))
|
||||||
|
handshakeData.encryptor.XORKeyStream(message, data)
|
||||||
|
copy(args.Get(0).([]byte), message) // nolint: forcetypeassert
|
||||||
|
})
|
||||||
|
|
||||||
|
n, err := handshakeData.proxyConn.Read(buffer)
|
||||||
|
|
||||||
|
assert.EqualValues(t, len(data), n)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, data, buffer)
|
||||||
|
handshakeData.connMock.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
package obfuscated2_test
|
package obfuscated2_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"encoding/base64"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/internal/testlib"
|
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2"
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
@@ -16,64 +10,31 @@ import (
|
|||||||
type ServerHandshakeTestSuite struct {
|
type ServerHandshakeTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
connMock *testlib.EssentialsConnMock
|
data ServerHandshakeTestData
|
||||||
proxyConn obfuscated2.Conn
|
|
||||||
encryptor cipher.Stream
|
|
||||||
decryptor cipher.Stream
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ServerHandshakeTestSuite) SetupTest() {
|
func (suite *ServerHandshakeTestSuite) SetupTest() {
|
||||||
buf := &bytes.Buffer{}
|
suite.data = NewServerHandshakeTestData(suite.T())
|
||||||
suite.connMock = &testlib.EssentialsConnMock{}
|
|
||||||
|
|
||||||
encryptor, decryptor, err := obfuscated2.ServerHandshake(buf)
|
|
||||||
suite.NoError(err)
|
|
||||||
|
|
||||||
suite.proxyConn = obfuscated2.Conn{
|
|
||||||
Conn: suite.connMock,
|
|
||||||
Encryptor: encryptor,
|
|
||||||
Decryptor: decryptor,
|
|
||||||
}
|
|
||||||
|
|
||||||
serverEncrypted := buf.Bytes()
|
|
||||||
|
|
||||||
decBlock, _ := aes.NewCipher(serverEncrypted[8 : 8+32])
|
|
||||||
suite.decryptor = cipher.NewCTR(decBlock, serverEncrypted[8+32:8+32+16])
|
|
||||||
|
|
||||||
serverDecrypted := make([]byte, len(serverEncrypted))
|
|
||||||
suite.decryptor.XORKeyStream(serverDecrypted, serverEncrypted)
|
|
||||||
|
|
||||||
suite.Equal("3d3d3Q",
|
|
||||||
base64.RawStdEncoding.EncodeToString(serverDecrypted[8+32+16:8+32+16+4]))
|
|
||||||
|
|
||||||
serverEncryptedReverted := make([]byte, len(serverEncrypted))
|
|
||||||
|
|
||||||
for i := 0; i < 32+16; i++ {
|
|
||||||
serverEncryptedReverted[8+i] = serverEncrypted[8+32+16-1-i]
|
|
||||||
}
|
|
||||||
|
|
||||||
encBlock, _ := aes.NewCipher(serverEncryptedReverted[8 : 8+32])
|
|
||||||
suite.encryptor = cipher.NewCTR(encBlock, serverEncryptedReverted[8+32:8+32+16])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ServerHandshakeTestSuite) TearDownTest() {
|
func (suite *ServerHandshakeTestSuite) TearDownTest() {
|
||||||
suite.connMock.AssertExpectations(suite.T())
|
suite.data.connMock.AssertExpectations(suite.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ServerHandshakeTestSuite) TestSendToTelegram() {
|
func (suite *ServerHandshakeTestSuite) TestSendToTelegram() {
|
||||||
messageToTelegram := []byte{10, 11, 12, 13, 14, 'a'}
|
messageToTelegram := []byte{10, 11, 12, 13, 14, 'a'}
|
||||||
|
|
||||||
suite.connMock.
|
suite.data.connMock.
|
||||||
On("Write", mock.Anything).
|
On("Write", mock.Anything).
|
||||||
Return(len(messageToTelegram), nil).
|
Return(len(messageToTelegram), nil).
|
||||||
Once().
|
Once().
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
message := make([]byte, len(messageToTelegram))
|
message := make([]byte, len(messageToTelegram))
|
||||||
suite.decryptor.XORKeyStream(message, args.Get(0).([]byte))
|
suite.data.decryptor.XORKeyStream(message, args.Get(0).([]byte)) // nolint: forcetypeassert
|
||||||
suite.Equal(messageToTelegram, message)
|
suite.Equal(messageToTelegram, message)
|
||||||
})
|
})
|
||||||
|
|
||||||
n, err := suite.proxyConn.Write(messageToTelegram)
|
n, err := suite.data.proxyConn.Write(messageToTelegram)
|
||||||
suite.EqualValues(len(messageToTelegram), n)
|
suite.EqualValues(len(messageToTelegram), n)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
}
|
}
|
||||||
@@ -82,17 +43,17 @@ func (suite *ServerHandshakeTestSuite) TestRecieveFromTelegram() {
|
|||||||
messageFromTelegram := []byte{10, 11, 12, 13, 14, 'a'}
|
messageFromTelegram := []byte{10, 11, 12, 13, 14, 'a'}
|
||||||
buffer := make([]byte, len(messageFromTelegram))
|
buffer := make([]byte, len(messageFromTelegram))
|
||||||
|
|
||||||
suite.connMock.
|
suite.data.connMock.
|
||||||
On("Read", mock.Anything).
|
On("Read", mock.Anything).
|
||||||
Return(len(messageFromTelegram), nil).
|
Return(len(messageFromTelegram), nil).
|
||||||
Once().
|
Once().
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
message := make([]byte, len(messageFromTelegram))
|
message := make([]byte, len(messageFromTelegram))
|
||||||
suite.encryptor.XORKeyStream(message, messageFromTelegram)
|
suite.data.encryptor.XORKeyStream(message, messageFromTelegram)
|
||||||
copy(args.Get(0).([]byte), message)
|
copy(args.Get(0).([]byte), message) // nolint: forcetypeassert
|
||||||
})
|
})
|
||||||
|
|
||||||
n, err := suite.proxyConn.Read(buffer)
|
n, err := suite.data.proxyConn.Read(buffer)
|
||||||
suite.EqualValues(len(messageFromTelegram), n)
|
suite.EqualValues(len(messageFromTelegram), n)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(messageFromTelegram, buffer)
|
suite.Equal(messageFromTelegram, buffer)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ var copyBufferPool = sync.Pool{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func acquireCopyBuffer() *[]byte {
|
func acquireCopyBuffer() *[]byte {
|
||||||
return copyBufferPool.Get().(*[]byte)
|
return copyBufferPool.Get().(*[]byte) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func releaseCopyBuffer(buf *[]byte) {
|
func releaseCopyBuffer(buf *[]byte) {
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ package telegram
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/essentials"
|
"github.com/9seconds/mtg/v2/essentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var errNoAddresses = errors.New("no addresses")
|
||||||
|
|
||||||
type preferIP uint8
|
type preferIP uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -28,10 +28,9 @@ func (t Telegram) Dial(ctx context.Context, dc int) (essentials.Conn, error) {
|
|||||||
addresses = append(t.pool.getV6(dc), t.pool.getV4(dc)...)
|
addresses = append(t.pool.getV6(dc), t.pool.getV4(dc)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var conn essentials.Conn
|
||||||
conn essentials.Conn
|
|
||||||
err error
|
err := errNoAddresses
|
||||||
)
|
|
||||||
|
|
||||||
for _, v := range addresses {
|
for _, v := range addresses {
|
||||||
conn, err = t.dialer.DialContext(ctx, v.network, v.address)
|
conn, err = t.dialer.DialContext(ctx, v.network, v.address)
|
||||||
|
|||||||
+15
-9
@@ -34,7 +34,7 @@ type Proxy struct {
|
|||||||
network Network
|
network Network
|
||||||
antiReplayCache AntiReplayCache
|
antiReplayCache AntiReplayCache
|
||||||
blocklist IPBlocklist
|
blocklist IPBlocklist
|
||||||
whitelist IPBlocklist
|
allowlist IPBlocklist
|
||||||
eventStream EventStream
|
eventStream EventStream
|
||||||
logger Logger
|
logger Logger
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ func (p *Proxy) ServeConn(conn essentials.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serve starts a proxy on a given listener.
|
// Serve starts a proxy on a given listener.
|
||||||
func (p *Proxy) Serve(listener net.Listener) error { // nolint: cyclop
|
func (p *Proxy) Serve(listener net.Listener) error {
|
||||||
p.streamWaitGroup.Add(1)
|
p.streamWaitGroup.Add(1)
|
||||||
defer p.streamWaitGroup.Done()
|
defer p.streamWaitGroup.Done()
|
||||||
|
|
||||||
@@ -106,13 +106,13 @@ func (p *Proxy) Serve(listener net.Listener) error { // nolint: cyclop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ipAddr := conn.RemoteAddr().(*net.TCPAddr).IP
|
ipAddr := conn.RemoteAddr().(*net.TCPAddr).IP // nolint: forcetypeassert
|
||||||
logger := p.logger.BindStr("ip", ipAddr.String())
|
logger := p.logger.BindStr("ip", ipAddr.String())
|
||||||
|
|
||||||
if p.whitelist != nil && !p.whitelist.Contains(ipAddr) {
|
if !p.allowlist.Contains(ipAddr) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
logger.Info("ip was rejected by whitelist")
|
logger.Info("ip was rejected by allowlist")
|
||||||
p.eventStream.Send(p.ctx, NewEventIPBlocklisted(ipAddr))
|
p.eventStream.Send(p.ctx, NewEventIPAllowlisted(ipAddr))
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -144,6 +144,9 @@ func (p *Proxy) Shutdown() {
|
|||||||
p.ctxCancel()
|
p.ctxCancel()
|
||||||
p.streamWaitGroup.Wait()
|
p.streamWaitGroup.Wait()
|
||||||
p.workerPool.Release()
|
p.workerPool.Release()
|
||||||
|
|
||||||
|
p.allowlist.Shutdown()
|
||||||
|
p.blocklist.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool {
|
func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool {
|
||||||
@@ -249,7 +252,10 @@ func (p *Proxy) doTelegramCall(ctx *streamContext) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.eventStream.Send(ctx,
|
p.eventStream.Send(ctx,
|
||||||
NewEventConnectedToDC(ctx.streamID, conn.RemoteAddr().(*net.TCPAddr).IP, ctx.dc))
|
NewEventConnectedToDC(ctx.streamID,
|
||||||
|
conn.RemoteAddr().(*net.TCPAddr).IP, // nolint: forcetypeassert
|
||||||
|
ctx.dc),
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -299,7 +305,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
|
|||||||
network: opts.Network,
|
network: opts.Network,
|
||||||
antiReplayCache: opts.AntiReplayCache,
|
antiReplayCache: opts.AntiReplayCache,
|
||||||
blocklist: opts.IPBlocklist,
|
blocklist: opts.IPBlocklist,
|
||||||
whitelist: opts.IPWhitelist,
|
allowlist: opts.IPAllowlist,
|
||||||
eventStream: opts.EventStream,
|
eventStream: opts.EventStream,
|
||||||
logger: opts.getLogger("proxy"),
|
logger: opts.getLogger("proxy"),
|
||||||
domainFrontingPort: opts.getDomainFrontingPort(),
|
domainFrontingPort: opts.getDomainFrontingPort(),
|
||||||
@@ -310,7 +316,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
|
|||||||
|
|
||||||
pool, err := ants.NewPoolWithFunc(opts.getConcurrency(),
|
pool, err := ants.NewPoolWithFunc(opts.getConcurrency(),
|
||||||
func(arg interface{}) {
|
func(arg interface{}) {
|
||||||
proxy.ServeConn(arg.(essentials.Conn))
|
proxy.ServeConn(arg.(essentials.Conn)) // nolint: forcetypeassert
|
||||||
},
|
},
|
||||||
ants.WithLogger(opts.getLogger("ants")),
|
ants.WithLogger(opts.getLogger("ants")),
|
||||||
ants.WithNonblocking(true))
|
ants.WithNonblocking(true))
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ type ProxyOpts struct {
|
|||||||
// This is a mandatory setting.
|
// This is a mandatory setting.
|
||||||
IPBlocklist IPBlocklist
|
IPBlocklist IPBlocklist
|
||||||
|
|
||||||
// IPWhitelist defines a whitelist of IPs to allow to use proxy.
|
// IPAllowlist defines a whitelist of IPs to allow to use proxy.
|
||||||
//
|
//
|
||||||
// This is an optional setting, ignored by default (no restrictions).
|
// This is an optional setting, ignored by default (no restrictions).
|
||||||
IPWhitelist IPBlocklist
|
IPAllowlist IPBlocklist
|
||||||
|
|
||||||
// EventStream defines an instance of event stream.
|
// EventStream defines an instance of event stream.
|
||||||
//
|
//
|
||||||
@@ -125,6 +125,8 @@ func (p ProxyOpts) valid() error {
|
|||||||
return ErrAntiReplayCacheIsNotDefined
|
return ErrAntiReplayCacheIsNotDefined
|
||||||
case p.IPBlocklist == nil:
|
case p.IPBlocklist == nil:
|
||||||
return ErrIPBlocklistIsNotDefined
|
return ErrIPBlocklistIsNotDefined
|
||||||
|
case p.IPAllowlist == nil:
|
||||||
|
return ErrIPAllowlistIsNotDefined
|
||||||
case p.EventStream == nil:
|
case p.EventStream == nil:
|
||||||
return ErrEventStreamIsNotDefined
|
return ErrEventStreamIsNotDefined
|
||||||
case p.Logger == nil:
|
case p.Logger == nil:
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/9seconds/mtg/v2/antireplay"
|
"github.com/9seconds/mtg/v2/antireplay"
|
||||||
"github.com/9seconds/mtg/v2/events"
|
"github.com/9seconds/mtg/v2/events"
|
||||||
"github.com/9seconds/mtg/v2/ipblocklist"
|
"github.com/9seconds/mtg/v2/ipblocklist"
|
||||||
|
"github.com/9seconds/mtg/v2/ipblocklist/files"
|
||||||
"github.com/9seconds/mtg/v2/logger"
|
"github.com/9seconds/mtg/v2/logger"
|
||||||
"github.com/9seconds/mtg/v2/mtglib"
|
"github.com/9seconds/mtg/v2/mtglib"
|
||||||
"github.com/9seconds/mtg/v2/network"
|
"github.com/9seconds/mtg/v2/network"
|
||||||
@@ -22,6 +23,7 @@ import (
|
|||||||
"github.com/gotd/td/telegram/dcs"
|
"github.com/gotd/td/telegram/dcs"
|
||||||
"github.com/gotd/td/tg"
|
"github.com/gotd/td/tg"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/yl2chen/cidranger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProxyTestSuite struct {
|
type ProxyTestSuite struct {
|
||||||
@@ -49,11 +51,26 @@ func (suite *ProxyTestSuite) SetupSuite() {
|
|||||||
ntw, err := network.NewNetwork(dialer, "mtgtest", "1.1.1.1", 0)
|
ntw, err := network.NewNetwork(dialer, "mtgtest", "1.1.1.1", 0)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
|
allowlist, _ := ipblocklist.NewFireholFromFiles(
|
||||||
|
logger.NewNoopLogger(),
|
||||||
|
1,
|
||||||
|
[]files.File{
|
||||||
|
files.NewMem([]*net.IPNet{
|
||||||
|
cidranger.AllIPv4,
|
||||||
|
cidranger.AllIPv6,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
go allowlist.Run(time.Second)
|
||||||
|
|
||||||
suite.opts = &mtglib.ProxyOpts{
|
suite.opts = &mtglib.ProxyOpts{
|
||||||
Secret: mtglib.GenerateSecret("httpbin.org"),
|
Secret: mtglib.GenerateSecret("httpbin.org"),
|
||||||
Network: ntw,
|
Network: ntw,
|
||||||
AntiReplayCache: antireplay.NewNoop(),
|
AntiReplayCache: antireplay.NewNoop(),
|
||||||
IPBlocklist: ipblocklist.NewNoop(),
|
IPBlocklist: ipblocklist.NewNoop(),
|
||||||
|
IPAllowlist: allowlist,
|
||||||
EventStream: events.NewNoopStream(),
|
EventStream: events.NewNoopStream(),
|
||||||
Logger: logger.NewNoopLogger(),
|
Logger: logger.NewNoopLogger(),
|
||||||
UseTestDCs: true,
|
UseTestDCs: true,
|
||||||
@@ -114,6 +131,14 @@ func (suite *ProxyTestSuite) TestCannotInitNoIPBlocklist() {
|
|||||||
suite.Error(err)
|
suite.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ProxyTestSuite) TestCannotInitNoIPAllowlist() {
|
||||||
|
opts := *suite.opts
|
||||||
|
opts.IPAllowlist = nil
|
||||||
|
|
||||||
|
_, err := mtglib.NewProxy(opts)
|
||||||
|
suite.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *ProxyTestSuite) TestCannotInitNoEventStream() {
|
func (suite *ProxyTestSuite) TestCannotInitNoEventStream() {
|
||||||
opts := *suite.opts
|
opts := *suite.opts
|
||||||
opts.EventStream = nil
|
opts.EventStream = nil
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (s *streamContext) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *streamContext) ClientIP() net.IP {
|
func (s *streamContext) ClientIP() net.IP {
|
||||||
return s.clientConn.RemoteAddr().(*net.TCPAddr).IP
|
return s.clientConn.RemoteAddr().(*net.TCPAddr).IP // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStreamContext(ctx context.Context, logger Logger, clientConn essentials.Conn) *streamContext {
|
func newStreamContext(ctx context.Context, logger Logger, clientConn essentials.Conn) *streamContext {
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ func (c *circuitBreakerDialer) Dial(network, address string) (essentials.Conn, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *circuitBreakerDialer) DialContext(ctx context.Context,
|
func (c *circuitBreakerDialer) DialContext(ctx context.Context,
|
||||||
network, address string) (essentials.Conn, error) {
|
network, address string,
|
||||||
|
) (essentials.Conn, error) {
|
||||||
switch atomic.LoadUint32(&c.state) {
|
switch atomic.LoadUint32(&c.state) {
|
||||||
case circuitBreakerStateClosed:
|
case circuitBreakerStateClosed:
|
||||||
return c.doClosed(ctx, network, address)
|
return c.doClosed(ctx, network, address)
|
||||||
@@ -48,7 +49,8 @@ func (c *circuitBreakerDialer) DialContext(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *circuitBreakerDialer) doClosed(ctx context.Context,
|
func (c *circuitBreakerDialer) doClosed(ctx context.Context,
|
||||||
network, address string) (essentials.Conn, error) {
|
network, address string,
|
||||||
|
) (essentials.Conn, error) {
|
||||||
conn, err := c.Dialer.DialContext(ctx, network, address)
|
conn, err := c.Dialer.DialContext(ctx, network, address)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -80,7 +82,8 @@ func (c *circuitBreakerDialer) doClosed(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *circuitBreakerDialer) doHalfOpened(ctx context.Context,
|
func (c *circuitBreakerDialer) doHalfOpened(ctx context.Context,
|
||||||
network, address string) (essentials.Conn, error) {
|
network, address string,
|
||||||
|
) (essentials.Conn, error) {
|
||||||
if !atomic.CompareAndSwapUint32(&c.halfOpenAttempts, 0, 1) {
|
if !atomic.CompareAndSwapUint32(&c.halfOpenAttempts, 0, 1) {
|
||||||
return nil, ErrCircuitBreakerOpened
|
return nil, ErrCircuitBreakerOpened
|
||||||
}
|
}
|
||||||
@@ -174,14 +177,16 @@ func (c *circuitBreakerDialer) stopTimer(timerRef **time.Timer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *circuitBreakerDialer) ensureTimer(timerRef **time.Timer,
|
func (c *circuitBreakerDialer) ensureTimer(timerRef **time.Timer,
|
||||||
timeout time.Duration, callback func()) {
|
timeout time.Duration, callback func(),
|
||||||
|
) {
|
||||||
if *timerRef == nil {
|
if *timerRef == nil {
|
||||||
*timerRef = time.AfterFunc(timeout, callback)
|
*timerRef = time.AfterFunc(timeout, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCircuitBreakerDialer(baseDialer Dialer,
|
func newCircuitBreakerDialer(baseDialer Dialer,
|
||||||
openThreshold uint32, halfOpenTimeout, resetFailuresTimeout time.Duration) Dialer {
|
openThreshold uint32, halfOpenTimeout, resetFailuresTimeout time.Duration,
|
||||||
|
) Dialer {
|
||||||
cb := &circuitBreakerDialer{
|
cb := &circuitBreakerDialer{
|
||||||
Dialer: baseDialer,
|
Dialer: baseDialer,
|
||||||
stateMutexChan: make(chan bool, 1),
|
stateMutexChan: make(chan bool, 1),
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ func (d *defaultDialer) DialContext(ctx context.Context, network, address string
|
|||||||
return nil, fmt.Errorf("cannot set socket options: %w", err)
|
return nil, fmt.Errorf("cannot set socket options: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn.(essentials.Conn), nil
|
return conn.(essentials.Conn), nil // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultDialer build a new dialer which dials bypassing proxies
|
// NewDefaultDialer build a new dialer which dials bypassing proxies
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ type DialerMock struct {
|
|||||||
func (d *DialerMock) Dial(network, address string) (essentials.Conn, error) {
|
func (d *DialerMock) Dial(network, address string) (essentials.Conn, error) {
|
||||||
args := d.Called(network, address)
|
args := d.Called(network, address)
|
||||||
|
|
||||||
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
|
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck, forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
|
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
|
||||||
args := d.Called(ctx, network, address)
|
args := d.Called(ctx, network, address)
|
||||||
|
|
||||||
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
|
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck, forcetypeassert
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ type DialerMock struct {
|
|||||||
func (d *DialerMock) Dial(network, address string) (essentials.Conn, error) {
|
func (d *DialerMock) Dial(network, address string) (essentials.Conn, error) {
|
||||||
args := d.Called(network, address)
|
args := d.Called(network, address)
|
||||||
|
|
||||||
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
|
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck, forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
|
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
|
||||||
args := d.Called(ctx, network, address)
|
args := d.Called(ctx, network, address)
|
||||||
|
|
||||||
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck
|
return args.Get(0).(essentials.Conn), args.Error(1) // nolint: wrapcheck, forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPServerTestSuite struct {
|
type HTTPServerTestSuite struct {
|
||||||
|
|||||||
+6
-3
@@ -61,7 +61,8 @@ func (n *network) DialContext(ctx context.Context, protocol, address string) (es
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *network) MakeHTTPClient(dialFunc func(ctx context.Context,
|
func (n *network) MakeHTTPClient(dialFunc func(ctx context.Context,
|
||||||
network, address string) (essentials.Conn, error)) *http.Client {
|
network, address string) (essentials.Conn, error),
|
||||||
|
) *http.Client {
|
||||||
if dialFunc == nil {
|
if dialFunc == nil {
|
||||||
dialFunc = n.DialContext
|
dialFunc = n.DialContext
|
||||||
}
|
}
|
||||||
@@ -123,7 +124,8 @@ func (n *network) dnsResolve(protocol, address string) ([]string, error) {
|
|||||||
// It brings simple DNS cache and DNS-Over-HTTPS when necessary.
|
// It brings simple DNS cache and DNS-Over-HTTPS when necessary.
|
||||||
func NewNetwork(dialer Dialer,
|
func NewNetwork(dialer Dialer,
|
||||||
userAgent, dohHostname string,
|
userAgent, dohHostname string,
|
||||||
httpTimeout time.Duration) (mtglib.Network, error) {
|
httpTimeout time.Duration,
|
||||||
|
) (mtglib.Network, error) {
|
||||||
switch {
|
switch {
|
||||||
case httpTimeout < 0:
|
case httpTimeout < 0:
|
||||||
return nil, fmt.Errorf("timeout should be positive number %s", httpTimeout)
|
return nil, fmt.Errorf("timeout should be positive number %s", httpTimeout)
|
||||||
@@ -146,7 +148,8 @@ func NewNetwork(dialer Dialer,
|
|||||||
|
|
||||||
func makeHTTPClient(userAgent string,
|
func makeHTTPClient(userAgent string,
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
dialFunc func(ctx context.Context, network, address string) (essentials.Conn, error)) *http.Client {
|
dialFunc func(ctx context.Context, network, address string) (essentials.Conn, error),
|
||||||
|
) *http.Client {
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Transport: networkHTTPTransport{
|
Transport: networkHTTPTransport{
|
||||||
|
|||||||
+2
-2
@@ -10,13 +10,13 @@ import (
|
|||||||
//
|
//
|
||||||
// bufferSize setting is deprecated and ignored.
|
// bufferSize setting is deprecated and ignored.
|
||||||
func SetClientSocketOptions(conn net.Conn, bufferSize int) error {
|
func SetClientSocketOptions(conn net.Conn, bufferSize int) error {
|
||||||
return setCommonSocketOptions(conn.(*net.TCPConn))
|
return setCommonSocketOptions(conn.(*net.TCPConn)) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetServerSocketOptions tunes a TCP socket that represents a connection to
|
// SetServerSocketOptions tunes a TCP socket that represents a connection to
|
||||||
// remote server like Telegram or fronting domain (but not end user).
|
// remote server like Telegram or fronting domain (but not end user).
|
||||||
func SetServerSocketOptions(conn net.Conn, bufferSize int) error {
|
func SetServerSocketOptions(conn net.Conn, bufferSize int) error {
|
||||||
return setCommonSocketOptions(conn.(*net.TCPConn))
|
return setCommonSocketOptions(conn.(*net.TCPConn)) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func setCommonSocketOptions(conn *net.TCPConn) error {
|
func setCommonSocketOptions(conn *net.TCPConn) error {
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ const (
|
|||||||
// Type: counter
|
// Type: counter
|
||||||
MetricReplayAttacks = "replay_attacks"
|
MetricReplayAttacks = "replay_attacks"
|
||||||
|
|
||||||
|
// MetricIPListSize defines a metric for the size of the the ip list.
|
||||||
|
//
|
||||||
|
// Type: gauge
|
||||||
|
// Tags:
|
||||||
|
// ip_list | 'allowlist' or 'blocklist'
|
||||||
|
MetricIPListSize = "iplist_size"
|
||||||
|
|
||||||
// TagIPFamily defines a name of the 'ip_family' tag and all values.
|
// TagIPFamily defines a name of the 'ip_family' tag and all values.
|
||||||
TagIPFamily = "ip_family"
|
TagIPFamily = "ip_family"
|
||||||
|
|
||||||
@@ -114,4 +121,13 @@ const (
|
|||||||
// TagDirectionFromClient defines that traffic is sent from a client to
|
// TagDirectionFromClient defines that traffic is sent from a client to
|
||||||
// Telegram.
|
// Telegram.
|
||||||
TagDirectionFromClient = "from_client"
|
TagDirectionFromClient = "from_client"
|
||||||
|
|
||||||
|
// TagIPList defines a name of the 'ip_list' and all values.
|
||||||
|
TagIPList = "ip_list"
|
||||||
|
|
||||||
|
// TagIPListAllow defines a value of 'ip_list' of allowlist.
|
||||||
|
TagIPListAllow = "allowlist"
|
||||||
|
|
||||||
|
// TagIPListBlock defines a value of 'ip_list' of blocklist.
|
||||||
|
TagIPListBlock = "blocklist"
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ var streamInfoPool = sync.Pool{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func acquireStreamInfo() *streamInfo {
|
func acquireStreamInfo() *streamInfo {
|
||||||
return streamInfoPool.Get().(*streamInfo)
|
return streamInfoPool.Get().(*streamInfo) // nolint: forcetypeassert
|
||||||
}
|
}
|
||||||
|
|
||||||
func releaseStreamInfo(info *streamInfo) {
|
func releaseStreamInfo(info *streamInfo) {
|
||||||
|
|||||||
+30
-9
@@ -110,14 +110,28 @@ func (p prometheusProcessor) EventConcurrencyLimited(_ mtglib.EventConcurrencyLi
|
|||||||
p.factory.metricConcurrencyLimited.Inc()
|
p.factory.metricConcurrencyLimited.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p prometheusProcessor) EventIPBlocklisted(_ mtglib.EventIPBlocklisted) {
|
func (p prometheusProcessor) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) {
|
||||||
p.factory.metricIPBlocklisted.Inc()
|
tag := TagIPListBlock
|
||||||
|
if !evt.IsBlockList {
|
||||||
|
tag = TagIPListAllow
|
||||||
|
}
|
||||||
|
|
||||||
|
p.factory.metricIPBlocklisted.WithLabelValues(tag).Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p prometheusProcessor) EventReplayAttack(_ mtglib.EventReplayAttack) {
|
func (p prometheusProcessor) EventReplayAttack(_ mtglib.EventReplayAttack) {
|
||||||
p.factory.metricReplayAttacks.Inc()
|
p.factory.metricReplayAttacks.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p prometheusProcessor) EventIPListSize(evt mtglib.EventIPListSize) {
|
||||||
|
tag := TagIPListBlock
|
||||||
|
if !evt.IsBlockList {
|
||||||
|
tag = TagIPListAllow
|
||||||
|
}
|
||||||
|
|
||||||
|
p.factory.metricIPListSize.WithLabelValues(tag).Set(float64(evt.Size))
|
||||||
|
}
|
||||||
|
|
||||||
func (p prometheusProcessor) Shutdown() {
|
func (p prometheusProcessor) Shutdown() {
|
||||||
for k, v := range p.streams {
|
for k, v := range p.streams {
|
||||||
releaseStreamInfo(v)
|
releaseStreamInfo(v)
|
||||||
@@ -137,13 +151,14 @@ type PrometheusFactory struct {
|
|||||||
metricClientConnections *prometheus.GaugeVec
|
metricClientConnections *prometheus.GaugeVec
|
||||||
metricTelegramConnections *prometheus.GaugeVec
|
metricTelegramConnections *prometheus.GaugeVec
|
||||||
metricDomainFrontingConnections *prometheus.GaugeVec
|
metricDomainFrontingConnections *prometheus.GaugeVec
|
||||||
|
metricIPListSize *prometheus.GaugeVec
|
||||||
|
|
||||||
metricTelegramTraffic *prometheus.CounterVec
|
metricTelegramTraffic *prometheus.CounterVec
|
||||||
metricDomainFrontingTraffic *prometheus.CounterVec
|
metricDomainFrontingTraffic *prometheus.CounterVec
|
||||||
|
metricIPBlocklisted *prometheus.CounterVec
|
||||||
|
|
||||||
metricDomainFronting prometheus.Counter
|
metricDomainFronting prometheus.Counter
|
||||||
metricConcurrencyLimited prometheus.Counter
|
metricConcurrencyLimited prometheus.Counter
|
||||||
metricIPBlocklisted prometheus.Counter
|
|
||||||
metricReplayAttacks prometheus.Counter
|
metricReplayAttacks prometheus.Counter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +212,11 @@ func NewPrometheus(metricPrefix, httpPath string) *PrometheusFactory { // nolint
|
|||||||
Name: MetricDomainFrontingConnections,
|
Name: MetricDomainFrontingConnections,
|
||||||
Help: "A number of connections which talk to front domain.",
|
Help: "A number of connections which talk to front domain.",
|
||||||
}, []string{TagIPFamily}),
|
}, []string{TagIPFamily}),
|
||||||
|
metricIPListSize: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
|
Namespace: metricPrefix,
|
||||||
|
Name: MetricIPListSize,
|
||||||
|
Help: "A size of the ip list (blocklist or allowlist)",
|
||||||
|
}, []string{TagIPList}),
|
||||||
|
|
||||||
metricTelegramTraffic: prometheus.NewCounterVec(prometheus.CounterOpts{
|
metricTelegramTraffic: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||||
Namespace: metricPrefix,
|
Namespace: metricPrefix,
|
||||||
@@ -208,6 +228,11 @@ func NewPrometheus(metricPrefix, httpPath string) *PrometheusFactory { // nolint
|
|||||||
Name: MetricDomainFrontingTraffic,
|
Name: MetricDomainFrontingTraffic,
|
||||||
Help: "Traffic which is generated talking with front domain.",
|
Help: "Traffic which is generated talking with front domain.",
|
||||||
}, []string{TagDirection}),
|
}, []string{TagDirection}),
|
||||||
|
metricIPBlocklisted: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||||
|
Namespace: metricPrefix,
|
||||||
|
Name: MetricIPBlocklisted,
|
||||||
|
Help: "A number of rejected sessions due to ip blocklisting.",
|
||||||
|
}, []string{TagIPList}),
|
||||||
|
|
||||||
metricDomainFronting: prometheus.NewCounter(prometheus.CounterOpts{
|
metricDomainFronting: prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
Namespace: metricPrefix,
|
Namespace: metricPrefix,
|
||||||
@@ -219,11 +244,6 @@ func NewPrometheus(metricPrefix, httpPath string) *PrometheusFactory { // nolint
|
|||||||
Name: MetricConcurrencyLimited,
|
Name: MetricConcurrencyLimited,
|
||||||
Help: "A number of sessions that were rejected by concurrency limiter.",
|
Help: "A number of sessions that were rejected by concurrency limiter.",
|
||||||
}),
|
}),
|
||||||
metricIPBlocklisted: prometheus.NewCounter(prometheus.CounterOpts{
|
|
||||||
Namespace: metricPrefix,
|
|
||||||
Name: MetricIPBlocklisted,
|
|
||||||
Help: "A number of rejected sessions due to ip blocklisting.",
|
|
||||||
}),
|
|
||||||
metricReplayAttacks: prometheus.NewCounter(prometheus.CounterOpts{
|
metricReplayAttacks: prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
Namespace: metricPrefix,
|
Namespace: metricPrefix,
|
||||||
Name: MetricReplayAttacks,
|
Name: MetricReplayAttacks,
|
||||||
@@ -234,13 +254,14 @@ func NewPrometheus(metricPrefix, httpPath string) *PrometheusFactory { // nolint
|
|||||||
registry.MustRegister(factory.metricClientConnections)
|
registry.MustRegister(factory.metricClientConnections)
|
||||||
registry.MustRegister(factory.metricTelegramConnections)
|
registry.MustRegister(factory.metricTelegramConnections)
|
||||||
registry.MustRegister(factory.metricDomainFrontingConnections)
|
registry.MustRegister(factory.metricDomainFrontingConnections)
|
||||||
|
registry.MustRegister(factory.metricIPListSize)
|
||||||
|
|
||||||
registry.MustRegister(factory.metricTelegramTraffic)
|
registry.MustRegister(factory.metricTelegramTraffic)
|
||||||
registry.MustRegister(factory.metricDomainFrontingTraffic)
|
registry.MustRegister(factory.metricDomainFrontingTraffic)
|
||||||
|
registry.MustRegister(factory.metricIPBlocklisted)
|
||||||
|
|
||||||
registry.MustRegister(factory.metricDomainFronting)
|
registry.MustRegister(factory.metricDomainFronting)
|
||||||
registry.MustRegister(factory.metricConcurrencyLimited)
|
registry.MustRegister(factory.metricConcurrencyLimited)
|
||||||
registry.MustRegister(factory.metricIPBlocklisted)
|
|
||||||
registry.MustRegister(factory.metricReplayAttacks)
|
registry.MustRegister(factory.metricReplayAttacks)
|
||||||
|
|
||||||
return factory
|
return factory
|
||||||
|
|||||||
@@ -156,7 +156,18 @@ func (suite *PrometheusTestSuite) TestEventIPBlocklisted() {
|
|||||||
|
|
||||||
data, err := suite.Get()
|
data, err := suite.Get()
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Contains(data, `mtg_ip_blocklisted 1`)
|
suite.Contains(data, `mtg_ip_blocklisted{ip_list="blocklist"} 1`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *PrometheusTestSuite) TestEventIPAllowlisted() {
|
||||||
|
suite.prometheus.EventIPBlocklisted(
|
||||||
|
mtglib.NewEventIPAllowlisted(net.ParseIP("2001:db8::68")))
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
data, err := suite.Get()
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Contains(data, `mtg_ip_blocklisted{ip_list="allowlist"} 1`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PrometheusTestSuite) TestEventReplayAttack() {
|
func (suite *PrometheusTestSuite) TestEventReplayAttack() {
|
||||||
@@ -169,6 +180,18 @@ func (suite *PrometheusTestSuite) TestEventReplayAttack() {
|
|||||||
suite.Contains(data, `mtg_replay_attacks 1`)
|
suite.Contains(data, `mtg_replay_attacks 1`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *PrometheusTestSuite) TestEventIPListSize() {
|
||||||
|
suite.prometheus.EventIPListSize(mtglib.NewEventIPListSize(10, false))
|
||||||
|
suite.prometheus.EventIPListSize(mtglib.NewEventIPListSize(3, true))
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
data, err := suite.Get()
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Contains(data, `mtg_iplist_size{ip_list="allowlist"} 10`)
|
||||||
|
suite.Contains(data, `mtg_iplist_size{ip_list="blocklist"} 3`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrometheus(t *testing.T) {
|
func TestPrometheus(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
suite.Run(t, &PrometheusTestSuite{})
|
suite.Run(t, &PrometheusTestSuite{})
|
||||||
|
|||||||
+18
-3
@@ -113,14 +113,28 @@ func (s statsdProcessor) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimite
|
|||||||
s.client.Incr(MetricConcurrencyLimited, 1)
|
s.client.Incr(MetricConcurrencyLimited, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s statsdProcessor) EventIPBlocklisted(_ mtglib.EventIPBlocklisted) {
|
func (s statsdProcessor) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) {
|
||||||
s.client.Incr(MetricIPBlocklisted, 1)
|
tag := TagIPListBlock
|
||||||
|
if !evt.IsBlockList {
|
||||||
|
tag = TagIPListAllow
|
||||||
|
}
|
||||||
|
|
||||||
|
s.client.Incr(MetricIPBlocklisted, 1, statsd.StringTag(TagIPList, tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s statsdProcessor) EventReplayAttack(_ mtglib.EventReplayAttack) {
|
func (s statsdProcessor) EventReplayAttack(_ mtglib.EventReplayAttack) {
|
||||||
s.client.Incr(MetricReplayAttacks, 1)
|
s.client.Incr(MetricReplayAttacks, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s statsdProcessor) EventIPListSize(evt mtglib.EventIPListSize) {
|
||||||
|
tag := TagIPListBlock
|
||||||
|
if !evt.IsBlockList {
|
||||||
|
tag = TagIPListAllow
|
||||||
|
}
|
||||||
|
|
||||||
|
s.client.Gauge(MetricIPListSize, int64(evt.Size), statsd.StringTag(TagIPList, tag))
|
||||||
|
}
|
||||||
|
|
||||||
func (s statsdProcessor) Shutdown() {
|
func (s statsdProcessor) Shutdown() {
|
||||||
events := make([]mtglib.EventFinish, 0, len(s.streams))
|
events := make([]mtglib.EventFinish, 0, len(s.streams))
|
||||||
|
|
||||||
@@ -162,7 +176,8 @@ func (s StatsdFactory) Make() events.Observer {
|
|||||||
//
|
//
|
||||||
// Valid tagFormats are 'datadog', 'influxdb' and 'graphite'.
|
// Valid tagFormats are 'datadog', 'influxdb' and 'graphite'.
|
||||||
func NewStatsd(address string, log logger.StdLikeLogger,
|
func NewStatsd(address string, log logger.StdLikeLogger,
|
||||||
metricPrefix, tagFormat string) (StatsdFactory, error) {
|
metricPrefix, tagFormat string,
|
||||||
|
) (StatsdFactory, error) {
|
||||||
options := []statsd.Option{
|
options := []statsd.Option{
|
||||||
statsd.MetricPrefix(metricPrefix),
|
statsd.MetricPrefix(metricPrefix),
|
||||||
statsd.Logger(log),
|
statsd.Logger(log),
|
||||||
|
|||||||
+25
-1
@@ -186,7 +186,15 @@ func (suite *StatsdTestSuite) TestEventIPBlocklisted() {
|
|||||||
mtglib.NewEventIPBlocklisted(net.ParseIP("10.0.0.10")))
|
mtglib.NewEventIPBlocklisted(net.ParseIP("10.0.0.10")))
|
||||||
|
|
||||||
time.Sleep(statsdSleepTime)
|
time.Sleep(statsdSleepTime)
|
||||||
suite.Equal("mtg.ip_blocklisted:1|c", suite.statsdServer.String())
|
suite.Equal("mtg.ip_blocklisted:1|c|#ip_list:blocklist", suite.statsdServer.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *StatsdTestSuite) TestEventIPAllowlisted() {
|
||||||
|
suite.statsd.EventIPBlocklisted(
|
||||||
|
mtglib.NewEventIPAllowlisted(net.ParseIP("10.0.0.10")))
|
||||||
|
|
||||||
|
time.Sleep(statsdSleepTime)
|
||||||
|
suite.Equal("mtg.ip_blocklisted:1|c|#ip_list:allowlist", suite.statsdServer.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *StatsdTestSuite) TestEventReplayAttack() {
|
func (suite *StatsdTestSuite) TestEventReplayAttack() {
|
||||||
@@ -196,6 +204,22 @@ func (suite *StatsdTestSuite) TestEventReplayAttack() {
|
|||||||
suite.Equal("mtg.replay_attacks:1|c", suite.statsdServer.String())
|
suite.Equal("mtg.replay_attacks:1|c", suite.statsdServer.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *StatsdTestSuite) TestEventIPListSizeAllowlist() {
|
||||||
|
suite.statsd.EventIPListSize(mtglib.NewEventIPListSize(10, false))
|
||||||
|
|
||||||
|
time.Sleep(statsdSleepTime)
|
||||||
|
suite.Contains(suite.statsdServer.String(), "mtg.iplist_size:10|g")
|
||||||
|
suite.Contains(suite.statsdServer.String(), "allowlist")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *StatsdTestSuite) TestEventIPListSizeBlocklist() {
|
||||||
|
suite.statsd.EventIPListSize(mtglib.NewEventIPListSize(10, true))
|
||||||
|
|
||||||
|
time.Sleep(statsdSleepTime)
|
||||||
|
suite.Contains(suite.statsdServer.String(), "mtg.iplist_size:10|g")
|
||||||
|
suite.Contains(suite.statsdServer.String(), "blocklist")
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatsd(t *testing.T) {
|
func TestStatsd(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
suite.Run(t, &StatsdTestSuite{})
|
suite.Run(t, &StatsdTestSuite{})
|
||||||
|
|||||||
Reference in New Issue
Block a user