REPOSITORY / ScuroNeko/mtg

Compare commits

DIFF REPOSITORY

Compare commits

...
19 Commits
Author SHA1 Message Date
9seconds b854b16e1a Merge branch 'stable' into v2 2026-02-19 14:37:52 +01:00
9seconds f4b296d1e0 Merge branch 'master' into stable 2026-02-19 14:37:36 +01:00
Sergei ArkhipovandGitHub 432e530f68 Adjust formatting in readme 2026-02-19 14:35:04 +01:00
Sergei ArkhipovandGitHub a2bf9a269a Merge pull request #333 from 9seconds/proxyproto
Add support of proxy protocol
2026-02-19 14:27:43 +01:00
9seconds cf3437bb63 Add support of proxy protocol 2026-02-19 14:22:00 +01:00
Sergei ArkhipovandGitHub d0e99dda2b Update README.md 2026-02-18 11:16:04 +01:00
Sergei ArkhipovandGitHub ac3bd16e83 Merge pull request #332 from 9seconds/telemt-recommend
Recommend to use telemt for adtag carvings
2026-02-18 11:14:22 +01:00
9seconds 222cef8c41 Recommend to use telemt for adtag carvings 2026-02-18 11:13:55 +01:00
Sergei ArkhipovandGitHub ed5da2864a Merge pull request #331 from 9seconds/go1.26
Chore updates for golang 1.26
2026-02-18 11:04:23 +01:00
9seconds 08393e426a Update mise dependencies 2026-02-18 10:37:41 +01:00
9seconds 0440ccf4ff Rewrite to WaitGroup.Go 2026-02-18 10:34:31 +01:00
9seconds a0aabf2391 Switch to rand/v2 2026-02-18 10:34:31 +01:00
9seconds 3b03c4a90a More idioms related to go 1.26 2026-02-17 23:38:12 +01:00
Sergei ArkhipovandGitHub 80b9159ce9 Merge pull request #330 from 9seconds/telemt
Mention telemt project in README
2026-02-17 22:19:50 +01:00
Sergei ArkhipovandGitHub bdabb0e59a Merge pull request #329 from 9seconds/tools
Tell about mise in readme
2026-02-17 22:19:32 +01:00
9seconds 543f5cde9c Mention telemt project in README 2026-02-17 21:46:15 +01:00
9seconds 1a247d18b1 Tell about mise in README 2026-02-17 21:43:16 +01:00
9seconds 5a63c7d5b4 Lock all mise dependencies 2026-02-17 21:39:12 +01:00
9seconds e7fdb02a29 Merge branch 'v2' into stable 2026-02-16 19:23:45 +01:00
41 changed files with 213 additions and 144 deletions
+4 -4
View File
@@ -1,7 +1,10 @@
[tools] [tools]
go = "latest" "go:golang.org/x/pkgsite/cmd/pkgsite" = "latest"
"go:golang.org/x/tools/gopls" = "latest" "go:golang.org/x/tools/gopls" = "latest"
"go:mvdan.cc/gofumpt" = "latest" "go:mvdan.cc/gofumpt" = "latest"
go = "latest"
golangci-lint = "latest"
goreleaser = "latest"
[vars] [vars]
fuzzflags = "-fuzztime=120s" fuzzflags = "-fuzztime=120s"
@@ -21,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]
@@ -77,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",
@@ -92,7 +93,6 @@ 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]
+27 -2
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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()
+9
View File
@@ -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
+1
View File
@@ -28,6 +28,7 @@ require (
require ( require (
github.com/pelletier/go-toml/v2 v2.2.4 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
) )
+2
View File
@@ -57,6 +57,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
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/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 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=
+4 -10
View File
@@ -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
View File
@@ -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"
) )
@@ -275,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
+1
View File
@@ -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"`
+1
View File
@@ -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"`
+2 -2
View File
@@ -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)
+20
View File
@@ -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
}
+25
View File
@@ -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()
}
+4 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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...))
} }
+23 -1
View File
@@ -7,10 +7,32 @@ backend = "core:go"
"platforms.macos-x64" = { checksum = "sha256:1ca28b7703cbea05a65b2a1d92d6b308610ef92f8824578a0874f2e60c9d5a22", url = "https://dl.google.com/go/go1.26.0.darwin-amd64.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"} "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"]] [[tools."go:golang.org/x/tools/gopls"]]
version = "0.21.0" version = "0.21.1"
backend = "go:golang.org/x/tools/gopls" backend = "go:golang.org/x/tools/gopls"
[[tools."go:mvdan.cc/gofumpt"]] [[tools."go:mvdan.cc/gofumpt"]]
version = "0.9.2" version = "0.9.2"
backend = "go:mvdan.cc/gofumpt" 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
View File
@@ -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)
+11 -11
View File
@@ -8,17 +8,17 @@ import (
type NoopLogger struct{} type NoopLogger struct{}
func (n NoopLogger) Named(_ string) Logger { return n } 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) {}
func (n NoopLogger) InfoError(_ string, _ error) {} func (n NoopLogger) InfoError(_ string, _ error) {}
func (n NoopLogger) WarningError(_ string, _ error) {} func (n NoopLogger) WarningError(_ string, _ error) {}
func (n NoopLogger) DebugError(_ string, _ error) {} func (n NoopLogger) DebugError(_ string, _ error) {}
type EventStreamMock struct { type EventStreamMock struct {
mock.Mock mock.Mock
+1 -1
View File
@@ -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]
} }
+2 -2
View File
@@ -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 -2
View File
@@ -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)
+1 -1
View File
@@ -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{}
}, },
} }
+1 -1
View File
@@ -5,7 +5,7 @@ import (
) )
var recordPool = sync.Pool{ var recordPool = sync.Pool{
New: func() interface{} { New: func() any {
return &Record{} return &Record{}
}, },
} }
+2 -2
View File
@@ -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)
} }
+1 -1
View File
@@ -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]
} }
+1 -1
View File
@@ -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]
} }
+2 -2
View File
@@ -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{}
}, },
} }
+1 -1
View File
@@ -5,5 +5,5 @@ const (
) )
type Logger interface { type Logger interface {
Printf(msg string, args ...interface{}) Printf(msg string, args ...any)
} }
+1 -1
View File
@@ -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) {}
+1 -1
View File
@@ -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 -1
View File
@@ -323,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")),
+1 -1
View File
@@ -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)
} }
+8 -11
View File
@@ -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()
+2 -2
View File
@@ -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 {
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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),
} }