mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 16:14:02 +03:00
REPOSITORY / ScuroNeko/mtg
Compare commits
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b854b16e1a | ||
|
|
f4b296d1e0 | ||
|
|
432e530f68 | ||
|
|
a2bf9a269a | ||
|
|
cf3437bb63 | ||
|
|
d0e99dda2b | ||
|
|
ac3bd16e83 | ||
|
|
222cef8c41 | ||
|
|
ed5da2864a | ||
|
|
08393e426a | ||
|
|
0440ccf4ff | ||
|
|
a0aabf2391 | ||
|
|
3b03c4a90a | ||
|
|
80b9159ce9 | ||
|
|
bdabb0e59a | ||
|
|
543f5cde9c | ||
|
|
1a247d18b1 | ||
|
|
5a63c7d5b4 | ||
|
|
e7fdb02a29 | ||
|
|
7a6ba6d8c6 | ||
|
|
be398f9c79 | ||
|
|
4c029fc683 | ||
|
|
3066672353 | ||
|
|
ae88c0cab0 | ||
|
|
816b7f72b6 | ||
|
|
1f7584c108 | ||
|
|
2fb025bbfd | ||
|
|
c7ab53134a | ||
|
|
f83ee17361 | ||
|
|
36546cec2f | ||
|
|
074c4017f5 | ||
|
|
8e87405d3e | ||
|
|
82679ec20f | ||
|
|
308e372a5d | ||
|
|
836a481026 | ||
|
|
0a5a45b32d | ||
|
|
852ca713c8 | ||
|
|
301bde88ac | ||
|
|
25bca76da5 |
@@ -93,6 +93,9 @@ jobs:
|
|||||||
name: Docker
|
name: Docker
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
@@ -103,9 +106,16 @@ jobs:
|
|||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: nineseconds/mtg
|
images: |
|
||||||
|
nineseconds/mtg
|
||||||
|
ghcr.io/${{ github.repository }}
|
||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
type=raw,value=master,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
type=raw,value=stable,enable=${{ github.ref == 'refs/heads/stable' }}
|
||||||
|
|
||||||
- name: Setup QEMU
|
- name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
@@ -128,6 +138,14 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
+5
-4
@@ -1,5 +1,10 @@
|
|||||||
[tools]
|
[tools]
|
||||||
|
"go:golang.org/x/pkgsite/cmd/pkgsite" = "latest"
|
||||||
|
"go:golang.org/x/tools/gopls" = "latest"
|
||||||
|
"go:mvdan.cc/gofumpt" = "latest"
|
||||||
go = "latest"
|
go = "latest"
|
||||||
|
golangci-lint = "latest"
|
||||||
|
goreleaser = "latest"
|
||||||
|
|
||||||
[vars]
|
[vars]
|
||||||
fuzzflags = "-fuzztime=120s"
|
fuzzflags = "-fuzztime=120s"
|
||||||
@@ -19,7 +24,6 @@ run = [
|
|||||||
|
|
||||||
[tasks.lint]
|
[tasks.lint]
|
||||||
description = "Run linter"
|
description = "Run linter"
|
||||||
tools.golangci-lint = "latest"
|
|
||||||
run = "golangci-lint run"
|
run = "golangci-lint run"
|
||||||
|
|
||||||
[tasks.test]
|
[tasks.test]
|
||||||
@@ -75,7 +79,6 @@ go build \
|
|||||||
|
|
||||||
[tasks.release]
|
[tasks.release]
|
||||||
description = "Create release tarballs"
|
description = "Create release tarballs"
|
||||||
tools.goreleaser = "latest"
|
|
||||||
sources = ["**/*.go", "go.mod", "go.sum", ".goreleaser.yml"]
|
sources = ["**/*.go", "go.mod", "go.sum", ".goreleaser.yml"]
|
||||||
run = [
|
run = [
|
||||||
"goreleaser --snapshot --clean",
|
"goreleaser --snapshot --clean",
|
||||||
@@ -90,11 +93,9 @@ run = "docker buildx build --pull -t mtg ."
|
|||||||
|
|
||||||
[tasks.docs]
|
[tasks.docs]
|
||||||
description = "Run doc server"
|
description = "Run doc server"
|
||||||
tools."go:golang.org/x/pkgsite/cmd/pkgsite" = "latest"
|
|
||||||
run = "pkgsite -http 0.0.0.0:10000"
|
run = "pkgsite -http 0.0.0.0:10000"
|
||||||
|
|
||||||
[tasks.fmt]
|
[tasks.fmt]
|
||||||
description = "Reformat source code"
|
description = "Reformat source code"
|
||||||
tools.gofumpt = "latest"
|
|
||||||
sources = ["**/*.go"]
|
sources = ["**/*.go"]
|
||||||
run = "gofumpt -w --extra ."
|
run = "gofumpt -w --extra ."
|
||||||
|
|||||||
@@ -10,6 +10,17 @@ Highly-opinionated (ex-bullshit-free) MTPROTO proxy for
|
|||||||
**If you use v1.0 or upgrade broke you proxy, please read the chapter
|
**If you use v1.0 or upgrade broke you proxy, please read the chapter
|
||||||
[Version 2](#version-2)**
|
[Version 2](#version-2)**
|
||||||
|
|
||||||
|
If you want to have a proxy that _supports adtag_ (possibility to promote a
|
||||||
|
channel with a special Telegram bot), I recommend to use
|
||||||
|
[telemt](https://github.com/telemt/telemt) project. v1 of mtg supports it
|
||||||
|
but I do not see any reasonable point of using it: adtag requires communication
|
||||||
|
via a fragile set of middle proxies, requires complex setup that must expose
|
||||||
|
a public IPs, has lower bandwidth and latency.
|
||||||
|
|
||||||
|
mtg idea is simple: minimal unbloated proxy that can handle a reasonable scale
|
||||||
|
~10-20k simultaneous connections, has no user management, but ticks all
|
||||||
|
checkboxes related to its main intent: provide a way to use Telegram.
|
||||||
|
|
||||||
## Rationale
|
## Rationale
|
||||||
|
|
||||||
There are several available proxies for Telegram MTPROTO available. Here
|
There are several available proxies for Telegram MTPROTO available. Here
|
||||||
@@ -18,6 +29,7 @@ are the most notable:
|
|||||||
* [Official](https://github.com/TelegramMessenger/MTProxy)
|
* [Official](https://github.com/TelegramMessenger/MTProxy)
|
||||||
* [Python](https://github.com/alexbers/mtprotoproxy)
|
* [Python](https://github.com/alexbers/mtprotoproxy)
|
||||||
* [Erlang](https://github.com/seriyps/mtproto_proxy)
|
* [Erlang](https://github.com/seriyps/mtproto_proxy)
|
||||||
|
* [Telemt (Rust)](https://github.com/telemt/telemt)
|
||||||
|
|
||||||
You can use any of these. They work great and all implementations have
|
You can use any of these. They work great and all implementations have
|
||||||
feature parity now. This includes support of adtag, replay attack
|
feature parity now. This includes support of adtag, replay attack
|
||||||
@@ -40,6 +52,12 @@ that probably matter.
|
|||||||
way of doing business I suppose. I think the only viable way is to
|
way of doing business I suppose. I think the only viable way is to
|
||||||
have a proxy that can be restored anywhere easily.
|
have a proxy that can be restored anywhere easily.
|
||||||
|
|
||||||
|
* **Supports proxy protocol v1/v2**
|
||||||
|
|
||||||
|
This makes integration with loadbalancers like HAProxy and ELB a first class
|
||||||
|
citizen by supporting their
|
||||||
|
[commuication protocols](https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt).
|
||||||
|
|
||||||
* **A single secret**
|
* **A single secret**
|
||||||
|
|
||||||
I think that multiple secrets solve no problems and just complex
|
I think that multiple secrets solve no problems and just complex
|
||||||
@@ -162,6 +180,12 @@ This project has several main branches
|
|||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
|
### Download mise
|
||||||
|
|
||||||
|
mtg uses [mise](https://mise.jdx.dev/) to maintain its development
|
||||||
|
dependencies + replaces a make for building things. Please
|
||||||
|
[install](https://mise.jdx.dev/getting-started.html) it first.
|
||||||
|
|
||||||
### Download a tool
|
### Download a tool
|
||||||
|
|
||||||
#### Download binaries
|
#### Download binaries
|
||||||
@@ -198,13 +222,14 @@ go install github.com/9seconds/mtg/v2@latest
|
|||||||
```console
|
```console
|
||||||
git clone https://github.com/9seconds/mtg.git
|
git clone https://github.com/9seconds/mtg.git
|
||||||
cd mtg
|
cd mtg
|
||||||
make static
|
mise install
|
||||||
|
mise tasks run build
|
||||||
```
|
```
|
||||||
|
|
||||||
or for the docker image:
|
or for the docker image:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
make docker
|
mise tasks run image
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generate secret
|
### Generate secret
|
||||||
@@ -275,7 +300,7 @@ Flags:
|
|||||||
-b, --tcp-buffer="4KB" Size of TCP buffer to use.
|
-b, --tcp-buffer="4KB" Size of TCP buffer to use.
|
||||||
-i, --prefer-ip="prefer-ipv6" IP preference. By default we prefer IPv6 with fallback to IPv4.
|
-i, --prefer-ip="prefer-ipv6" IP preference. By default we prefer IPv6 with fallback to IPv4.
|
||||||
-p, --domain-fronting-port=443 A port to access for domain fronting.
|
-p, --domain-fronting-port=443 A port to access for domain fronting.
|
||||||
-n, --doh-ip=9.9.9.9 IP address of DNS-over-HTTP to use.
|
-n, --doh-ip=1.1.1.1 IP address of DNS-over-HTTP to use.
|
||||||
-t, --timeout=10s Network timeout to use
|
-t, --timeout=10s Network timeout to use
|
||||||
-a, --antireplay-cache-size="1MB" A size of anti-replay cache to use.
|
-a, --antireplay-cache-size="1MB" A size of anti-replay cache to use.
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package events
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/mtglib"
|
"github.com/9seconds/mtg/v2/mtglib"
|
||||||
@@ -64,7 +64,7 @@ func NewEventStream(observerFactories []ObserverFactory) EventStream {
|
|||||||
chans: make([]chan mtglib.Event, runtime.NumCPU()),
|
chans: make([]chan mtglib.Event, runtime.NumCPU()),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < runtime.NumCPU(); i++ {
|
for i := range runtime.NumCPU() {
|
||||||
rv.chans[i] = make(chan mtglib.Event, 1)
|
rv.chans[i] = make(chan mtglib.Event, 1)
|
||||||
|
|
||||||
if len(observerFactories) == 1 {
|
if len(observerFactories) == 1 {
|
||||||
|
|||||||
+27
-54
@@ -12,14 +12,11 @@ type multiObserver struct {
|
|||||||
|
|
||||||
func (m multiObserver) EventStart(evt mtglib.EventStart) {
|
func (m multiObserver) EventStart(evt mtglib.EventStart) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventStart(evt)
|
||||||
|
})
|
||||||
obs.EventStart(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -27,14 +24,11 @@ func (m multiObserver) EventStart(evt mtglib.EventStart) {
|
|||||||
|
|
||||||
func (m multiObserver) EventConnectedToDC(evt mtglib.EventConnectedToDC) {
|
func (m multiObserver) EventConnectedToDC(evt mtglib.EventConnectedToDC) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventConnectedToDC(evt)
|
||||||
|
})
|
||||||
obs.EventConnectedToDC(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -42,14 +36,11 @@ func (m multiObserver) EventConnectedToDC(evt mtglib.EventConnectedToDC) {
|
|||||||
|
|
||||||
func (m multiObserver) EventDomainFronting(evt mtglib.EventDomainFronting) {
|
func (m multiObserver) EventDomainFronting(evt mtglib.EventDomainFronting) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventDomainFronting(evt)
|
||||||
|
})
|
||||||
obs.EventDomainFronting(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -57,14 +48,11 @@ func (m multiObserver) EventDomainFronting(evt mtglib.EventDomainFronting) {
|
|||||||
|
|
||||||
func (m multiObserver) EventTraffic(evt mtglib.EventTraffic) {
|
func (m multiObserver) EventTraffic(evt mtglib.EventTraffic) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventTraffic(evt)
|
||||||
|
})
|
||||||
obs.EventTraffic(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -72,14 +60,11 @@ func (m multiObserver) EventTraffic(evt mtglib.EventTraffic) {
|
|||||||
|
|
||||||
func (m multiObserver) EventFinish(evt mtglib.EventFinish) {
|
func (m multiObserver) EventFinish(evt mtglib.EventFinish) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventFinish(evt)
|
||||||
|
})
|
||||||
obs.EventFinish(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -87,14 +72,11 @@ func (m multiObserver) EventFinish(evt mtglib.EventFinish) {
|
|||||||
|
|
||||||
func (m multiObserver) EventConcurrencyLimited(evt mtglib.EventConcurrencyLimited) {
|
func (m multiObserver) EventConcurrencyLimited(evt mtglib.EventConcurrencyLimited) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventConcurrencyLimited(evt)
|
||||||
|
})
|
||||||
obs.EventConcurrencyLimited(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -102,14 +84,11 @@ func (m multiObserver) EventConcurrencyLimited(evt mtglib.EventConcurrencyLimite
|
|||||||
|
|
||||||
func (m multiObserver) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) {
|
func (m multiObserver) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventIPBlocklisted(evt)
|
||||||
|
})
|
||||||
obs.EventIPBlocklisted(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -117,14 +96,11 @@ func (m multiObserver) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) {
|
|||||||
|
|
||||||
func (m multiObserver) EventReplayAttack(evt mtglib.EventReplayAttack) {
|
func (m multiObserver) EventReplayAttack(evt mtglib.EventReplayAttack) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventReplayAttack(evt)
|
||||||
|
})
|
||||||
obs.EventReplayAttack(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -132,14 +108,11 @@ func (m multiObserver) EventReplayAttack(evt mtglib.EventReplayAttack) {
|
|||||||
|
|
||||||
func (m multiObserver) EventIPListSize(evt mtglib.EventIPListSize) {
|
func (m multiObserver) EventIPListSize(evt mtglib.EventIPListSize) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(m.observers))
|
|
||||||
|
|
||||||
for _, v := range m.observers {
|
for _, v := range m.observers {
|
||||||
go func(obs Observer) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
v.EventIPListSize(evt)
|
||||||
|
})
|
||||||
obs.EventIPListSize(evt)
|
|
||||||
}(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
+20
-2
@@ -23,6 +23,15 @@ secret = "ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c6561706973
|
|||||||
# Host:port pair to run proxy on.
|
# Host:port pair to run proxy on.
|
||||||
bind-to = "0.0.0.0:3128"
|
bind-to = "0.0.0.0:3128"
|
||||||
|
|
||||||
|
# This defines what types of traffic mtg listens to. If you are not sure,
|
||||||
|
# then definitely keep it disable. Enable it only and only if incoming traffic
|
||||||
|
# is coming from some sort of load-balancer like HAProxy or ELB.
|
||||||
|
# https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt
|
||||||
|
#
|
||||||
|
# mtg uses a library that supports v1 and v2 versions of ProxyProtocol.
|
||||||
|
# default value is false.
|
||||||
|
# proxy-protocol-listener = false
|
||||||
|
|
||||||
# Defines how many concurrent connections are allowed to this proxy.
|
# Defines how many concurrent connections are allowed to this proxy.
|
||||||
# All other incoming connections are going to be dropped.
|
# All other incoming connections are going to be dropped.
|
||||||
concurrency = 8192
|
concurrency = 8192
|
||||||
@@ -71,6 +80,15 @@ tolerate-time-skewness = "5s"
|
|||||||
# Otherwise, chose a new DC.
|
# Otherwise, chose a new DC.
|
||||||
allow-fallback-on-unknown-dc = false
|
allow-fallback-on-unknown-dc = false
|
||||||
|
|
||||||
|
# Telegram uses different DCs for different purposes. Unfortunately, most of
|
||||||
|
# DCs are not public, and dependent on a location of the current user, so
|
||||||
|
# mtg cannot know upfront about all of them, and how to access them. It has
|
||||||
|
# a default list of DCs, including some CDN IPs, but it is possible that some
|
||||||
|
# of them are not working for you. In this case, you can override them here.
|
||||||
|
[[dc-overrides]]
|
||||||
|
dc = 101
|
||||||
|
ips = ["127.0.0.1:443"]
|
||||||
|
|
||||||
# network defines different network-related settings
|
# network defines different network-related settings
|
||||||
[network]
|
[network]
|
||||||
# please be aware that mtg needs to do some external requests. For
|
# please be aware that mtg needs to do some external requests. For
|
||||||
@@ -84,8 +102,8 @@ allow-fallback-on-unknown-dc = false
|
|||||||
# resolver of the operating system and uses DOH instead. This is a host
|
# resolver of the operating system and uses DOH instead. This is a host
|
||||||
# it has to access.
|
# it has to access.
|
||||||
#
|
#
|
||||||
# By default we use Quad9.
|
# By default we use Cloudflare.
|
||||||
doh-ip = "9.9.9.9"
|
doh-ip = "1.1.1.1"
|
||||||
|
|
||||||
# mtg can work via proxies (for now, we support only socks5). Proxy
|
# mtg can work via proxies (for now, we support only socks5). Proxy
|
||||||
# configuration is done via list. So, you can specify many proxies
|
# configuration is done via list. So, you can specify many proxies
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ require (
|
|||||||
github.com/jarcoal/httpmock v1.0.8
|
github.com/jarcoal/httpmock v1.0.8
|
||||||
github.com/mccutchen/go-httpbin v1.1.1
|
github.com/mccutchen/go-httpbin v1.1.1
|
||||||
github.com/panjf2000/ants/v2 v2.11.5
|
github.com/panjf2000/ants/v2 v2.11.5
|
||||||
github.com/pelletier/go-toml v1.9.5
|
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
@@ -28,6 +27,8 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4
|
||||||
|
github.com/pires/go-proxyproto v0.11.0
|
||||||
github.com/txthinking/socks5 v0.0.0-20251011041537-5c31f201a10e
|
github.com/txthinking/socks5 v0.0.0-20251011041537-5c31f201a10e
|
||||||
github.com/yl2chen/cidranger v1.0.2
|
github.com/yl2chen/cidranger v1.0.2
|
||||||
)
|
)
|
||||||
@@ -36,6 +37,7 @@ require (
|
|||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.3 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
@@ -43,8 +45,10 @@ require (
|
|||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // 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.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
github.com/txthinking/runnergroup v0.0.0-20250224021307-5864ffeb65ae // indirect
|
github.com/txthinking/runnergroup v0.0.0-20250224021307-5864ffeb65ae // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
|
golang.org/x/tools v0.41.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
|
|||||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
|
github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
|
||||||
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@@ -55,8 +55,10 @@ github.com/panjf2000/ants/v2 v2.11.5 h1:a7LMnMEeux/ebqTux140tRiaqcFTV0q2bEHF03nl
|
|||||||
github.com/panjf2000/ants/v2 v2.11.5/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
|
github.com/panjf2000/ants/v2 v2.11.5/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
|
||||||
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.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4=
|
||||||
|
github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@@ -68,8 +70,8 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU
|
|||||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||||
@@ -106,8 +108,9 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
|
||||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||||
|
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
@@ -140,8 +143,9 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
|
|
||||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||||
|
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||||
|
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
|||||||
+4
-10
@@ -61,11 +61,8 @@ func (a *Access) Run(cli *CLI, version string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(2)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
|
wg.Go(func() {
|
||||||
ip := a.PublicIPv4
|
ip := a.PublicIPv4
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
ip = a.getIP(ntw, "tcp4")
|
ip = a.getIP(ntw, "tcp4")
|
||||||
@@ -76,11 +73,8 @@ func (a *Access) Run(cli *CLI, version string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp.IPv4 = a.makeURLs(conf, ip)
|
resp.IPv4 = a.makeURLs(conf, ip)
|
||||||
}()
|
})
|
||||||
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
ip := a.PublicIPv6
|
ip := a.PublicIPv6
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
ip = a.getIP(ntw, "tcp6")
|
ip = a.getIP(ntw, "tcp6")
|
||||||
@@ -91,7 +85,7 @@ func (a *Access) Run(cli *CLI, version string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp.IPv6 = a.makeURLs(conf, ip)
|
resp.IPv6 = a.makeURLs(conf, ip)
|
||||||
}()
|
})
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,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/internal/config"
|
"github.com/9seconds/mtg/v2/internal/config"
|
||||||
|
"github.com/9seconds/mtg/v2/internal/proxyprotocol"
|
||||||
"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/ipblocklist/files"
|
||||||
@@ -17,6 +18,7 @@ import (
|
|||||||
"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/pires/go-proxyproto"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/yl2chen/cidranger"
|
"github.com/yl2chen/cidranger"
|
||||||
)
|
)
|
||||||
@@ -240,6 +242,14 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
|
|||||||
return fmt.Errorf("cannot build ip allowlist: %w", err)
|
return fmt.Errorf("cannot build ip allowlist: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dcOverrides := map[int][]string{}
|
||||||
|
for _, override := range conf.DCOverrides {
|
||||||
|
dcid := override.DC.Get()
|
||||||
|
for _, addr := range override.IPs {
|
||||||
|
dcOverrides[dcid] = append(dcOverrides[dcid], addr.Get(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opts := mtglib.ProxyOpts{
|
opts := mtglib.ProxyOpts{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Network: ntw,
|
Network: ntw,
|
||||||
@@ -254,6 +264,7 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
|
|||||||
|
|
||||||
AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
|
AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
|
||||||
TolerateTimeSkewness: conf.TolerateTimeSkewness.Value,
|
TolerateTimeSkewness: conf.TolerateTimeSkewness.Value,
|
||||||
|
DCOverrides: dcOverrides,
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy, err := mtglib.NewProxy(opts)
|
proxy, err := mtglib.NewProxy(opts)
|
||||||
@@ -266,6 +277,14 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
|
|||||||
return fmt.Errorf("cannot start proxy: %w", err)
|
return fmt.Errorf("cannot start proxy: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.ProxyProtocolListener.Get(false) {
|
||||||
|
listener = &proxyprotocol.ListenerAdapter{
|
||||||
|
Listener: proxyproto.Listener{
|
||||||
|
Listener: listener,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx := utils.RootContext()
|
ctx := utils.RootContext()
|
||||||
|
|
||||||
go proxy.Serve(listener) //nolint: errcheck
|
go proxy.Serve(listener) //nolint: errcheck
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type SimpleRun struct {
|
|||||||
TCPBuffer string `kong:"name='tcp-buffer',short='b',default='4KB',help='Deprecated and ignored'"` //nolint: lll
|
TCPBuffer string `kong:"name='tcp-buffer',short='b',default='4KB',help='Deprecated and ignored'"` //nolint: lll
|
||||||
PreferIP string `kong:"name='prefer-ip',short='i',default='prefer-ipv6',help='IP preference. By default we prefer IPv6 with fallback to IPv4.'"` //nolint: lll
|
PreferIP string `kong:"name='prefer-ip',short='i',default='prefer-ipv6',help='IP preference. By default we prefer IPv6 with fallback to IPv4.'"` //nolint: lll
|
||||||
DomainFrontingPort uint64 `kong:"name='domain-fronting-port',short='p',default='443',help='A port to access for domain fronting.'"` //nolint: lll
|
DomainFrontingPort uint64 `kong:"name='domain-fronting-port',short='p',default='443',help='A port to access for domain fronting.'"` //nolint: lll
|
||||||
DOHIP net.IP `kong:"name='doh-ip',short='n',default='9.9.9.9',help='IP address of DNS-over-HTTP to use.'"` //nolint: lll
|
DOHIP net.IP `kong:"name='doh-ip',short='n',default='1.1.1.1',help='IP address of DNS-over-HTTP to use.'"` //nolint: lll
|
||||||
Timeout time.Duration `kong:"name='timeout',short='t',default='10s',help='Network timeout to use'"` //nolint: lll
|
Timeout time.Duration `kong:"name='timeout',short='t',default='10s',help='Network timeout to use'"` //nolint: lll
|
||||||
Socks5Proxies []string `kong:"name='socks5-proxy',short='s',help='Socks5 proxies to use for network access.'"` //nolint: lll
|
Socks5Proxies []string `kong:"name='socks5-proxy',short='s',help='Socks5 proxies to use for network access.'"` //nolint: lll
|
||||||
AntiReplayCacheSize string `kong:"name='antireplay-cache-size',short='a',default='1MB',help='A size of anti-replay cache to use.'"` //nolint: lll
|
AntiReplayCacheSize string `kong:"name='antireplay-cache-size',short='a',default='1MB',help='A size of anti-replay cache to use.'"` //nolint: lll
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type Config struct {
|
|||||||
AllowFallbackOnUnknownDC TypeBool `json:"allowFallbackOnUnknownDc"`
|
AllowFallbackOnUnknownDC TypeBool `json:"allowFallbackOnUnknownDc"`
|
||||||
Secret mtglib.Secret `json:"secret"`
|
Secret mtglib.Secret `json:"secret"`
|
||||||
BindTo TypeHostPort `json:"bindTo"`
|
BindTo TypeHostPort `json:"bindTo"`
|
||||||
|
ProxyProtocolListener TypeBool `json:"proxyProtocolListener"`
|
||||||
PreferIP TypePreferIP `json:"preferIp"`
|
PreferIP TypePreferIP `json:"preferIp"`
|
||||||
DomainFrontingPort TypePort `json:"domainFrontingPort"`
|
DomainFrontingPort TypePort `json:"domainFrontingPort"`
|
||||||
TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"`
|
TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"`
|
||||||
@@ -64,6 +65,10 @@ type Config struct {
|
|||||||
MetricPrefix TypeMetricPrefix `json:"metricPrefix"`
|
MetricPrefix TypeMetricPrefix `json:"metricPrefix"`
|
||||||
} `json:"prometheus"`
|
} `json:"prometheus"`
|
||||||
} `json:"stats"`
|
} `json:"stats"`
|
||||||
|
DCOverrides []struct {
|
||||||
|
DC TypeDC `json:"dc"`
|
||||||
|
IPs []TypeHostPort `json:"ips"`
|
||||||
|
} `json:"dcOverrides"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pelletier/go-toml"
|
"github.com/pelletier/go-toml/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tomlConfig struct {
|
type tomlConfig struct {
|
||||||
@@ -13,6 +13,7 @@ type tomlConfig struct {
|
|||||||
AllowFallbackOnUnknownDC bool `toml:"allow-fallback-on-unknown-dc" json:"allowFallbackOnUnknownDc,omitempty"`
|
AllowFallbackOnUnknownDC bool `toml:"allow-fallback-on-unknown-dc" json:"allowFallbackOnUnknownDc,omitempty"`
|
||||||
Secret string `toml:"secret" json:"secret"`
|
Secret string `toml:"secret" json:"secret"`
|
||||||
BindTo string `toml:"bind-to" json:"bindTo"`
|
BindTo string `toml:"bind-to" json:"bindTo"`
|
||||||
|
ProxyProtocolListener bool `toml:"proxy-protocol-listener" json:"proxyProtocolListener"`
|
||||||
PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"`
|
PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"`
|
||||||
DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"`
|
DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"`
|
||||||
TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"`
|
TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"`
|
||||||
@@ -59,6 +60,10 @@ type tomlConfig struct {
|
|||||||
MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"`
|
MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"`
|
||||||
} `toml:"prometheus" json:"prometheus,omitempty"`
|
} `toml:"prometheus" json:"prometheus,omitempty"`
|
||||||
} `toml:"stats" json:"stats,omitempty"`
|
} `toml:"stats" json:"stats,omitempty"`
|
||||||
|
DCOverrides []struct {
|
||||||
|
DC uint `toml:"dc" json:"dc"`
|
||||||
|
IPs []string `toml:"ips" json:"ips"`
|
||||||
|
} `toml:"dc-overrides" json:"dcOverrides,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Parse(rawData []byte) (*Config, error) {
|
func Parse(rawData []byte) (*Config, error) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type TypeBoolTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *TypeBoolTestSuite) TestUnmarshalFail() {
|
func (suite *TypeBoolTestSuite) TestUnmarshalFail() {
|
||||||
testData := []interface{}{
|
testData := []any{
|
||||||
"",
|
"",
|
||||||
"np",
|
"np",
|
||||||
"нет",
|
"нет",
|
||||||
@@ -29,7 +29,7 @@ func (suite *TypeBoolTestSuite) TestUnmarshalFail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range testData {
|
for _, v := range testData {
|
||||||
data, err := json.Marshal(map[string]interface{}{
|
data, err := json.Marshal(map[string]any{
|
||||||
"value": v,
|
"value": v,
|
||||||
})
|
})
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TypeDC struct {
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypeDC) Set(value string) error {
|
||||||
|
parsed, err := strconv.ParseInt(value, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot parse dc: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed < 0 {
|
||||||
|
parsed = -parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Value = int(parsed)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypeDC) UnmarshalJSON(data []byte) error {
|
||||||
|
return t.Set(string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TypeDC) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(t.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TypeDC) String() string {
|
||||||
|
return strconv.Itoa(t.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TypeDC) Get() int {
|
||||||
|
return t.Value
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package config_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/v2/internal/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type typeDCTestStruct struct {
|
||||||
|
Value config.TypeDC `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeDCTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TypeDCTestSuite) TestUnmarshalFail() {
|
||||||
|
testData := []string{
|
||||||
|
"-1s",
|
||||||
|
"1202002020202",
|
||||||
|
"xxx",
|
||||||
|
"-11111111111111",
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range testData {
|
||||||
|
data, err := json.Marshal(map[string]string{
|
||||||
|
"value": v,
|
||||||
|
})
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
suite.T().Run(v, func(t *testing.T) {
|
||||||
|
assert.Error(t, json.Unmarshal(data, &typeDCTestStruct{}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TypeDCTestSuite) TestUnmarshalOk() {
|
||||||
|
testData := map[int]int{
|
||||||
|
1: 1,
|
||||||
|
-1: 1,
|
||||||
|
203: 203,
|
||||||
|
}
|
||||||
|
|
||||||
|
for value, expected := range testData {
|
||||||
|
data, err := json.Marshal(map[string]int{
|
||||||
|
"value": value,
|
||||||
|
})
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
suite.T().Run(strconv.Itoa(value), func(t *testing.T) {
|
||||||
|
testStruct := &typeDCTestStruct{}
|
||||||
|
|
||||||
|
assert.NoError(t, json.Unmarshal(data, testStruct))
|
||||||
|
assert.Equal(t, expected, testStruct.Value.Value)
|
||||||
|
assert.Equal(t, expected, testStruct.Value.Get())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TypeDCTestSuite) TestMarshalOk() {
|
||||||
|
testData := map[string]int{
|
||||||
|
"1": 1,
|
||||||
|
"203": 203,
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range testData {
|
||||||
|
value := k
|
||||||
|
expected := v
|
||||||
|
|
||||||
|
suite.T().Run(value, func(t *testing.T) {
|
||||||
|
testStruct := &typeDCTestStruct{}
|
||||||
|
|
||||||
|
assert.NoError(t, testStruct.Value.Set(value))
|
||||||
|
|
||||||
|
data, err := json.Marshal(testStruct)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
expectedJSON, err := json.Marshal(map[string]int{
|
||||||
|
"value": expected,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.JSONEq(t, string(expectedJSON), string(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypeDC(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
suite.Run(t, &TypeDCTestSuite{})
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package proxyprotocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/pires/go-proxyproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListenerAdapter struct {
|
||||||
|
proxyproto.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListenerAdapter) Accept() (net.Conn, error) {
|
||||||
|
conn, err := l.Listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return connWrapper{conn.(*proxyproto.Conn)}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package proxyprotocol
|
||||||
|
|
||||||
|
import "github.com/pires/go-proxyproto"
|
||||||
|
|
||||||
|
type connWrapper struct {
|
||||||
|
*proxyproto.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c connWrapper) CloseRead() error {
|
||||||
|
tcpConn, ok := c.TCPConn()
|
||||||
|
if !ok {
|
||||||
|
panic("we support only tcp connections")
|
||||||
|
}
|
||||||
|
|
||||||
|
return tcpConn.CloseRead()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c connWrapper) CloseWrite() error {
|
||||||
|
tcpConn, ok := c.TCPConn()
|
||||||
|
if !ok {
|
||||||
|
panic("we support only tcp connections")
|
||||||
|
}
|
||||||
|
|
||||||
|
return tcpConn.CloseWrite()
|
||||||
|
}
|
||||||
@@ -112,18 +112,15 @@ func (f *Firehol) update() {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(f.blocklists))
|
|
||||||
|
|
||||||
mutex := &sync.Mutex{}
|
mutex := &sync.Mutex{}
|
||||||
ranger := cidranger.NewPCTrieRanger()
|
ranger := cidranger.NewPCTrieRanger()
|
||||||
|
|
||||||
for _, v := range f.blocklists {
|
for _, v := range f.blocklists {
|
||||||
go func(file files.File) {
|
wg.Go(func() {
|
||||||
defer wg.Done()
|
logger := f.logger.BindStr("filename", v.String())
|
||||||
|
|
||||||
logger := f.logger.BindStr("filename", file.String())
|
fileContent, err := v.Open(ctx)
|
||||||
|
|
||||||
fileContent, err := file.Open(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.WarningError("update has failed", err)
|
logger.WarningError("update has failed", err)
|
||||||
|
|
||||||
@@ -135,7 +132,7 @@ func (f *Firehol) update() {
|
|||||||
if err := f.updateFromFile(mutex, ranger, 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)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
+1
-1
@@ -8,5 +8,5 @@ package logger
|
|||||||
// commonly used by many 3pp tools. While mtglib itself does not need it, it is
|
// commonly used by many 3pp tools. While mtglib itself does not need it, it is
|
||||||
// always a good idea to support it and have a transient end to end logging.
|
// always a good idea to support it and have a transient end to end logging.
|
||||||
type StdLikeLogger interface {
|
type StdLikeLogger interface {
|
||||||
Printf(format string, args ...interface{})
|
Printf(format string, args ...any)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ func (n noopLogger) Named(_ string) mtglib.Logger { return n }
|
|||||||
func (n noopLogger) BindInt(_ string, _ int) mtglib.Logger { return n }
|
func (n noopLogger) BindInt(_ string, _ int) mtglib.Logger { return n }
|
||||||
func (n noopLogger) BindStr(_, _ string) mtglib.Logger { return n }
|
func (n noopLogger) BindStr(_, _ string) mtglib.Logger { return n }
|
||||||
func (n noopLogger) BindJSON(_, _ string) mtglib.Logger { return n }
|
func (n noopLogger) BindJSON(_, _ string) mtglib.Logger { return n }
|
||||||
func (n noopLogger) Printf(_ string, _ ...interface{}) {}
|
func (n noopLogger) Printf(_ string, _ ...any) {}
|
||||||
func (n noopLogger) Info(_ string) {}
|
func (n noopLogger) Info(_ string) {}
|
||||||
func (n noopLogger) Warning(_ string) {}
|
func (n noopLogger) Warning(_ string) {}
|
||||||
func (n noopLogger) Debug(_ string) {}
|
func (n noopLogger) Debug(_ string) {}
|
||||||
|
|||||||
+1
-1
@@ -78,7 +78,7 @@ func (z *zeroLogContext) BindJSON(name, value string) mtglib.Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *zeroLogContext) Printf(format string, args ...interface{}) {
|
func (z *zeroLogContext) Printf(format string, args ...any) {
|
||||||
z.Debug(fmt.Sprintf(format, args...))
|
z.Debug(fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
[[tools.go]]
|
||||||
|
version = "1.26.0"
|
||||||
|
backend = "core:go"
|
||||||
|
"platforms.linux-arm64" = { checksum = "sha256:bd03b743eb6eb4193ea3c3fd3956546bf0e3ca5b7076c8226334afe6b75704cd", url = "https://dl.google.com/go/go1.26.0.linux-arm64.tar.gz"}
|
||||||
|
"platforms.linux-x64" = { checksum = "sha256:aac1b08a0fb0c4e0a7c1555beb7b59180b05dfc5a3d62e40e9de90cd42f88235", url = "https://dl.google.com/go/go1.26.0.linux-amd64.tar.gz"}
|
||||||
|
"platforms.macos-arm64" = { checksum = "sha256:b1640525dfe68f066d56f200bef7bf4dce955a1a893bd061de6754c211431023", url = "https://dl.google.com/go/go1.26.0.darwin-arm64.tar.gz"}
|
||||||
|
"platforms.macos-x64" = { checksum = "sha256:1ca28b7703cbea05a65b2a1d92d6b308610ef92f8824578a0874f2e60c9d5a22", url = "https://dl.google.com/go/go1.26.0.darwin-amd64.tar.gz"}
|
||||||
|
"platforms.windows-x64" = { checksum = "sha256:9bbe0fc64236b2b51f6255c05c4232532b8ecc0e6d2e00950bd3021d8a4d07d4", url = "https://dl.google.com/go/go1.26.0.windows-amd64.zip"}
|
||||||
|
|
||||||
|
[[tools."go:golang.org/x/pkgsite/cmd/pkgsite"]]
|
||||||
|
version = "latest"
|
||||||
|
backend = "go:golang.org/x/pkgsite/cmd/pkgsite"
|
||||||
|
|
||||||
|
[[tools."go:golang.org/x/tools/gopls"]]
|
||||||
|
version = "0.21.1"
|
||||||
|
backend = "go:golang.org/x/tools/gopls"
|
||||||
|
|
||||||
|
[[tools."go:mvdan.cc/gofumpt"]]
|
||||||
|
version = "0.9.2"
|
||||||
|
backend = "go:mvdan.cc/gofumpt"
|
||||||
|
|
||||||
|
[[tools.golangci-lint]]
|
||||||
|
version = "2.10.1"
|
||||||
|
backend = "aqua:golangci/golangci-lint"
|
||||||
|
"platforms.linux-arm64" = { checksum = "sha256:6652b42ae02915eb2f9cb2a2e0cac99514c8eded8388d88ae3e06e1a52c00de8", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-linux-arm64.tar.gz"}
|
||||||
|
"platforms.linux-x64" = { checksum = "sha256:dfa775874cf0561b404a02a8f4481fc69b28091da95aa697259820d429b09c99", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-linux-amd64.tar.gz"}
|
||||||
|
"platforms.macos-arm64" = { checksum = "sha256:03bfadf67e52b441b7ec21305e501c717df93c959836d66c7f97312654acb297", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-darwin-arm64.tar.gz"}
|
||||||
|
"platforms.macos-x64" = { checksum = "sha256:66fb0da81b8033b477f97eea420d4b46b230ca172b8bb87c6610109f3772b6b6", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-darwin-amd64.tar.gz"}
|
||||||
|
"platforms.windows-x64" = { checksum = "sha256:c60c87695e79db8e320f0e5be885059859de52bb5ee5f11be5577828570bc2a3", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-windows-amd64.zip"}
|
||||||
|
|
||||||
|
[[tools.goreleaser]]
|
||||||
|
version = "2.13.3"
|
||||||
|
backend = "aqua:goreleaser/goreleaser"
|
||||||
|
"platforms.linux-arm64" = { checksum = "sha256:156656d0f874542d618568bd50afd3d33ced2e8aab2c60cc7c21e1b9fa52031e", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Linux_arm64.tar.gz"}
|
||||||
|
"platforms.linux-x64" = { checksum = "sha256:4b66f2f78f78561330350651ade557b70328664718490f37834749073af21d20", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Linux_x86_64.tar.gz"}
|
||||||
|
"platforms.macos-arm64" = { checksum = "sha256:5516c37779efb3935d5b213cda3b0b9025ae94ddbcb51df6919acbcdef4194b0", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Darwin_all.tar.gz"}
|
||||||
|
"platforms.macos-x64" = { checksum = "sha256:5516c37779efb3935d5b213cda3b0b9025ae94ddbcb51df6919acbcdef4194b0", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Darwin_all.tar.gz"}
|
||||||
|
"platforms.windows-x64" = { checksum = "sha256:c5586c4ed749ca358ad61ed73ee4b8039cfa68daae8c23e69fb086d549dfb31d", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Windows_x86_64.zip"}
|
||||||
+1
-1
@@ -249,7 +249,7 @@ type Logger interface {
|
|||||||
BindJSON(name, value string) Logger
|
BindJSON(name, value string) Logger
|
||||||
|
|
||||||
// Printf is to support log.Logger behavior.
|
// Printf is to support log.Logger behavior.
|
||||||
Printf(format string, args ...interface{})
|
Printf(format string, args ...any)
|
||||||
|
|
||||||
// Info puts a message about some normal situation.
|
// Info puts a message about some normal situation.
|
||||||
Info(msg string)
|
Info(msg string)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func (n NoopLogger) Named(_ string) Logger { return n }
|
|||||||
func (n NoopLogger) BindInt(_ string, _ int) Logger { return n }
|
func (n NoopLogger) BindInt(_ string, _ int) Logger { return n }
|
||||||
func (n NoopLogger) BindStr(_, _ string) Logger { return n }
|
func (n NoopLogger) BindStr(_, _ string) Logger { return n }
|
||||||
func (n NoopLogger) BindJSON(_, _ string) Logger { return n }
|
func (n NoopLogger) BindJSON(_, _ string) Logger { return n }
|
||||||
func (n NoopLogger) Printf(_ string, _ ...interface{}) {}
|
func (n NoopLogger) Printf(_ string, _ ...any) {}
|
||||||
func (n NoopLogger) Info(_ string) {}
|
func (n NoopLogger) Info(_ string) {}
|
||||||
func (n NoopLogger) Warning(_ string) {}
|
func (n NoopLogger) Warning(_ string) {}
|
||||||
func (n NoopLogger) Debug(_ string) {}
|
func (n NoopLogger) Debug(_ string) {}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package dc
|
||||||
|
|
||||||
|
type Addr struct {
|
||||||
|
Network string
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Addr) String() string {
|
||||||
|
return d.Address
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package dc
|
||||||
|
|
||||||
|
import "math/rand/v2"
|
||||||
|
|
||||||
|
type dcAddrSet struct {
|
||||||
|
v4 map[int][]Addr
|
||||||
|
v6 map[int][]Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dcAddrSet) getV4(dc int) []Addr {
|
||||||
|
if d.v4 == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return d.get(d.v4[dc])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dcAddrSet) getV6(dc int) []Addr {
|
||||||
|
if d.v6 == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return d.get(d.v6[dc])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dcAddrSet) get(addrs []Addr) []Addr {
|
||||||
|
otherSet := make([]Addr, 0, len(addrs))
|
||||||
|
otherSet = append(otherSet, addrs...)
|
||||||
|
|
||||||
|
rand.Shuffle(len(otherSet), func(i, j int) {
|
||||||
|
otherSet[i], otherSet[j] = otherSet[j], otherSet[i]
|
||||||
|
})
|
||||||
|
|
||||||
|
return otherSet
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/v2/mtglib/internal/dc"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddr(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
addr := dc.Addr{Network: "tcp4", Address: "127.0.0.1:443"}
|
||||||
|
|
||||||
|
assert.Equal(t, "127.0.0.1:443", addr.String())
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package dc
|
||||||
|
|
||||||
|
type preferIP uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
preferIPOnlyIPv4 preferIP = iota
|
||||||
|
preferIPOnlyIPv6
|
||||||
|
preferIPPreferIPv4
|
||||||
|
preferIPPreferIPv6
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultDC = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
Info(msg string)
|
||||||
|
WarningError(msg string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/mtproto/mtproto_dc_options.cpp#L30
|
||||||
|
defaultDCAddrSet = dcAddrSet{
|
||||||
|
v4: map[int][]Addr{
|
||||||
|
1: {
|
||||||
|
{Network: "tcp4", Address: "149.154.175.50:443"},
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
{Network: "tcp4", Address: "149.154.167.51:443"},
|
||||||
|
{Network: "tcp4", Address: "95.161.76.100:443"},
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
{Network: "tcp4", Address: "149.154.175.100:443"},
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
{Network: "tcp4", Address: "149.154.167.91:443"},
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
{Network: "tcp4", Address: "149.154.171.5:443"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
v6: map[int][]Addr{
|
||||||
|
1: {
|
||||||
|
{Network: "tcp6", Address: "[2001:b28:f23d:f001::a]:443"},
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
{Network: "tcp6", Address: "[2001:67c:04e8:f002::a]:443"},
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
{Network: "tcp6", Address: "[2001:b28:f23d:f003::a]:443"},
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
{Network: "tcp6", Address: "[2001:67c:04e8:f004::a]:443"},
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
{Network: "tcp6", Address: "[2001:b28:f23f:f005::a]:443"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultDCOverridesAddrSet = dcAddrSet{
|
||||||
|
v4: map[int][]Addr{
|
||||||
|
203: {
|
||||||
|
{Network: "tcp4", Address: "91.105.192.100:443"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
v6: map[int][]Addr{
|
||||||
|
203: {
|
||||||
|
{Network: "tcp6", Address: "[2a0a:f280:0203:000a:5000:0000:0000:0100]:443"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package dc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Telegram struct {
|
||||||
|
view dcView
|
||||||
|
preferIP preferIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Telegram) GetAddresses(dc int) []Addr {
|
||||||
|
switch t.preferIP {
|
||||||
|
case preferIPOnlyIPv4:
|
||||||
|
return t.view.getV4(dc)
|
||||||
|
case preferIPOnlyIPv6:
|
||||||
|
return t.view.getV4(dc)
|
||||||
|
case preferIPPreferIPv4:
|
||||||
|
return append(t.view.getV4(dc), t.view.getV6(dc)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(t.view.getV6(dc), t.view.getV4(dc)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ipPreference string, userOverrides map[int][]string) (*Telegram, error) {
|
||||||
|
var pref preferIP
|
||||||
|
|
||||||
|
switch strings.ToLower(ipPreference) {
|
||||||
|
case "prefer-ipv4":
|
||||||
|
pref = preferIPPreferIPv4
|
||||||
|
case "prefer-ipv6":
|
||||||
|
pref = preferIPPreferIPv6
|
||||||
|
case "only-ipv4":
|
||||||
|
pref = preferIPOnlyIPv4
|
||||||
|
case "only-ipv6":
|
||||||
|
pref = preferIPOnlyIPv6
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown ip preference %s", ipPreference)
|
||||||
|
}
|
||||||
|
|
||||||
|
overrides := dcAddrSet{
|
||||||
|
v4: map[int][]Addr{},
|
||||||
|
v6: map[int][]Addr{},
|
||||||
|
}
|
||||||
|
for dc, addrs := range userOverrides {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
host, _, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("incorrect host %s: %w", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed := net.ParseIP(host)
|
||||||
|
if parsed == nil {
|
||||||
|
return nil, fmt.Errorf("incorrect host %s", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed.To4() != nil {
|
||||||
|
overrides.v4[dc] = append(overrides.v4[dc], Addr{
|
||||||
|
Network: "tcp4",
|
||||||
|
Address: addr,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
overrides.v6[dc] = append(overrides.v6[dc], Addr{
|
||||||
|
Network: "tcp6",
|
||||||
|
Address: addr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Telegram{
|
||||||
|
view: dcView{
|
||||||
|
overrides: overrides,
|
||||||
|
},
|
||||||
|
preferIP: pref,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package dc
|
||||||
|
|
||||||
|
type dcView struct {
|
||||||
|
overrides dcAddrSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dcView) getV4(dc int) []Addr {
|
||||||
|
addrs := d.overrides.getV4(dc)
|
||||||
|
addrs = append(addrs, defaultDCOverridesAddrSet.getV4(dc)...)
|
||||||
|
addrs = append(addrs, defaultDCAddrSet.getV4(dc)...)
|
||||||
|
|
||||||
|
return addrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dcView) getV6(dc int) []Addr {
|
||||||
|
addrs := d.overrides.getV6(dc)
|
||||||
|
addrs = append(addrs, defaultDCOverridesAddrSet.getV6(dc)...)
|
||||||
|
addrs = append(addrs, defaultDCAddrSet.getV6(dc)...)
|
||||||
|
|
||||||
|
return addrs
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package dc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ViewTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
|
view dcView
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ViewTestSuite) SetupSuite() {
|
||||||
|
suite.view = dcView{
|
||||||
|
overrides: dcAddrSet{
|
||||||
|
v4: map[int][]Addr{
|
||||||
|
111: {
|
||||||
|
{Network: "tcp4", Address: "127.0.0.1:443"},
|
||||||
|
},
|
||||||
|
203: {
|
||||||
|
{Network: "tcp4", Address: "127.0.0.2:443"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
v6: map[int][]Addr{
|
||||||
|
203: {
|
||||||
|
{Network: "tcp6", Address: "xxx"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ViewTestSuite) TestGetV4() {
|
||||||
|
testData := map[int][]Addr{
|
||||||
|
111: {
|
||||||
|
{"tcp4", "127.0.0.1:443"},
|
||||||
|
},
|
||||||
|
203: {
|
||||||
|
{"tcp4", "127.0.0.2:443"},
|
||||||
|
{"tcp4", "91.105.192.100:443"},
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
{"tcp4", "149.154.167.51:443"},
|
||||||
|
{"tcp4", "95.161.76.100:443"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for dc, addresses := range testData {
|
||||||
|
suite.T().Run(fmt.Sprintf("dc%d", dc), func(t *testing.T) {
|
||||||
|
assert.ElementsMatch(t, addresses, suite.view.getV4(dc))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ViewTestSuite) TestGetV6() {
|
||||||
|
testData := map[int][]Addr{
|
||||||
|
111: {},
|
||||||
|
203: {
|
||||||
|
{"tcp6", "xxx"},
|
||||||
|
{"tcp6", "[2a0a:f280:0203:000a:5000:0000:0000:0100]:443"},
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
{"tcp6", "[2001:b28:f23d:f001::a]:443"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for dc, addresses := range testData {
|
||||||
|
suite.T().Run(fmt.Sprintf("dc%d", dc), func(t *testing.T) {
|
||||||
|
assert.ElementsMatch(t, addresses, suite.view.getV6(dc))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestView(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
suite.Run(t, &ViewTestSuite{})
|
||||||
|
}
|
||||||
@@ -76,7 +76,7 @@ func ParseClientHello(secret, handshake []byte) (ClientHello, error) {
|
|||||||
|
|
||||||
computedRandom := mac.Sum(nil)
|
computedRandom := mac.Sum(nil)
|
||||||
|
|
||||||
for i := 0; i < RandomLen; i++ {
|
for i := range RandomLen {
|
||||||
computedRandom[i] ^= hello.Random[i]
|
computedRandom[i] ^= hello.Random[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package faketls
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/essentials"
|
"github.com/9seconds/mtg/v2/essentials"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
|
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
|
||||||
@@ -53,7 +53,7 @@ func (c *Conn) Write(p []byte) (int, error) {
|
|||||||
lenP := len(p)
|
lenP := len(p)
|
||||||
|
|
||||||
for len(p) > 0 {
|
for len(p) > 0 {
|
||||||
chunkSize := rand.Intn(record.TLSMaxRecordSize)
|
chunkSize := rand.IntN(record.TLSMaxRecordSize)
|
||||||
if chunkSize > len(p) || chunkSize == 0 {
|
if chunkSize > len(p) || chunkSize == 0 {
|
||||||
chunkSize = len(p)
|
chunkSize = len(p)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package faketls_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/internal/testlib"
|
"github.com/9seconds/mtg/v2/internal/testlib"
|
||||||
@@ -123,7 +123,7 @@ func (suite *ConnTestSuite) TestWrite() {
|
|||||||
suite.connMock.On("Write", mock.Anything).Return(0, nil)
|
suite.connMock.On("Write", mock.Anything).Return(0, nil)
|
||||||
|
|
||||||
dataToRec := make([]byte, record.TLSMaxRecordSize*2)
|
dataToRec := make([]byte, record.TLSMaxRecordSize*2)
|
||||||
rand.Read(dataToRec) //nolint: staticcheck
|
rand.Read(dataToRec) //nolint: staticcheck, errcheck
|
||||||
|
|
||||||
n, err := suite.c.Write(dataToRec)
|
n, err := suite.c.Write(dataToRec)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var bytesBufferPool = sync.Pool{
|
var bytesBufferPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() any {
|
||||||
return &bytes.Buffer{}
|
return &bytes.Buffer{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var recordPool = sync.Pool{
|
var recordPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() any {
|
||||||
return &Record{}
|
return &Record{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
mrand "math/rand"
|
mrand "math/rand/v2"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
|
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
@@ -36,7 +36,7 @@ func SendWelcomePacket(writer io.Writer, secret []byte, clientHello ClientHello)
|
|||||||
rec.Type = record.TypeApplicationData
|
rec.Type = record.TypeApplicationData
|
||||||
rec.Version = record.Version12
|
rec.Version = record.Version12
|
||||||
|
|
||||||
if _, err := io.CopyN(&rec.Payload, rand.Reader, int64(1024+mrand.Intn(3092))); err != nil {
|
if _, err := io.CopyN(&rec.Payload, rand.Reader, int64(1024+mrand.IntN(3092))); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package faketls_test
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"math/rand"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (h *handshakeFrame) connectionType() []byte {
|
|||||||
func (h *handshakeFrame) invert() handshakeFrame {
|
func (h *handshakeFrame) invert() handshakeFrame {
|
||||||
copyFrame := *h
|
copyFrame := *h
|
||||||
|
|
||||||
for i := 0; i < handshakeFrameLenKey+handshakeFrameLenIV; i++ {
|
for i := range handshakeFrameLenKey + handshakeFrameLenIV {
|
||||||
copyFrame.data[handshakeFrameOffsetKey+i] = h.data[handshakeFrameOffsetConnectionType-1-i]
|
copyFrame.data[handshakeFrameOffsetKey+i] = h.data[handshakeFrameOffsetConnectionType-1-i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func NewServerHandshakeTestData(t *testing.T) ServerHandshakeTestData {
|
|||||||
|
|
||||||
serverEncryptedReverted := make([]byte, len(serverEncrypted))
|
serverEncryptedReverted := make([]byte, len(serverEncrypted))
|
||||||
|
|
||||||
for i := 0; i < 32+16; i++ {
|
for i := range 32 + 16 {
|
||||||
serverEncryptedReverted[8+i] = serverEncrypted[8+32+16-1-i]
|
serverEncryptedReverted[8+i] = serverEncrypted[8+32+16-1-i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
sha256HasherPool = sync.Pool{
|
sha256HasherPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() any {
|
||||||
return sha256.New()
|
return sha256.New()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
bytesBufferPool = sync.Pool{
|
bytesBufferPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() any {
|
||||||
return &bytes.Buffer{}
|
return &bytes.Buffer{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
Printf(msg string, args ...interface{})
|
Printf(msg string, args ...any)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ package relay_test
|
|||||||
|
|
||||||
type loggerMock struct{}
|
type loggerMock struct{}
|
||||||
|
|
||||||
func (l loggerMock) Printf(format string, args ...interface{}) {}
|
func (l loggerMock) Printf(format string, args ...any) {}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package relay
|
|||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
var copyBufferPool = sync.Pool{
|
var copyBufferPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() any {
|
||||||
rv := make([]byte, copyBufferSize)
|
rv := make([]byte, copyBufferSize)
|
||||||
|
|
||||||
return &rv
|
return &rv
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
package telegram
|
|
||||||
|
|
||||||
import "math/rand"
|
|
||||||
|
|
||||||
type addressPool struct {
|
|
||||||
v4 [][]tgAddr
|
|
||||||
v6 [][]tgAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a addressPool) isValidDC(dc int) bool {
|
|
||||||
return dc > 0 && dc <= len(a.v4) && dc <= len(a.v6)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a addressPool) getRandomDC() int {
|
|
||||||
return 1 + rand.Intn(len(a.v4))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a addressPool) getV4(dc int) []tgAddr {
|
|
||||||
return a.get(a.v4, dc-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a addressPool) getV6(dc int) []tgAddr {
|
|
||||||
return a.get(a.v6, dc-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a addressPool) get(addresses [][]tgAddr, dc int) []tgAddr {
|
|
||||||
if dc < 0 || dc >= len(addresses) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rv := make([]tgAddr, len(addresses[dc]))
|
|
||||||
copy(rv, addresses[dc])
|
|
||||||
|
|
||||||
if len(rv) > 1 {
|
|
||||||
rand.Shuffle(len(rv), func(i, j int) {
|
|
||||||
rv[i], rv[j] = rv[j], rv[i]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package telegram
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/essentials"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errNoAddresses = errors.New("no addresses")
|
|
||||||
|
|
||||||
type preferIP uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
preferIPOnlyIPv4 preferIP = iota
|
|
||||||
preferIPOnlyIPv6
|
|
||||||
preferIPPreferIPv4
|
|
||||||
preferIPPreferIPv6
|
|
||||||
)
|
|
||||||
|
|
||||||
type tgAddr struct {
|
|
||||||
network string
|
|
||||||
address string
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/mtproto/mtproto_dc_options.cpp#L30
|
|
||||||
var (
|
|
||||||
productionV4Addresses = [][]tgAddr{
|
|
||||||
{ // dc1
|
|
||||||
{network: "tcp4", address: "149.154.175.50:443"},
|
|
||||||
},
|
|
||||||
{ // dc2
|
|
||||||
{network: "tcp4", address: "149.154.167.51:443"},
|
|
||||||
{network: "tcp4", address: "95.161.76.100:443"},
|
|
||||||
},
|
|
||||||
{ // dc3
|
|
||||||
{network: "tcp4", address: "149.154.175.100:443"},
|
|
||||||
},
|
|
||||||
{ // dc4
|
|
||||||
{network: "tcp4", address: "149.154.167.91:443"},
|
|
||||||
},
|
|
||||||
{ // dc5
|
|
||||||
{network: "tcp4", address: "149.154.171.5:443"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
productionV6Addresses = [][]tgAddr{
|
|
||||||
{ // dc1
|
|
||||||
{network: "tcp6", address: "[2001:b28:f23d:f001::a]:443"},
|
|
||||||
},
|
|
||||||
{ // dc2
|
|
||||||
{network: "tcp6", address: "[2001:67c:04e8:f002::a]:443"},
|
|
||||||
},
|
|
||||||
{ // dc3
|
|
||||||
{network: "tcp6", address: "[2001:b28:f23d:f003::a]:443"},
|
|
||||||
},
|
|
||||||
{ // dc4
|
|
||||||
{network: "tcp6", address: "[2001:67c:04e8:f004::a]:443"},
|
|
||||||
},
|
|
||||||
{ // dc5
|
|
||||||
{network: "tcp6", address: "[2001:b28:f23f:f005::a]:443"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
testV4Addresses = [][]tgAddr{
|
|
||||||
{ // dc1
|
|
||||||
{network: "tcp4", address: "149.154.175.10:443"},
|
|
||||||
},
|
|
||||||
{ // dc2
|
|
||||||
{network: "tcp4", address: "149.154.167.40:443"},
|
|
||||||
},
|
|
||||||
{ // dc3
|
|
||||||
{network: "tcp4", address: "149.154.175.117:443"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
testV6Addresses = [][]tgAddr{
|
|
||||||
{ // dc1
|
|
||||||
{network: "tcp6", address: "[2001:b28:f23d:f001::e]:443"},
|
|
||||||
},
|
|
||||||
{ // dc2
|
|
||||||
{network: "tcp6", address: "[2001:67c:04e8:f002::e]:443"},
|
|
||||||
},
|
|
||||||
{ // dc3
|
|
||||||
{network: "tcp6", address: "[2001:b28:f23d:f003::e]:443"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type Dialer interface {
|
|
||||||
DialContext(ctx context.Context, network, address string) (essentials.Conn, error)
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package telegram
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/essentials"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Telegram struct {
|
|
||||||
dialer Dialer
|
|
||||||
preferIP preferIP
|
|
||||||
pool addressPool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Telegram) Dial(ctx context.Context, dc int) (essentials.Conn, error) {
|
|
||||||
var addresses []tgAddr
|
|
||||||
|
|
||||||
switch t.preferIP {
|
|
||||||
case preferIPOnlyIPv4:
|
|
||||||
addresses = t.pool.getV4(dc)
|
|
||||||
case preferIPOnlyIPv6:
|
|
||||||
addresses = t.pool.getV6(dc)
|
|
||||||
case preferIPPreferIPv4:
|
|
||||||
addresses = append(t.pool.getV4(dc), t.pool.getV6(dc)...)
|
|
||||||
case preferIPPreferIPv6:
|
|
||||||
addresses = append(t.pool.getV6(dc), t.pool.getV4(dc)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var conn essentials.Conn
|
|
||||||
|
|
||||||
err := errNoAddresses
|
|
||||||
|
|
||||||
for _, v := range addresses {
|
|
||||||
conn, err = t.dialer.DialContext(ctx, v.network, v.address)
|
|
||||||
if err == nil {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("cannot dial to %d dc: %w", dc, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Telegram) IsKnownDC(dc int) bool {
|
|
||||||
return t.pool.isValidDC(dc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Telegram) GetFallbackDC() int {
|
|
||||||
return t.pool.getRandomDC()
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(dialer Dialer, ipPreference string, useTestDCs bool) (*Telegram, error) {
|
|
||||||
var pref preferIP
|
|
||||||
|
|
||||||
switch strings.ToLower(ipPreference) {
|
|
||||||
case "prefer-ipv4":
|
|
||||||
pref = preferIPPreferIPv4
|
|
||||||
case "prefer-ipv6":
|
|
||||||
pref = preferIPPreferIPv6
|
|
||||||
case "only-ipv4":
|
|
||||||
pref = preferIPOnlyIPv4
|
|
||||||
case "only-ipv6":
|
|
||||||
pref = preferIPOnlyIPv6
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown ip preference %s", ipPreference)
|
|
||||||
}
|
|
||||||
|
|
||||||
pool := addressPool{
|
|
||||||
v4: productionV4Addresses,
|
|
||||||
v6: productionV6Addresses,
|
|
||||||
}
|
|
||||||
if useTestDCs {
|
|
||||||
pool.v4 = testV4Addresses
|
|
||||||
pool.v6 = testV6Addresses
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Telegram{
|
|
||||||
dialer: dialer,
|
|
||||||
preferIP: pref,
|
|
||||||
pool: pool,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
package telegram
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/internal/testlib"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TelegramTestSuite struct {
|
|
||||||
suite.Suite
|
|
||||||
|
|
||||||
dialerMock *testlib.MtglibNetworkMock
|
|
||||||
t *Telegram
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *TelegramTestSuite) SetupTest() {
|
|
||||||
suite.dialerMock = &testlib.MtglibNetworkMock{}
|
|
||||||
suite.t, _ = New(suite.dialerMock, "prefer-ipv4", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *TelegramTestSuite) TearDownTest() {
|
|
||||||
suite.dialerMock.AssertExpectations(suite.T())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *TelegramTestSuite) TestUnknownDC() {
|
|
||||||
testData := []int{
|
|
||||||
-1,
|
|
||||||
0,
|
|
||||||
6,
|
|
||||||
100,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range testData {
|
|
||||||
value := v
|
|
||||||
|
|
||||||
suite.T().Run(strconv.Itoa(value), func(t *testing.T) {
|
|
||||||
_, err := suite.t.Dial(context.Background(), value)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.False(t, suite.t.IsKnownDC(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *TelegramTestSuite) TestDialToCorrectIPs() {
|
|
||||||
testData := map[int][]tgAddr{}
|
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
testData[i] = []tgAddr{}
|
|
||||||
testData[i] = append(testData[i], productionV4Addresses[i-1]...)
|
|
||||||
testData[i] = append(testData[i], productionV6Addresses[i-1]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, v := range testData {
|
|
||||||
idx := i
|
|
||||||
addresses := v
|
|
||||||
|
|
||||||
suite.T().Run(strconv.Itoa(idx), func(t *testing.T) {
|
|
||||||
for _, addr := range addresses {
|
|
||||||
suite.dialerMock.
|
|
||||||
On("DialContext", mock.Anything, addr.network, addr.address).
|
|
||||||
Once().
|
|
||||||
Return((*net.TCPConn)(nil), io.EOF)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := suite.t.Dial(context.Background(), idx)
|
|
||||||
assert.True(t, errors.Is(err, io.EOF))
|
|
||||||
assert.True(t, suite.t.IsKnownDC(idx))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *TelegramTestSuite) TestDialPreferIPRange() {
|
|
||||||
testData := map[string][]tgAddr{
|
|
||||||
"prefer-ipv4": {testV4Addresses[0][0], testV6Addresses[0][0]},
|
|
||||||
"prefer-ipv6": {testV6Addresses[0][0], testV4Addresses[0][0]},
|
|
||||||
"only-ipv4": {testV4Addresses[0][0]},
|
|
||||||
"only-ipv6": {testV6Addresses[0][0]},
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range testData {
|
|
||||||
name := k
|
|
||||||
addresses := v
|
|
||||||
|
|
||||||
suite.T().Run(name, func(t *testing.T) {
|
|
||||||
for _, addr := range addresses {
|
|
||||||
suite.dialerMock.
|
|
||||||
On("DialContext", mock.Anything, addr.network, addr.address).
|
|
||||||
Once().
|
|
||||||
Return((*net.TCPConn)(nil), io.EOF)
|
|
||||||
}
|
|
||||||
|
|
||||||
tg, _ := New(suite.dialerMock, name, true)
|
|
||||||
_, err := tg.Dial(context.Background(), 1)
|
|
||||||
|
|
||||||
assert.True(t, errors.Is(err, io.EOF))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *TelegramTestSuite) TestDialPreferIPPriority() {
|
|
||||||
testData := map[string]tgAddr{
|
|
||||||
"prefer-ipv4": productionV4Addresses[0][0],
|
|
||||||
"prefer-ipv6": productionV6Addresses[0][0],
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range testData {
|
|
||||||
name := k
|
|
||||||
addr := v
|
|
||||||
|
|
||||||
suite.T().Run(name, func(t *testing.T) {
|
|
||||||
conn := &net.TCPConn{}
|
|
||||||
|
|
||||||
suite.dialerMock.
|
|
||||||
On("DialContext", mock.Anything, addr.network, addr.address).
|
|
||||||
Once().
|
|
||||||
Return(conn, nil)
|
|
||||||
|
|
||||||
tg, _ := New(suite.dialerMock, name, false)
|
|
||||||
|
|
||||||
res, err := tg.Dial(context.Background(), 1)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, conn, res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *TelegramTestSuite) TestUnknownPreferIP() {
|
|
||||||
_, err := New(suite.dialerMock, "xxx", false)
|
|
||||||
suite.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *TelegramTestSuite) TestFallbackDC() {
|
|
||||||
dcs := make([]int, 10)
|
|
||||||
|
|
||||||
for i := 0; i < len(dcs); i++ {
|
|
||||||
dcs[i] = suite.t.GetFallbackDC()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range dcs {
|
|
||||||
value := v
|
|
||||||
|
|
||||||
suite.T().Run(strconv.Itoa(value), func(t *testing.T) {
|
|
||||||
assert.True(t, suite.t.IsKnownDC(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTelegram(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
suite.Run(t, &TelegramTestSuite{})
|
|
||||||
}
|
|
||||||
+20
-12
@@ -10,11 +10,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/essentials"
|
"github.com/9seconds/mtg/v2/essentials"
|
||||||
|
"github.com/9seconds/mtg/v2/mtglib/internal/dc"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/faketls"
|
"github.com/9seconds/mtg/v2/mtglib/internal/faketls"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
|
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2"
|
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/relay"
|
"github.com/9seconds/mtg/v2/mtglib/internal/relay"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/telegram"
|
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ type Proxy struct {
|
|||||||
tolerateTimeSkewness time.Duration
|
tolerateTimeSkewness time.Duration
|
||||||
domainFrontingPort int
|
domainFrontingPort int
|
||||||
workerPool *ants.PoolWithFunc
|
workerPool *ants.PoolWithFunc
|
||||||
telegram *telegram.Telegram
|
telegram *dc.Telegram
|
||||||
|
|
||||||
secret Secret
|
secret Secret
|
||||||
network Network
|
network Network
|
||||||
@@ -219,18 +219,26 @@ func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) doTelegramCall(ctx *streamContext) error {
|
func (p *Proxy) doTelegramCall(ctx *streamContext) error {
|
||||||
dc := ctx.dc
|
dcid := ctx.dc
|
||||||
|
|
||||||
if p.allowFallbackOnUnknownDC && !p.telegram.IsKnownDC(dc) {
|
|
||||||
dc = p.telegram.GetFallbackDC()
|
|
||||||
ctx.logger = ctx.logger.BindInt("fallback_dc", dc)
|
|
||||||
|
|
||||||
|
addresses := p.telegram.GetAddresses(dcid)
|
||||||
|
if len(addresses) == 0 && p.allowFallbackOnUnknownDC {
|
||||||
|
ctx.logger = ctx.logger.BindInt("fallback_dc", dc.DefaultDC)
|
||||||
ctx.logger.Warning("unknown DC, fallbacks")
|
ctx.logger.Warning("unknown DC, fallbacks")
|
||||||
|
addresses = p.telegram.GetAddresses(dc.DefaultDC)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := p.telegram.Dial(ctx, dc)
|
var conn essentials.Conn
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for _, addr := range addresses {
|
||||||
|
conn, err = p.network.Dial(addr.Network, addr.Address)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot dial to Telegram: %w", err)
|
return fmt.Errorf("no addresses to call: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptor, decryptor, err := obfuscated2.ServerHandshake(conn)
|
encryptor, decryptor, err := obfuscated2.ServerHandshake(conn)
|
||||||
@@ -292,9 +300,9 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
|
|||||||
return nil, fmt.Errorf("invalid settings: %w", err)
|
return nil, fmt.Errorf("invalid settings: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tg, err := telegram.New(opts.Network, opts.getPreferIP(), opts.UseTestDCs)
|
tg, err := dc.New(opts.getPreferIP(), opts.DCOverrides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot build telegram dialer: %w", err)
|
return nil, fmt.Errorf("cannot build telegram dc fetcher: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
@@ -315,7 +323,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pool, err := ants.NewPoolWithFunc(opts.getConcurrency(),
|
pool, err := ants.NewPoolWithFunc(opts.getConcurrency(),
|
||||||
func(arg interface{}) {
|
func(arg any) {
|
||||||
proxy.ServeConn(arg.(essentials.Conn)) //nolint: forcetypeassert
|
proxy.ServeConn(arg.(essentials.Conn)) //nolint: forcetypeassert
|
||||||
},
|
},
|
||||||
ants.WithLogger(opts.getLogger("ants")),
|
ants.WithLogger(opts.getLogger("ants")),
|
||||||
|
|||||||
@@ -110,7 +110,15 @@ type ProxyOpts struct {
|
|||||||
// Telegram-related projects.
|
// Telegram-related projects.
|
||||||
//
|
//
|
||||||
// This is an optional setting.
|
// This is an optional setting.
|
||||||
|
//
|
||||||
|
// OBSOLETE and DEPRECATED. Ignored.
|
||||||
UseTestDCs bool
|
UseTestDCs bool
|
||||||
|
|
||||||
|
// DCOverrides defines a set of IP addresses that should be used
|
||||||
|
// with a higher priority to those that are calculated somehow by mtg.
|
||||||
|
//
|
||||||
|
// This is an optional setting
|
||||||
|
DCOverrides map[int][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ProxyOpts) valid() error {
|
func (p ProxyOpts) valid() error {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (s *streamContext) Err() error {
|
|||||||
return s.ctx.Err() //nolint: wrapcheck
|
return s.ctx.Err() //nolint: wrapcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *streamContext) Value(key interface{}) interface{} {
|
func (s *streamContext) Value(key any) any {
|
||||||
return s.ctx.Value(key)
|
return s.ctx.Value(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,17 +52,9 @@ func (suite *CircuitBreakerTestSuite) TestMultipleRunsOk() {
|
|||||||
Return(suite.connMock, nil)
|
Return(suite.connMock, nil)
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(5)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
suite.ctxCancel()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
|
for range 5 {
|
||||||
|
wg.Go(func() {
|
||||||
conn, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
|
conn, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
|
||||||
|
|
||||||
suite.mutex.Lock()
|
suite.mutex.Lock()
|
||||||
@@ -70,9 +62,14 @@ func (suite *CircuitBreakerTestSuite) TestMultipleRunsOk() {
|
|||||||
|
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal("127.0.0.1:3128", conn.RemoteAddr().String())
|
suite.Equal("127.0.0.1:3128", conn.RemoteAddr().String())
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
suite.ctxCancel()
|
||||||
|
}()
|
||||||
|
|
||||||
suite.Eventually(func() bool {
|
suite.Eventually(func() bool {
|
||||||
_, ok := <-suite.ctx.Done()
|
_, ok := <-suite.ctx.Done()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ const (
|
|||||||
// DefaultDOHHostname defines a default IP address for DOH host. Since mtg is
|
// DefaultDOHHostname defines a default IP address for DOH host. Since mtg is
|
||||||
// simple, please pass IP address here. We do not have bootstrap servers here
|
// simple, please pass IP address here. We do not have bootstrap servers here
|
||||||
// embedded.
|
// embedded.
|
||||||
DefaultDOHHostname = "9.9.9.9"
|
DefaultDOHHostname = "1.1.1.1"
|
||||||
|
|
||||||
// DNSTimeout defines a timeout for DNS queries.
|
// DNSTimeout defines a timeout for DNS queries.
|
||||||
DNSTimeout = 5 * time.Second
|
DNSTimeout = 5 * time.Second
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package network
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/essentials"
|
"github.com/9seconds/mtg/v2/essentials"
|
||||||
@@ -19,7 +19,7 @@ func (l loadBalancedSocks5Dialer) Dial(network, address string) (essentials.Conn
|
|||||||
|
|
||||||
func (l loadBalancedSocks5Dialer) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
|
func (l loadBalancedSocks5Dialer) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) {
|
||||||
length := len(l.dialers)
|
length := len(l.dialers)
|
||||||
start := rand.Intn(length)
|
start := rand.IntN(length)
|
||||||
moved := false
|
moved := false
|
||||||
|
|
||||||
for i := start; i != start || !moved; i = (i + 1) % length {
|
for i := start; i != start || !moved; i = (i + 1) % length {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func (suite *LoadBalancedSocks5TestSuite) TestCannotDial() {
|
|||||||
})
|
})
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
for i := 0; i < network.ProxyDialerOpenThreshold*2; i++ {
|
for range network.ProxyDialerOpenThreshold * 2 {
|
||||||
_, err = lbDialer.Dial("tcp", "127.1.1.1:80")
|
_, err = lbDialer.Dial("tcp", "127.1.1.1:80")
|
||||||
suite.True(errors.Is(err, network.ErrCannotDialWithAllProxies))
|
suite.True(errors.Is(err, network.ErrCannotDialWithAllProxies))
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-13
@@ -3,7 +3,7 @@ package network
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -81,32 +81,24 @@ func (n *network) dnsResolve(protocol, address string) ([]string, error) {
|
|||||||
|
|
||||||
switch protocol {
|
switch protocol {
|
||||||
case "tcp", "tcp4":
|
case "tcp", "tcp4":
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
resolved := n.dns.LookupA(address)
|
resolved := n.dns.LookupA(address)
|
||||||
|
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
ips = append(ips, resolved...)
|
ips = append(ips, resolved...)
|
||||||
mutex.Unlock()
|
mutex.Unlock()
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
switch protocol {
|
switch protocol {
|
||||||
case "tcp", "tcp6":
|
case "tcp", "tcp6":
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
resolved := n.dns.LookupAAAA(address)
|
resolved := n.dns.LookupAAAA(address)
|
||||||
|
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
ips = append(ips, resolved...)
|
ips = append(ips, resolved...)
|
||||||
mutex.Unlock()
|
mutex.Unlock()
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ package stats
|
|||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
var streamInfoPool = sync.Pool{
|
var streamInfoPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() any {
|
||||||
return &streamInfo{
|
return &streamInfo{
|
||||||
tags: make(map[string]string),
|
tags: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user