Explorar el Código

Merge branch 'v2' into stable

tags/v2.1.10^2
9seconds hace 2 meses
padre
commit
e7fdb02a29

+ 19
- 1
.github/workflows/ci.yaml Ver fichero

@@ -93,6 +93,9 @@ jobs:
93 93
     name: Docker
94 94
     runs-on: ubuntu-latest
95 95
     timeout-minutes: 20
96
+    permissions:
97
+      contents: read
98
+      packages: write
96 99
     steps:
97 100
       - name: Checkout
98 101
         uses: actions/checkout@v6
@@ -103,9 +106,16 @@ jobs:
103 106
         id: meta
104 107
         uses: docker/metadata-action@v5
105 108
         with:
106
-          images: nineseconds/mtg
109
+          images: |
110
+            nineseconds/mtg
111
+            ghcr.io/${{ github.repository }}
107 112
           tags: |
108 113
             type=semver,pattern={{version}}
114
+            type=semver,pattern={{major}}.{{minor}}
115
+            type=semver,pattern={{major}}
116
+            type=raw,value=latest,enable={{is_default_branch}}
117
+            type=raw,value=master,enable=${{ github.ref == 'refs/heads/master' }}
118
+            type=raw,value=stable,enable=${{ github.ref == 'refs/heads/stable' }}
109 119
 
110 120
       - name: Setup QEMU
111 121
         uses: docker/setup-qemu-action@v3
@@ -128,6 +138,14 @@ jobs:
128 138
           username: ${{ secrets.DOCKERHUB_USERNAME }}
129 139
           password: ${{ secrets.DOCKERHUB_PASSWORD }}
130 140
 
141
+      - name: Login to GitHub Container Registry
142
+        if: github.event_name != 'pull_request'
143
+        uses: docker/login-action@v3
144
+        with:
145
+          registry: ghcr.io
146
+          username: ${{ github.actor }}
147
+          password: ${{ secrets.GITHUB_TOKEN }}
148
+
131 149
       - name: Build and push
132 150
         uses: docker/build-push-action@v2
133 151
         with:

+ 2
- 1
.mise.toml Ver fichero

@@ -1,5 +1,7 @@
1 1
 [tools]
2 2
 go = "latest"
3
+"go:golang.org/x/tools/gopls" = "latest"
4
+"go:mvdan.cc/gofumpt" = "latest"
3 5
 
4 6
 [vars]
5 7
 fuzzflags = "-fuzztime=120s"
@@ -95,6 +97,5 @@ run = "pkgsite -http 0.0.0.0:10000"
95 97
 
96 98
 [tasks.fmt]
97 99
 description = "Reformat source code"
98
-tools.gofumpt = "latest"
99 100
 sources = ["**/*.go"]
100 101
 run = "gofumpt -w --extra ."

+ 1
- 1
README.md Ver fichero

@@ -275,7 +275,7 @@ Flags:
275 275
   -b, --tcp-buffer="4KB"               Size of TCP buffer to use.
276 276
   -i, --prefer-ip="prefer-ipv6"        IP preference. By default we prefer IPv6 with fallback to IPv4.
277 277
   -p, --domain-fronting-port=443       A port to access for domain fronting.
278
-  -n, --doh-ip=9.9.9.9                 IP address of DNS-over-HTTP to use.
278
+  -n, --doh-ip=1.1.1.1                 IP address of DNS-over-HTTP to use.
279 279
   -t, --timeout=10s                    Network timeout to use
280 280
   -a, --antireplay-cache-size="1MB"    A size of anti-replay cache to use.
281 281
 ```

+ 11
- 2
example.config.toml Ver fichero

@@ -71,6 +71,15 @@ tolerate-time-skewness = "5s"
71 71
 # Otherwise, chose a new DC.
72 72
 allow-fallback-on-unknown-dc = false
73 73
 
74
+# Telegram uses different DCs for different purposes. Unfortunately, most of
75
+# DCs are not public, and dependent on a location of the current user, so
76
+# mtg cannot know upfront about all of them, and how to access them. It has
77
+# a default list of DCs, including some CDN IPs, but it is possible that some
78
+# of them are not working for you. In this case, you can override them here.
79
+[[dc-overrides]]
80
+dc = 101
81
+ips = ["127.0.0.1:443"]
82
+
74 83
 # network defines different network-related settings
75 84
 [network]
76 85
 # please be aware that mtg needs to do some external requests. For
@@ -84,8 +93,8 @@ allow-fallback-on-unknown-dc = false
84 93
 # resolver of the operating system and uses DOH instead. This is a host
85 94
 # it has to access.
86 95
 #
87
-# By default we use Quad9.
88
-doh-ip = "9.9.9.9"
96
+# By default we use Cloudflare.
97
+doh-ip = "1.1.1.1"
89 98
 
90 99
 # mtg can work via proxies (for now, we support only socks5). Proxy
91 100
 # configuration is done via list. So, you can specify many proxies

+ 4
- 1
go.mod Ver fichero

@@ -12,7 +12,6 @@ require (
12 12
 	github.com/jarcoal/httpmock v1.0.8
13 13
 	github.com/mccutchen/go-httpbin v1.1.1
14 14
 	github.com/panjf2000/ants/v2 v2.11.5
15
-	github.com/pelletier/go-toml v1.9.5
16 15
 	github.com/prometheus/client_golang v1.23.2
17 16
 	github.com/prometheus/common v0.67.5 // indirect
18 17
 	github.com/prometheus/procfs v0.19.2 // indirect
@@ -28,6 +27,7 @@ require (
28 27
 )
29 28
 
30 29
 require (
30
+	github.com/pelletier/go-toml/v2 v2.2.4
31 31
 	github.com/txthinking/socks5 v0.0.0-20251011041537-5c31f201a10e
32 32
 	github.com/yl2chen/cidranger v1.0.2
33 33
 )
@@ -36,6 +36,7 @@ require (
36 36
 	github.com/beorn7/perks v1.0.1 // indirect
37 37
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
38 38
 	github.com/davecgh/go-spew v1.1.1 // indirect
39
+	github.com/klauspost/compress v1.18.3 // indirect
39 40
 	github.com/kr/text v0.2.0 // indirect
40 41
 	github.com/mattn/go-colorable v0.1.14 // indirect
41 42
 	github.com/mattn/go-isatty v0.0.20 // indirect
@@ -43,8 +44,10 @@ require (
43 44
 	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
44 45
 	github.com/pmezard/go-difflib v1.0.0 // indirect
45 46
 	github.com/prometheus/client_model v0.6.2 // indirect
47
+	github.com/rogpeppe/go-internal v1.14.1 // indirect
46 48
 	github.com/txthinking/runnergroup v0.0.0-20250224021307-5864ffeb65ae // indirect
47 49
 	go.yaml.in/yaml/v2 v2.4.3 // indirect
48 50
 	golang.org/x/sync v0.19.0 // indirect
51
+	golang.org/x/tools v0.41.0 // indirect
49 52
 	gopkg.in/yaml.v3 v3.0.1 // indirect
50 53
 )

+ 10
- 8
go.sum Ver fichero

@@ -30,8 +30,8 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
30 30
 github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
31 31
 github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
32 32
 github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
33
-github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
34
-github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
33
+github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
34
+github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
35 35
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
36 36
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
37 37
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -55,8 +55,8 @@ github.com/panjf2000/ants/v2 v2.11.5 h1:a7LMnMEeux/ebqTux140tRiaqcFTV0q2bEHF03nl
55 55
 github.com/panjf2000/ants/v2 v2.11.5/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
56 56
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
57 57
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
58
-github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
59
-github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
58
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
59
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
60 60
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
61 61
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
62 62
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -68,8 +68,8 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU
68 68
 github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
69 69
 github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
70 70
 github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
71
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
72
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
71
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
72
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
73 73
 github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
74 74
 github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
75 75
 github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
@@ -106,8 +106,9 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
106 106
 golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
107 107
 golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
108 108
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
109
-golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
110 109
 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
110
+golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
111
+golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
111 112
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
112 113
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
113 114
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@@ -140,8 +141,9 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
140 141
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
141 142
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
142 143
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
143
-golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
144 144
 golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
145
+golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
146
+golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
145 147
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
146 148
 google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
147 149
 google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=

+ 1
- 1
internal/cli/access.go Ver fichero

@@ -129,7 +129,7 @@ func (a *Access) getIP(ntw mtglib.Network, protocol string) net.IP {
129 129
 
130 130
 	defer func() {
131 131
 		io.Copy(io.Discard, resp.Body) //nolint: errcheck
132
-		resp.Body.Close() //nolint: errcheck
132
+		resp.Body.Close()              //nolint: errcheck
133 133
 	}()
134 134
 
135 135
 	data, err := io.ReadAll(resp.Body)

+ 9
- 0
internal/cli/run_proxy.go Ver fichero

@@ -240,6 +240,14 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
240 240
 		return fmt.Errorf("cannot build ip allowlist: %w", err)
241 241
 	}
242 242
 
243
+	dcOverrides := map[int][]string{}
244
+	for _, override := range conf.DCOverrides {
245
+		dcid := override.DC.Get()
246
+		for _, addr := range override.IPs {
247
+			dcOverrides[dcid] = append(dcOverrides[dcid], addr.Get(""))
248
+		}
249
+	}
250
+
243 251
 	opts := mtglib.ProxyOpts{
244 252
 		Logger:          logger,
245 253
 		Network:         ntw,
@@ -254,6 +262,7 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
254 262
 
255 263
 		AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
256 264
 		TolerateTimeSkewness:     conf.TolerateTimeSkewness.Value,
265
+		DCOverrides:              dcOverrides,
257 266
 	}
258 267
 
259 268
 	proxy, err := mtglib.NewProxy(opts)

+ 1
- 1
internal/cli/simple_run.go Ver fichero

@@ -18,7 +18,7 @@ type SimpleRun struct {
18 18
 	TCPBuffer           string        `kong:"name='tcp-buffer',short='b',default='4KB',help='Deprecated and ignored'"`                                                 //nolint: lll
19 19
 	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
20 20
 	DomainFrontingPort  uint64        `kong:"name='domain-fronting-port',short='p',default='443',help='A port to access for domain fronting.'"`                        //nolint: lll
21
-	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
21
+	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
22 22
 	Timeout             time.Duration `kong:"name='timeout',short='t',default='10s',help='Network timeout to use'"`                                                    //nolint: lll
23 23
 	Socks5Proxies       []string      `kong:"name='socks5-proxy',short='s',help='Socks5 proxies to use for network access.'"`                                          //nolint: lll
24 24
 	AntiReplayCacheSize string        `kong:"name='antireplay-cache-size',short='a',default='1MB',help='A size of anti-replay cache to use.'"`                         //nolint: lll

+ 4
- 0
internal/config/config.go Ver fichero

@@ -64,6 +64,10 @@ type Config struct {
64 64
 			MetricPrefix TypeMetricPrefix `json:"metricPrefix"`
65 65
 		} `json:"prometheus"`
66 66
 	} `json:"stats"`
67
+	DCOverrides []struct {
68
+		DC  TypeDC         `json:"dc"`
69
+		IPs []TypeHostPort `json:"ips"`
70
+	} `json:"dcOverrides"`
67 71
 }
68 72
 
69 73
 func (c *Config) Validate() error {

+ 5
- 1
internal/config/parse.go Ver fichero

@@ -5,7 +5,7 @@ import (
5 5
 	"encoding/json"
6 6
 	"fmt"
7 7
 
8
-	"github.com/pelletier/go-toml"
8
+	"github.com/pelletier/go-toml/v2"
9 9
 )
10 10
 
11 11
 type tomlConfig struct {
@@ -59,6 +59,10 @@ type tomlConfig struct {
59 59
 			MetricPrefix string `toml:"metric-prefix" json:"metricPrefix,omitempty"`
60 60
 		} `toml:"prometheus" json:"prometheus,omitempty"`
61 61
 	} `toml:"stats" json:"stats,omitempty"`
62
+	DCOverrides []struct {
63
+		DC  uint     `toml:"dc" json:"dc"`
64
+		IPs []string `toml:"ips" json:"ips"`
65
+	} `toml:"dc-overrides" json:"dcOverrides,omitempty"`
62 66
 }
63 67
 
64 68
 func Parse(rawData []byte) (*Config, error) {

+ 41
- 0
internal/config/type_dc.go Ver fichero

@@ -0,0 +1,41 @@
1
+package config
2
+
3
+import (
4
+	"fmt"
5
+	"strconv"
6
+)
7
+
8
+type TypeDC struct {
9
+	Value int
10
+}
11
+
12
+func (t *TypeDC) Set(value string) error {
13
+	parsed, err := strconv.ParseInt(value, 10, 16)
14
+	if err != nil {
15
+		return fmt.Errorf("cannot parse dc: %w", err)
16
+	}
17
+
18
+	if parsed < 0 {
19
+		parsed = -parsed
20
+	}
21
+
22
+	t.Value = int(parsed)
23
+
24
+	return nil
25
+}
26
+
27
+func (t *TypeDC) UnmarshalJSON(data []byte) error {
28
+	return t.Set(string(data))
29
+}
30
+
31
+func (t TypeDC) MarshalJSON() ([]byte, error) {
32
+	return []byte(t.String()), nil
33
+}
34
+
35
+func (t TypeDC) String() string {
36
+	return strconv.Itoa(t.Value)
37
+}
38
+
39
+func (t TypeDC) Get() int {
40
+	return t.Value
41
+}

+ 96
- 0
internal/config/type_dc_test.go Ver fichero

@@ -0,0 +1,96 @@
1
+package config_test
2
+
3
+import (
4
+	"encoding/json"
5
+	"strconv"
6
+	"testing"
7
+
8
+	"github.com/9seconds/mtg/v2/internal/config"
9
+	"github.com/stretchr/testify/assert"
10
+	"github.com/stretchr/testify/suite"
11
+)
12
+
13
+type typeDCTestStruct struct {
14
+	Value config.TypeDC `json:"value"`
15
+}
16
+
17
+type TypeDCTestSuite struct {
18
+	suite.Suite
19
+}
20
+
21
+func (suite *TypeDCTestSuite) TestUnmarshalFail() {
22
+	testData := []string{
23
+		"-1s",
24
+		"1202002020202",
25
+		"xxx",
26
+		"-11111111111111",
27
+		"",
28
+	}
29
+
30
+	for _, v := range testData {
31
+		data, err := json.Marshal(map[string]string{
32
+			"value": v,
33
+		})
34
+		suite.NoError(err)
35
+
36
+		suite.T().Run(v, func(t *testing.T) {
37
+			assert.Error(t, json.Unmarshal(data, &typeDCTestStruct{}))
38
+		})
39
+	}
40
+}
41
+
42
+func (suite *TypeDCTestSuite) TestUnmarshalOk() {
43
+	testData := map[int]int{
44
+		1:   1,
45
+		-1:  1,
46
+		203: 203,
47
+	}
48
+
49
+	for value, expected := range testData {
50
+		data, err := json.Marshal(map[string]int{
51
+			"value": value,
52
+		})
53
+		suite.NoError(err)
54
+
55
+		suite.T().Run(strconv.Itoa(value), func(t *testing.T) {
56
+			testStruct := &typeDCTestStruct{}
57
+
58
+			assert.NoError(t, json.Unmarshal(data, testStruct))
59
+			assert.Equal(t, expected, testStruct.Value.Value)
60
+			assert.Equal(t, expected, testStruct.Value.Get())
61
+		})
62
+	}
63
+}
64
+
65
+func (suite *TypeDCTestSuite) TestMarshalOk() {
66
+	testData := map[string]int{
67
+		"1":   1,
68
+		"203": 203,
69
+	}
70
+
71
+	for k, v := range testData {
72
+		value := k
73
+		expected := v
74
+
75
+		suite.T().Run(value, func(t *testing.T) {
76
+			testStruct := &typeDCTestStruct{}
77
+
78
+			assert.NoError(t, testStruct.Value.Set(value))
79
+
80
+			data, err := json.Marshal(testStruct)
81
+			assert.NoError(t, err)
82
+
83
+			expectedJSON, err := json.Marshal(map[string]int{
84
+				"value": expected,
85
+			})
86
+			assert.NoError(t, err)
87
+
88
+			assert.JSONEq(t, string(expectedJSON), string(data))
89
+		})
90
+	}
91
+}
92
+
93
+func TestTypeDC(t *testing.T) {
94
+	t.Parallel()
95
+	suite.Run(t, &TypeDCTestSuite{})
96
+}

+ 1
- 1
ipblocklist/files/http.go Ver fichero

@@ -23,7 +23,7 @@ func (h httpFile) Open(ctx context.Context) (io.ReadCloser, error) {
23 23
 	if err != nil {
24 24
 		if response != nil {
25 25
 			io.Copy(io.Discard, response.Body) //nolint: errcheck
26
-			response.Body.Close() //nolint: errcheck
26
+			response.Body.Close()              //nolint: errcheck
27 27
 		}
28 28
 
29 29
 		return nil, fmt.Errorf("cannot get url %s: %w", h.url, err)

+ 16
- 0
mise.lock Ver fichero

@@ -0,0 +1,16 @@
1
+[[tools.go]]
2
+version = "1.26.0"
3
+backend = "core:go"
4
+"platforms.linux-arm64" = { checksum = "sha256:bd03b743eb6eb4193ea3c3fd3956546bf0e3ca5b7076c8226334afe6b75704cd", url = "https://dl.google.com/go/go1.26.0.linux-arm64.tar.gz"}
5
+"platforms.linux-x64" = { checksum = "sha256:aac1b08a0fb0c4e0a7c1555beb7b59180b05dfc5a3d62e40e9de90cd42f88235", url = "https://dl.google.com/go/go1.26.0.linux-amd64.tar.gz"}
6
+"platforms.macos-arm64" = { checksum = "sha256:b1640525dfe68f066d56f200bef7bf4dce955a1a893bd061de6754c211431023", url = "https://dl.google.com/go/go1.26.0.darwin-arm64.tar.gz"}
7
+"platforms.macos-x64" = { checksum = "sha256:1ca28b7703cbea05a65b2a1d92d6b308610ef92f8824578a0874f2e60c9d5a22", url = "https://dl.google.com/go/go1.26.0.darwin-amd64.tar.gz"}
8
+"platforms.windows-x64" = { checksum = "sha256:9bbe0fc64236b2b51f6255c05c4232532b8ecc0e6d2e00950bd3021d8a4d07d4", url = "https://dl.google.com/go/go1.26.0.windows-amd64.zip"}
9
+
10
+[[tools."go:golang.org/x/tools/gopls"]]
11
+version = "0.21.0"
12
+backend = "go:golang.org/x/tools/gopls"
13
+
14
+[[tools."go:mvdan.cc/gofumpt"]]
15
+version = "0.9.2"
16
+backend = "go:mvdan.cc/gofumpt"

+ 10
- 0
mtglib/internal/dc/addr.go Ver fichero

@@ -0,0 +1,10 @@
1
+package dc
2
+
3
+type Addr struct {
4
+	Network string
5
+	Address string
6
+}
7
+
8
+func (d Addr) String() string {
9
+	return d.Address
10
+}

+ 33
- 0
mtglib/internal/dc/addr_set.go Ver fichero

@@ -0,0 +1,33 @@
1
+package dc
2
+
3
+import "math/rand/v2"
4
+
5
+type dcAddrSet struct {
6
+	v4 map[int][]Addr
7
+	v6 map[int][]Addr
8
+}
9
+
10
+func (d dcAddrSet) getV4(dc int) []Addr {
11
+	if d.v4 == nil {
12
+		return nil
13
+	}
14
+	return d.get(d.v4[dc])
15
+}
16
+
17
+func (d dcAddrSet) getV6(dc int) []Addr {
18
+	if d.v6 == nil {
19
+		return nil
20
+	}
21
+	return d.get(d.v6[dc])
22
+}
23
+
24
+func (d dcAddrSet) get(addrs []Addr) []Addr {
25
+	otherSet := make([]Addr, 0, len(addrs))
26
+	otherSet = append(otherSet, addrs...)
27
+
28
+	rand.Shuffle(len(otherSet), func(i, j int) {
29
+		otherSet[i], otherSet[j] = otherSet[j], otherSet[i]
30
+	})
31
+
32
+	return otherSet
33
+}

+ 16
- 0
mtglib/internal/dc/addr_test.go Ver fichero

@@ -0,0 +1,16 @@
1
+package dc_test
2
+
3
+import (
4
+	"testing"
5
+
6
+	"github.com/9seconds/mtg/v2/mtglib/internal/dc"
7
+	"github.com/stretchr/testify/assert"
8
+)
9
+
10
+func TestAddr(t *testing.T) {
11
+	t.Parallel()
12
+
13
+	addr := dc.Addr{Network: "tcp4", Address: "127.0.0.1:443"}
14
+
15
+	assert.Equal(t, "127.0.0.1:443", addr.String())
16
+}

+ 73
- 0
mtglib/internal/dc/init.go Ver fichero

@@ -0,0 +1,73 @@
1
+package dc
2
+
3
+type preferIP uint8
4
+
5
+const (
6
+	preferIPOnlyIPv4 preferIP = iota
7
+	preferIPOnlyIPv6
8
+	preferIPPreferIPv4
9
+	preferIPPreferIPv6
10
+)
11
+
12
+const (
13
+	DefaultDC = 2
14
+)
15
+
16
+type Logger interface {
17
+	Info(msg string)
18
+	WarningError(msg string, err error)
19
+}
20
+
21
+var (
22
+	// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/mtproto/mtproto_dc_options.cpp#L30
23
+	defaultDCAddrSet = dcAddrSet{
24
+		v4: map[int][]Addr{
25
+			1: {
26
+				{Network: "tcp4", Address: "149.154.175.50:443"},
27
+			},
28
+			2: {
29
+				{Network: "tcp4", Address: "149.154.167.51:443"},
30
+				{Network: "tcp4", Address: "95.161.76.100:443"},
31
+			},
32
+			3: {
33
+				{Network: "tcp4", Address: "149.154.175.100:443"},
34
+			},
35
+			4: {
36
+				{Network: "tcp4", Address: "149.154.167.91:443"},
37
+			},
38
+			5: {
39
+				{Network: "tcp4", Address: "149.154.171.5:443"},
40
+			},
41
+		},
42
+		v6: map[int][]Addr{
43
+			1: {
44
+				{Network: "tcp6", Address: "[2001:b28:f23d:f001::a]:443"},
45
+			},
46
+			2: {
47
+				{Network: "tcp6", Address: "[2001:67c:04e8:f002::a]:443"},
48
+			},
49
+			3: {
50
+				{Network: "tcp6", Address: "[2001:b28:f23d:f003::a]:443"},
51
+			},
52
+			4: {
53
+				{Network: "tcp6", Address: "[2001:67c:04e8:f004::a]:443"},
54
+			},
55
+			5: {
56
+				{Network: "tcp6", Address: "[2001:b28:f23f:f005::a]:443"},
57
+			},
58
+		},
59
+	}
60
+
61
+	defaultDCOverridesAddrSet = dcAddrSet{
62
+		v4: map[int][]Addr{
63
+			203: {
64
+				{Network: "tcp4", Address: "91.105.192.100:443"},
65
+			},
66
+		},
67
+		v6: map[int][]Addr{
68
+			203: {
69
+				{Network: "tcp6", Address: "[2a0a:f280:0203:000a:5000:0000:0000:0100]:443"},
70
+			},
71
+		},
72
+	}
73
+)

+ 79
- 0
mtglib/internal/dc/telegram.go Ver fichero

@@ -0,0 +1,79 @@
1
+package dc
2
+
3
+import (
4
+	"fmt"
5
+	"net"
6
+	"strings"
7
+)
8
+
9
+type Telegram struct {
10
+	view     dcView
11
+	preferIP preferIP
12
+}
13
+
14
+func (t *Telegram) GetAddresses(dc int) []Addr {
15
+	switch t.preferIP {
16
+	case preferIPOnlyIPv4:
17
+		return t.view.getV4(dc)
18
+	case preferIPOnlyIPv6:
19
+		return t.view.getV4(dc)
20
+	case preferIPPreferIPv4:
21
+		return append(t.view.getV4(dc), t.view.getV6(dc)...)
22
+	}
23
+
24
+	return append(t.view.getV6(dc), t.view.getV4(dc)...)
25
+}
26
+
27
+func New(ipPreference string, userOverrides map[int][]string) (*Telegram, error) {
28
+	var pref preferIP
29
+
30
+	switch strings.ToLower(ipPreference) {
31
+	case "prefer-ipv4":
32
+		pref = preferIPPreferIPv4
33
+	case "prefer-ipv6":
34
+		pref = preferIPPreferIPv6
35
+	case "only-ipv4":
36
+		pref = preferIPOnlyIPv4
37
+	case "only-ipv6":
38
+		pref = preferIPOnlyIPv6
39
+	default:
40
+		return nil, fmt.Errorf("unknown ip preference %s", ipPreference)
41
+	}
42
+
43
+	overrides := dcAddrSet{
44
+		v4: map[int][]Addr{},
45
+		v6: map[int][]Addr{},
46
+	}
47
+	for dc, addrs := range userOverrides {
48
+		for _, addr := range addrs {
49
+			host, _, err := net.SplitHostPort(addr)
50
+			if err != nil {
51
+				return nil, fmt.Errorf("incorrect host %s: %w", addr, err)
52
+			}
53
+
54
+			parsed := net.ParseIP(host)
55
+			if parsed == nil {
56
+				return nil, fmt.Errorf("incorrect host %s", addr)
57
+			}
58
+
59
+			if parsed.To4() != nil {
60
+				overrides.v4[dc] = append(overrides.v4[dc], Addr{
61
+					Network: "tcp4",
62
+					Address: addr,
63
+				})
64
+			} else {
65
+				overrides.v6[dc] = append(overrides.v6[dc], Addr{
66
+					Network: "tcp6",
67
+					Address: addr,
68
+				})
69
+			}
70
+		}
71
+	}
72
+
73
+	return &Telegram{
74
+		view: dcView{
75
+			overrides: overrides,
76
+		},
77
+		preferIP: pref,
78
+	}, nil
79
+}

+ 21
- 0
mtglib/internal/dc/view.go Ver fichero

@@ -0,0 +1,21 @@
1
+package dc
2
+
3
+type dcView struct {
4
+	overrides dcAddrSet
5
+}
6
+
7
+func (d dcView) getV4(dc int) []Addr {
8
+	addrs := d.overrides.getV4(dc)
9
+	addrs = append(addrs, defaultDCOverridesAddrSet.getV4(dc)...)
10
+	addrs = append(addrs, defaultDCAddrSet.getV4(dc)...)
11
+
12
+	return addrs
13
+}
14
+
15
+func (d dcView) getV6(dc int) []Addr {
16
+	addrs := d.overrides.getV6(dc)
17
+	addrs = append(addrs, defaultDCOverridesAddrSet.getV6(dc)...)
18
+	addrs = append(addrs, defaultDCAddrSet.getV6(dc)...)
19
+
20
+	return addrs
21
+}

+ 81
- 0
mtglib/internal/dc/view_test.go Ver fichero

@@ -0,0 +1,81 @@
1
+package dc
2
+
3
+import (
4
+	"fmt"
5
+	"testing"
6
+
7
+	"github.com/stretchr/testify/assert"
8
+	"github.com/stretchr/testify/suite"
9
+)
10
+
11
+type ViewTestSuite struct {
12
+	suite.Suite
13
+
14
+	view dcView
15
+}
16
+
17
+func (suite *ViewTestSuite) SetupSuite() {
18
+	suite.view = dcView{
19
+		overrides: dcAddrSet{
20
+			v4: map[int][]Addr{
21
+				111: {
22
+					{Network: "tcp4", Address: "127.0.0.1:443"},
23
+				},
24
+				203: {
25
+					{Network: "tcp4", Address: "127.0.0.2:443"},
26
+				},
27
+			},
28
+			v6: map[int][]Addr{
29
+				203: {
30
+					{Network: "tcp6", Address: "xxx"},
31
+				},
32
+			},
33
+		},
34
+	}
35
+}
36
+
37
+func (suite *ViewTestSuite) TestGetV4() {
38
+	testData := map[int][]Addr{
39
+		111: {
40
+			{"tcp4", "127.0.0.1:443"},
41
+		},
42
+		203: {
43
+			{"tcp4", "127.0.0.2:443"},
44
+			{"tcp4", "91.105.192.100:443"},
45
+		},
46
+		2: {
47
+			{"tcp4", "149.154.167.51:443"},
48
+			{"tcp4", "95.161.76.100:443"},
49
+		},
50
+	}
51
+
52
+	for dc, addresses := range testData {
53
+		suite.T().Run(fmt.Sprintf("dc%d", dc), func(t *testing.T) {
54
+			assert.ElementsMatch(t, addresses, suite.view.getV4(dc))
55
+		})
56
+	}
57
+}
58
+
59
+func (suite *ViewTestSuite) TestGetV6() {
60
+	testData := map[int][]Addr{
61
+		111: {},
62
+		203: {
63
+			{"tcp6", "xxx"},
64
+			{"tcp6", "[2a0a:f280:0203:000a:5000:0000:0000:0100]:443"},
65
+		},
66
+		1: {
67
+			{"tcp6", "[2001:b28:f23d:f001::a]:443"},
68
+		},
69
+	}
70
+
71
+	for dc, addresses := range testData {
72
+		suite.T().Run(fmt.Sprintf("dc%d", dc), func(t *testing.T) {
73
+			assert.ElementsMatch(t, addresses, suite.view.getV6(dc))
74
+		})
75
+	}
76
+}
77
+
78
+func TestView(t *testing.T) {
79
+	t.Parallel()
80
+	suite.Run(t, &ViewTestSuite{})
81
+}

+ 2
- 2
mtglib/internal/relay/relay.go Ver fichero

@@ -10,7 +10,7 @@ import (
10 10
 
11 11
 func Relay(ctx context.Context, log Logger, telegramConn, clientConn essentials.Conn) {
12 12
 	defer telegramConn.Close() //nolint: errcheck
13
-	defer clientConn.Close() //nolint: errcheck
13
+	defer clientConn.Close()   //nolint: errcheck
14 14
 
15 15
 	ctx, cancel := context.WithCancel(ctx)
16 16
 	defer cancel()
@@ -18,7 +18,7 @@ func Relay(ctx context.Context, log Logger, telegramConn, clientConn essentials.
18 18
 	go func() {
19 19
 		<-ctx.Done()
20 20
 		telegramConn.Close() //nolint: errcheck
21
-		clientConn.Close() //nolint: errcheck
21
+		clientConn.Close()   //nolint: errcheck
22 22
 	}()
23 23
 
24 24
 	closeChan := make(chan struct{})

+ 0
- 41
mtglib/internal/telegram/address_pool.go Ver fichero

@@ -1,41 +0,0 @@
1
-package telegram
2
-
3
-import "math/rand"
4
-
5
-type addressPool struct {
6
-	v4 [][]tgAddr
7
-	v6 [][]tgAddr
8
-}
9
-
10
-func (a addressPool) isValidDC(dc int) bool {
11
-	return dc > 0 && dc <= len(a.v4) && dc <= len(a.v6)
12
-}
13
-
14
-func (a addressPool) getRandomDC() int {
15
-	return 1 + rand.Intn(len(a.v4))
16
-}
17
-
18
-func (a addressPool) getV4(dc int) []tgAddr {
19
-	return a.get(a.v4, dc-1)
20
-}
21
-
22
-func (a addressPool) getV6(dc int) []tgAddr {
23
-	return a.get(a.v6, dc-1)
24
-}
25
-
26
-func (a addressPool) get(addresses [][]tgAddr, dc int) []tgAddr {
27
-	if dc < 0 || dc >= len(addresses) {
28
-		return nil
29
-	}
30
-
31
-	rv := make([]tgAddr, len(addresses[dc]))
32
-	copy(rv, addresses[dc])
33
-
34
-	if len(rv) > 1 {
35
-		rand.Shuffle(len(rv), func(i, j int) {
36
-			rv[i], rv[j] = rv[j], rv[i]
37
-		})
38
-	}
39
-
40
-	return rv
41
-}

+ 0
- 90
mtglib/internal/telegram/init.go Ver fichero

@@ -1,90 +0,0 @@
1
-package telegram
2
-
3
-import (
4
-	"context"
5
-	"errors"
6
-
7
-	"github.com/9seconds/mtg/v2/essentials"
8
-)
9
-
10
-var errNoAddresses = errors.New("no addresses")
11
-
12
-type preferIP uint8
13
-
14
-const (
15
-	preferIPOnlyIPv4 preferIP = iota
16
-	preferIPOnlyIPv6
17
-	preferIPPreferIPv4
18
-	preferIPPreferIPv6
19
-)
20
-
21
-type tgAddr struct {
22
-	network string
23
-	address string
24
-}
25
-
26
-// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/mtproto/mtproto_dc_options.cpp#L30
27
-var (
28
-	productionV4Addresses = [][]tgAddr{
29
-		{ // dc1
30
-			{network: "tcp4", address: "149.154.175.50:443"},
31
-		},
32
-		{ // dc2
33
-			{network: "tcp4", address: "149.154.167.51:443"},
34
-			{network: "tcp4", address: "95.161.76.100:443"},
35
-		},
36
-		{ // dc3
37
-			{network: "tcp4", address: "149.154.175.100:443"},
38
-		},
39
-		{ // dc4
40
-			{network: "tcp4", address: "149.154.167.91:443"},
41
-		},
42
-		{ // dc5
43
-			{network: "tcp4", address: "149.154.171.5:443"},
44
-		},
45
-	}
46
-	productionV6Addresses = [][]tgAddr{
47
-		{ // dc1
48
-			{network: "tcp6", address: "[2001:b28:f23d:f001::a]:443"},
49
-		},
50
-		{ // dc2
51
-			{network: "tcp6", address: "[2001:67c:04e8:f002::a]:443"},
52
-		},
53
-		{ // dc3
54
-			{network: "tcp6", address: "[2001:b28:f23d:f003::a]:443"},
55
-		},
56
-		{ // dc4
57
-			{network: "tcp6", address: "[2001:67c:04e8:f004::a]:443"},
58
-		},
59
-		{ // dc5
60
-			{network: "tcp6", address: "[2001:b28:f23f:f005::a]:443"},
61
-		},
62
-	}
63
-
64
-	testV4Addresses = [][]tgAddr{
65
-		{ // dc1
66
-			{network: "tcp4", address: "149.154.175.10:443"},
67
-		},
68
-		{ // dc2
69
-			{network: "tcp4", address: "149.154.167.40:443"},
70
-		},
71
-		{ // dc3
72
-			{network: "tcp4", address: "149.154.175.117:443"},
73
-		},
74
-	}
75
-	testV6Addresses = [][]tgAddr{
76
-		{ // dc1
77
-			{network: "tcp6", address: "[2001:b28:f23d:f001::e]:443"},
78
-		},
79
-		{ // dc2
80
-			{network: "tcp6", address: "[2001:67c:04e8:f002::e]:443"},
81
-		},
82
-		{ // dc3
83
-			{network: "tcp6", address: "[2001:b28:f23d:f003::e]:443"},
84
-		},
85
-	}
86
-)
87
-
88
-type Dialer interface {
89
-	DialContext(ctx context.Context, network, address string) (essentials.Conn, error)
90
-}

+ 0
- 83
mtglib/internal/telegram/telegram.go Ver fichero

@@ -1,83 +0,0 @@
1
-package telegram
2
-
3
-import (
4
-	"context"
5
-	"fmt"
6
-	"strings"
7
-
8
-	"github.com/9seconds/mtg/v2/essentials"
9
-)
10
-
11
-type Telegram struct {
12
-	dialer   Dialer
13
-	preferIP preferIP
14
-	pool     addressPool
15
-}
16
-
17
-func (t Telegram) Dial(ctx context.Context, dc int) (essentials.Conn, error) {
18
-	var addresses []tgAddr
19
-
20
-	switch t.preferIP {
21
-	case preferIPOnlyIPv4:
22
-		addresses = t.pool.getV4(dc)
23
-	case preferIPOnlyIPv6:
24
-		addresses = t.pool.getV6(dc)
25
-	case preferIPPreferIPv4:
26
-		addresses = append(t.pool.getV4(dc), t.pool.getV6(dc)...)
27
-	case preferIPPreferIPv6:
28
-		addresses = append(t.pool.getV6(dc), t.pool.getV4(dc)...)
29
-	}
30
-
31
-	var conn essentials.Conn
32
-
33
-	err := errNoAddresses
34
-
35
-	for _, v := range addresses {
36
-		conn, err = t.dialer.DialContext(ctx, v.network, v.address)
37
-		if err == nil {
38
-			return conn, nil
39
-		}
40
-	}
41
-
42
-	return nil, fmt.Errorf("cannot dial to %d dc: %w", dc, err)
43
-}
44
-
45
-func (t Telegram) IsKnownDC(dc int) bool {
46
-	return t.pool.isValidDC(dc)
47
-}
48
-
49
-func (t Telegram) GetFallbackDC() int {
50
-	return t.pool.getRandomDC()
51
-}
52
-
53
-func New(dialer Dialer, ipPreference string, useTestDCs bool) (*Telegram, error) {
54
-	var pref preferIP
55
-
56
-	switch strings.ToLower(ipPreference) {
57
-	case "prefer-ipv4":
58
-		pref = preferIPPreferIPv4
59
-	case "prefer-ipv6":
60
-		pref = preferIPPreferIPv6
61
-	case "only-ipv4":
62
-		pref = preferIPOnlyIPv4
63
-	case "only-ipv6":
64
-		pref = preferIPOnlyIPv6
65
-	default:
66
-		return nil, fmt.Errorf("unknown ip preference %s", ipPreference)
67
-	}
68
-
69
-	pool := addressPool{
70
-		v4: productionV4Addresses,
71
-		v6: productionV6Addresses,
72
-	}
73
-	if useTestDCs {
74
-		pool.v4 = testV4Addresses
75
-		pool.v6 = testV6Addresses
76
-	}
77
-
78
-	return &Telegram{
79
-		dialer:   dialer,
80
-		preferIP: pref,
81
-		pool:     pool,
82
-	}, nil
83
-}

+ 0
- 159
mtglib/internal/telegram/telegram_internal_test.go Ver fichero

@@ -1,159 +0,0 @@
1
-package telegram
2
-
3
-import (
4
-	"context"
5
-	"errors"
6
-	"io"
7
-	"net"
8
-	"strconv"
9
-	"testing"
10
-
11
-	"github.com/9seconds/mtg/v2/internal/testlib"
12
-	"github.com/stretchr/testify/assert"
13
-	"github.com/stretchr/testify/mock"
14
-	"github.com/stretchr/testify/suite"
15
-)
16
-
17
-type TelegramTestSuite struct {
18
-	suite.Suite
19
-
20
-	dialerMock *testlib.MtglibNetworkMock
21
-	t          *Telegram
22
-}
23
-
24
-func (suite *TelegramTestSuite) SetupTest() {
25
-	suite.dialerMock = &testlib.MtglibNetworkMock{}
26
-	suite.t, _ = New(suite.dialerMock, "prefer-ipv4", false)
27
-}
28
-
29
-func (suite *TelegramTestSuite) TearDownTest() {
30
-	suite.dialerMock.AssertExpectations(suite.T())
31
-}
32
-
33
-func (suite *TelegramTestSuite) TestUnknownDC() {
34
-	testData := []int{
35
-		-1,
36
-		0,
37
-		6,
38
-		100,
39
-	}
40
-
41
-	for _, v := range testData {
42
-		value := v
43
-
44
-		suite.T().Run(strconv.Itoa(value), func(t *testing.T) {
45
-			_, err := suite.t.Dial(context.Background(), value)
46
-			assert.Error(t, err)
47
-			assert.False(t, suite.t.IsKnownDC(value))
48
-		})
49
-	}
50
-}
51
-
52
-func (suite *TelegramTestSuite) TestDialToCorrectIPs() {
53
-	testData := map[int][]tgAddr{}
54
-
55
-	for i := 1; i <= 5; i++ {
56
-		testData[i] = []tgAddr{}
57
-		testData[i] = append(testData[i], productionV4Addresses[i-1]...)
58
-		testData[i] = append(testData[i], productionV6Addresses[i-1]...)
59
-	}
60
-
61
-	for i, v := range testData {
62
-		idx := i
63
-		addresses := v
64
-
65
-		suite.T().Run(strconv.Itoa(idx), func(t *testing.T) {
66
-			for _, addr := range addresses {
67
-				suite.dialerMock.
68
-					On("DialContext", mock.Anything, addr.network, addr.address).
69
-					Once().
70
-					Return((*net.TCPConn)(nil), io.EOF)
71
-			}
72
-
73
-			_, err := suite.t.Dial(context.Background(), idx)
74
-			assert.True(t, errors.Is(err, io.EOF))
75
-			assert.True(t, suite.t.IsKnownDC(idx))
76
-		})
77
-	}
78
-}
79
-
80
-func (suite *TelegramTestSuite) TestDialPreferIPRange() {
81
-	testData := map[string][]tgAddr{
82
-		"prefer-ipv4": {testV4Addresses[0][0], testV6Addresses[0][0]},
83
-		"prefer-ipv6": {testV6Addresses[0][0], testV4Addresses[0][0]},
84
-		"only-ipv4":   {testV4Addresses[0][0]},
85
-		"only-ipv6":   {testV6Addresses[0][0]},
86
-	}
87
-
88
-	for k, v := range testData {
89
-		name := k
90
-		addresses := v
91
-
92
-		suite.T().Run(name, func(t *testing.T) {
93
-			for _, addr := range addresses {
94
-				suite.dialerMock.
95
-					On("DialContext", mock.Anything, addr.network, addr.address).
96
-					Once().
97
-					Return((*net.TCPConn)(nil), io.EOF)
98
-			}
99
-
100
-			tg, _ := New(suite.dialerMock, name, true)
101
-			_, err := tg.Dial(context.Background(), 1)
102
-
103
-			assert.True(t, errors.Is(err, io.EOF))
104
-		})
105
-	}
106
-}
107
-
108
-func (suite *TelegramTestSuite) TestDialPreferIPPriority() {
109
-	testData := map[string]tgAddr{
110
-		"prefer-ipv4": productionV4Addresses[0][0],
111
-		"prefer-ipv6": productionV6Addresses[0][0],
112
-	}
113
-
114
-	for k, v := range testData {
115
-		name := k
116
-		addr := v
117
-
118
-		suite.T().Run(name, func(t *testing.T) {
119
-			conn := &net.TCPConn{}
120
-
121
-			suite.dialerMock.
122
-				On("DialContext", mock.Anything, addr.network, addr.address).
123
-				Once().
124
-				Return(conn, nil)
125
-
126
-			tg, _ := New(suite.dialerMock, name, false)
127
-
128
-			res, err := tg.Dial(context.Background(), 1)
129
-			assert.NoError(t, err)
130
-			assert.Equal(t, conn, res)
131
-		})
132
-	}
133
-}
134
-
135
-func (suite *TelegramTestSuite) TestUnknownPreferIP() {
136
-	_, err := New(suite.dialerMock, "xxx", false)
137
-	suite.Error(err)
138
-}
139
-
140
-func (suite *TelegramTestSuite) TestFallbackDC() {
141
-	dcs := make([]int, 10)
142
-
143
-	for i := 0; i < len(dcs); i++ {
144
-		dcs[i] = suite.t.GetFallbackDC()
145
-	}
146
-
147
-	for _, v := range dcs {
148
-		value := v
149
-
150
-		suite.T().Run(strconv.Itoa(value), func(t *testing.T) {
151
-			assert.True(t, suite.t.IsKnownDC(value))
152
-		})
153
-	}
154
-}
155
-
156
-func TestTelegram(t *testing.T) {
157
-	t.Parallel()
158
-	suite.Run(t, &TelegramTestSuite{})
159
-}

+ 19
- 11
mtglib/proxy.go Ver fichero

@@ -10,11 +10,11 @@ import (
10 10
 	"time"
11 11
 
12 12
 	"github.com/9seconds/mtg/v2/essentials"
13
+	"github.com/9seconds/mtg/v2/mtglib/internal/dc"
13 14
 	"github.com/9seconds/mtg/v2/mtglib/internal/faketls"
14 15
 	"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
15 16
 	"github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2"
16 17
 	"github.com/9seconds/mtg/v2/mtglib/internal/relay"
17
-	"github.com/9seconds/mtg/v2/mtglib/internal/telegram"
18 18
 	"github.com/panjf2000/ants/v2"
19 19
 )
20 20
 
@@ -28,7 +28,7 @@ type Proxy struct {
28 28
 	tolerateTimeSkewness     time.Duration
29 29
 	domainFrontingPort       int
30 30
 	workerPool               *ants.PoolWithFunc
31
-	telegram                 *telegram.Telegram
31
+	telegram                 *dc.Telegram
32 32
 
33 33
 	secret          Secret
34 34
 	network         Network
@@ -219,18 +219,26 @@ func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error {
219 219
 }
220 220
 
221 221
 func (p *Proxy) doTelegramCall(ctx *streamContext) error {
222
-	dc := ctx.dc
223
-
224
-	if p.allowFallbackOnUnknownDC && !p.telegram.IsKnownDC(dc) {
225
-		dc = p.telegram.GetFallbackDC()
226
-		ctx.logger = ctx.logger.BindInt("fallback_dc", dc)
222
+	dcid := ctx.dc
227 223
 
224
+	addresses := p.telegram.GetAddresses(dcid)
225
+	if len(addresses) == 0 && p.allowFallbackOnUnknownDC {
226
+		ctx.logger = ctx.logger.BindInt("fallback_dc", dc.DefaultDC)
228 227
 		ctx.logger.Warning("unknown DC, fallbacks")
228
+		addresses = p.telegram.GetAddresses(dc.DefaultDC)
229 229
 	}
230 230
 
231
-	conn, err := p.telegram.Dial(ctx, dc)
231
+	var conn essentials.Conn
232
+	var err error
233
+
234
+	for _, addr := range addresses {
235
+		conn, err = p.network.Dial(addr.Network, addr.Address)
236
+		if err == nil {
237
+			break
238
+		}
239
+	}
232 240
 	if err != nil {
233
-		return fmt.Errorf("cannot dial to Telegram: %w", err)
241
+		return fmt.Errorf("no addresses to call: %w", err)
234 242
 	}
235 243
 
236 244
 	encryptor, decryptor, err := obfuscated2.ServerHandshake(conn)
@@ -292,9 +300,9 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
292 300
 		return nil, fmt.Errorf("invalid settings: %w", err)
293 301
 	}
294 302
 
295
-	tg, err := telegram.New(opts.Network, opts.getPreferIP(), opts.UseTestDCs)
303
+	tg, err := dc.New(opts.getPreferIP(), opts.DCOverrides)
296 304
 	if err != nil {
297
-		return nil, fmt.Errorf("cannot build telegram dialer: %w", err)
305
+		return nil, fmt.Errorf("cannot build telegram dc fetcher: %w", err)
298 306
 	}
299 307
 
300 308
 	ctx, cancel := context.WithCancel(context.Background())

+ 8
- 0
mtglib/proxy_opts.go Ver fichero

@@ -110,7 +110,15 @@ type ProxyOpts struct {
110 110
 	// Telegram-related projects.
111 111
 	//
112 112
 	// This is an optional setting.
113
+	//
114
+	// OBSOLETE and DEPRECATED. Ignored.
113 115
 	UseTestDCs bool
116
+
117
+	// DCOverrides defines a set of IP addresses that should be used
118
+	// with a higher priority to those that are calculated somehow by mtg.
119
+	//
120
+	// This is an optional setting
121
+	DCOverrides map[int][]string
114 122
 }
115 123
 
116 124
 func (p ProxyOpts) valid() error {

+ 1
- 1
network/init.go Ver fichero

@@ -63,7 +63,7 @@ const (
63 63
 	// DefaultDOHHostname defines a default IP address for DOH host. Since mtg is
64 64
 	// simple, please pass IP address here. We do not have bootstrap servers here
65 65
 	// embedded.
66
-	DefaultDOHHostname = "9.9.9.9"
66
+	DefaultDOHHostname = "1.1.1.1"
67 67
 
68 68
 	// DNSTimeout defines a timeout for DNS queries.
69 69
 	DNSTimeout = 5 * time.Second

+ 1
- 1
stats/statsd_test.go Ver fichero

@@ -100,7 +100,7 @@ func (suite *StatsdTestSuite) SetupTest() {
100 100
 
101 101
 func (suite *StatsdTestSuite) TearDownTest() {
102 102
 	suite.statsd.Shutdown()
103
-	suite.factory.Close() //nolint: errcheck
103
+	suite.factory.Close()      //nolint: errcheck
104 104
 	suite.statsdServer.Close() //nolint: errcheck
105 105
 }
106 106
 

Loading…
Cancelar
Guardar