소스 검색

Direct proxy works

tags/1.0^2
9seconds 6 년 전
부모
커밋
2492a47d0a
87개의 변경된 파일1838개의 추가작업 그리고 1402개의 파일을 삭제
  1. 37
    0
      _antireplay/cache.go
  2. 1
    1
      _antireplay/hasher.go
  3. 0
    0
      _client/client.go
  4. 0
    0
      _client/direct.go
  5. 0
    0
      _client/middle.go
  6. 224
    0
      _config/config.go
  7. 7
    24
      _config/global_ips.go
  8. 6
    33
      _config/urls.go
  9. 0
    0
      _mtproto/connection_options.go
  10. 0
    0
      _mtproto/rpc/handshake_request.go
  11. 0
    0
      _mtproto/rpc/handshake_response.go
  12. 0
    0
      _mtproto/rpc/nonce_request.go
  13. 0
    0
      _mtproto/rpc/nonce_response.go
  14. 0
    0
      _mtproto/rpc/proxy_flags.go
  15. 0
    0
      _mtproto/rpc/proxy_request.go
  16. 0
    0
      _mtproto/rpc/rpc.go
  17. 121
    0
      _obfuscated2/frame.go
  18. 0
    0
      _obfuscated2/frame_test.go
  19. 0
    0
      _obfuscated2/obfuscated2.go
  20. 0
    0
      _obfuscated2/obfuscated2_test.go
  21. 178
    0
      _proxy/proxy.go
  22. 0
    0
      _stats/channels.go
  23. 0
    0
      _stats/init.go
  24. 0
    0
      _stats/prometheus.go
  25. 0
    0
      _stats/server.go
  26. 175
    0
      _stats/stats.go
  27. 0
    0
      _stats/statsd.go
  28. 0
    0
      _telegram/dialer.go
  29. 79
    0
      _telegram/direct.go
  30. 0
    0
      _telegram/middle.go
  31. 0
    0
      _telegram/middle_caller.go
  32. 0
    0
      _telegram/telegram.go
  33. 0
    0
      _utils/read_current_data.go
  34. 15
    0
      _utils/reverse_bytes.go
  35. 15
    0
      _utils/uint24.go
  36. 0
    0
      _wrappers/blockcipher.go
  37. 0
    0
      _wrappers/conn.go
  38. 0
    0
      _wrappers/mtproto_abridged.go
  39. 0
    0
      _wrappers/mtproto_cipher.go
  40. 0
    0
      _wrappers/mtproto_frame.go
  41. 0
    0
      _wrappers/mtproto_intermediate.go
  42. 0
    0
      _wrappers/mtproto_intermediate_secure.go
  43. 0
    0
      _wrappers/mtproto_proxy.go
  44. 0
    0
      _wrappers/streamcipher.go
  45. 0
    0
      _wrappers/streamcipher_pool.go
  46. 0
    0
      _wrappers/wrap.go
  47. 11
    18
      antireplay/cache.go
  48. 3
    3
      cli/generate.go
  49. 86
    0
      cli/proxy.go
  50. 1
    1
      cli/utils.go
  51. 208
    174
      config/config.go
  52. 23
    6
      config/global_ips.go
  53. 32
    5
      config/urls.go
  54. 5
    0
      conntypes/dc.go
  55. 1
    1
      conntypes/protocol.go
  56. 1
    1
      conntypes/type.go
  57. 31
    31
      main.go
  58. 0
    32
      newantireplay/cache.go
  59. 0
    61
      newcli/proxy.go
  60. 0
    258
      newconfig/config.go
  61. 0
    95
      newobfuscated2/client_protocol.go
  62. 0
    54
      newobfuscated2/frame.go
  63. 0
    61
      newobfuscated2/telegram_protocol.go
  64. 0
    7
      newprotocol/base_protocol.go
  65. 0
    1
      newproxy/proxy.go
  66. 0
    93
      newstats/stats.go
  67. 94
    0
      obfuscated2/client_protocol.go
  68. 22
    89
      obfuscated2/frame.go
  69. 78
    0
      obfuscated2/telegram_protocol.go
  70. 21
    0
      protocol/base_protocol.go
  71. 22
    0
      protocol/interfaces.go
  72. 18
    0
      protocol/request.go
  73. 70
    120
      proxy/proxy.go
  74. 54
    136
      stats/stats.go
  75. 14
    10
      stats/stats_json.go
  76. 12
    12
      stats/stats_prometheus.go
  77. 16
    16
      stats/stats_statsd.go
  78. 70
    0
      telegram/base.go
  79. 20
    38
      telegram/direct.go
  80. 15
    0
      telegram/interfaces.go
  81. 25
    0
      utils/init_tcp.go
  82. 11
    0
      utils/stream_cipher.go
  83. 0
    4
      utils/uint24.go
  84. 1
    1
      wrappers/interfaces.go
  85. 8
    9
      wrappers/wrapper_conn.go
  86. 1
    1
      wrappers/wrapper_obfuscated2.go
  87. 6
    6
      wrappers/wrapper_stats.go

+ 37
- 0
_antireplay/cache.go 파일 보기

@@ -0,0 +1,37 @@
1
+package antireplay
2
+
3
+import (
4
+	"github.com/allegro/bigcache"
5
+	"github.com/juju/errors"
6
+
7
+	"github.com/9seconds/mtg/config"
8
+)
9
+
10
+// Cache defines storage for obfuscated2 handshake frames.
11
+type Cache struct {
12
+	cache *bigcache.BigCache
13
+}
14
+
15
+func (a Cache) Add(frame []byte) {
16
+	a.cache.Set(string(frame), nil) // nolint: errcheck
17
+}
18
+
19
+func (a Cache) Has(frame []byte) bool {
20
+	_, err := a.cache.Get(string(frame))
21
+
22
+	return err == nil
23
+}
24
+
25
+func NewCache(config *config.Config) (Cache, error) {
26
+	cache, err := bigcache.NewBigCache(bigcache.Config{
27
+		Shards:           1024,
28
+		LifeWindow:       config.AntiReplayEvictionTime,
29
+		Hasher:           hasher{},
30
+		HardMaxCacheSize: config.AntiReplayMaxSize,
31
+	})
32
+	if err != nil {
33
+		return Cache{}, errors.Annotate(err, "Cannot make cache")
34
+	}
35
+
36
+	return Cache{cache}, nil
37
+}

newantireplay/hasher.go → _antireplay/hasher.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newantireplay
1
+package antireplay
2 2
 
3 3
 import "github.com/cespare/xxhash"
4 4
 

client/client.go → _client/client.go 파일 보기


client/direct.go → _client/direct.go 파일 보기


client/middle.go → _client/middle.go 파일 보기


+ 224
- 0
_config/config.go 파일 보기

@@ -0,0 +1,224 @@
1
+package config
2
+
3
+import (
4
+	"bytes"
5
+	"encoding/hex"
6
+	"fmt"
7
+	"net"
8
+	"strconv"
9
+	"time"
10
+
11
+	"github.com/juju/errors"
12
+	statsd "gopkg.in/alexcesaro/statsd.v2"
13
+)
14
+
15
+// Config represents common configuration of mtg.
16
+type Config struct {
17
+	Debug      bool
18
+	Verbose    bool
19
+	SecureMode bool
20
+	SecureOnly bool
21
+
22
+	ReadBufferSize  int
23
+	WriteBufferSize int
24
+
25
+	BindPort       uint16
26
+	PublicIPv4Port uint16
27
+	PublicIPv6Port uint16
28
+	StatsPort      uint16
29
+
30
+	BindIP     net.IP
31
+	PublicIPv4 net.IP
32
+	PublicIPv6 net.IP
33
+	StatsIP    net.IP
34
+
35
+	AntiReplayMaxSize      int
36
+	AntiReplayEvictionTime time.Duration
37
+
38
+	StatsD struct {
39
+		Addr       net.Addr
40
+		Prefix     string
41
+		Tags       map[string]string
42
+		TagsFormat statsd.TagFormat
43
+		Enabled    bool
44
+	}
45
+	Prometheus struct {
46
+		Prefix string
47
+	}
48
+
49
+	Secret []byte
50
+	AdTag  []byte
51
+}
52
+
53
+// URLs contains links to the proxy (tg://, t.me) and their QR codes.
54
+type URLs struct {
55
+	TG        string `json:"tg_url"`
56
+	TMe       string `json:"tme_url"`
57
+	TGQRCode  string `json:"tg_qrcode"`
58
+	TMeQRCode string `json:"tme_qrcode"`
59
+}
60
+
61
+// IPURLs contains links to both ipv4 and ipv6 of the proxy.
62
+type IPURLs struct {
63
+	IPv4      URLs   `json:"ipv4"`
64
+	IPv6      URLs   `json:"ipv6"`
65
+	BotSecret string `json:"secret_for_mtproxybot"`
66
+}
67
+
68
+// BindAddr returns connection for this server to bind to.
69
+func (c *Config) BindAddr() string {
70
+	return getAddr(c.BindIP, c.BindPort)
71
+}
72
+
73
+// StatAddr returns connection string to the stats API.
74
+func (c *Config) StatAddr() string {
75
+	return getAddr(c.StatsIP, c.StatsPort)
76
+}
77
+
78
+// UseMiddleProxy defines if this proxy has to connect middle proxies
79
+// which supports promoted channels or directly access Telegram.
80
+func (c *Config) UseMiddleProxy() bool {
81
+	return len(c.AdTag) > 0
82
+}
83
+
84
+// BotSecretString returns secret string which should work with MTProxybot.
85
+func (c *Config) BotSecretString() string {
86
+	return hex.EncodeToString(c.Secret)
87
+}
88
+
89
+// SecretString returns a secret in a form entered on the start of the
90
+// application.
91
+func (c *Config) SecretString() string {
92
+	secret := c.BotSecretString()
93
+	if c.SecureMode {
94
+		return "dd" + secret
95
+	}
96
+	return secret
97
+}
98
+
99
+// GetURLs returns configured IPURLs instance with links to this server.
100
+func (c *Config) GetURLs() IPURLs {
101
+	urls := IPURLs{}
102
+	secret := c.SecretString()
103
+	if c.PublicIPv4 != nil {
104
+		urls.IPv4 = getURLs(c.PublicIPv4, c.PublicIPv4Port, secret)
105
+	}
106
+	if c.PublicIPv6 != nil {
107
+		urls.IPv6 = getURLs(c.PublicIPv6, c.PublicIPv6Port, secret)
108
+	}
109
+	urls.BotSecret = c.BotSecretString()
110
+
111
+	return urls
112
+}
113
+
114
+func getAddr(host fmt.Stringer, port uint16) string {
115
+	return net.JoinHostPort(host.String(), strconv.Itoa(int(port)))
116
+}
117
+
118
+// NewConfig returns new configuration. If required, it manages and
119
+// fetches data from external sources. Parameters passed to this
120
+// function, should come from command line arguments.
121
+func NewConfig(debug, verbose bool, // nolint: gocyclo
122
+	writeBufferSize, readBufferSize uint32,
123
+	bindIP, publicIPv4, publicIPv6, statsIP net.IP,
124
+	bindPort, publicIPv4Port, publicIPv6Port, statsPort, statsdPort uint16,
125
+	statsdIP, statsdNetwork, statsdPrefix, statsdTagsFormat string,
126
+	statsdTags map[string]string, prometheusPrefix string,
127
+	secureOnly bool,
128
+	antiReplayMaxSize int, antiReplayEvictionTime time.Duration,
129
+	secret, adtag []byte) (*Config, error) {
130
+	secureMode := secureOnly
131
+	if bytes.HasPrefix(secret, []byte{0xdd}) && len(secret) == 17 {
132
+		secureMode = true
133
+		secret = bytes.TrimPrefix(secret, []byte{0xdd})
134
+	} else if len(secret) != 16 {
135
+		return nil, errors.New("Telegram demands secret of length 32")
136
+	}
137
+
138
+	var err error
139
+	if publicIPv4 == nil {
140
+		publicIPv4, err = getGlobalIPv4()
141
+		if err != nil {
142
+			publicIPv4 = nil
143
+		} else if publicIPv4.To4() == nil {
144
+			return nil, errors.Errorf("IP %s is not IPv4", publicIPv4.String())
145
+		}
146
+	}
147
+	if publicIPv4Port == 0 {
148
+		publicIPv4Port = bindPort
149
+	}
150
+
151
+	if publicIPv6 == nil {
152
+		publicIPv6, err = getGlobalIPv6()
153
+		if err != nil {
154
+			publicIPv6 = nil
155
+		} else if publicIPv6.To4() != nil {
156
+			return nil, errors.Errorf("IP %s is not IPv6", publicIPv6.String())
157
+		}
158
+	}
159
+	if publicIPv6Port == 0 {
160
+		publicIPv6Port = bindPort
161
+	}
162
+
163
+	if statsIP == nil {
164
+		statsIP = publicIPv4
165
+	}
166
+
167
+	conf := &Config{
168
+		Debug:                  debug,
169
+		Verbose:                verbose,
170
+		SecureOnly:             secureOnly,
171
+		BindIP:                 bindIP,
172
+		BindPort:               bindPort,
173
+		PublicIPv4:             publicIPv4,
174
+		PublicIPv4Port:         publicIPv4Port,
175
+		PublicIPv6:             publicIPv6,
176
+		PublicIPv6Port:         publicIPv6Port,
177
+		StatsIP:                statsIP,
178
+		StatsPort:              statsPort,
179
+		Secret:                 secret,
180
+		AdTag:                  adtag,
181
+		SecureMode:             secureMode,
182
+		ReadBufferSize:         int(readBufferSize),
183
+		WriteBufferSize:        int(writeBufferSize),
184
+		AntiReplayMaxSize:      antiReplayMaxSize,
185
+		AntiReplayEvictionTime: antiReplayEvictionTime,
186
+	}
187
+	conf.Prometheus.Prefix = prometheusPrefix
188
+
189
+	if statsdIP != "" {
190
+		conf.StatsD.Enabled = true
191
+		conf.StatsD.Prefix = statsdPrefix
192
+		conf.StatsD.Tags = statsdTags
193
+
194
+		var (
195
+			addr net.Addr
196
+			err  error
197
+		)
198
+		hostPort := net.JoinHostPort(statsdIP, strconv.Itoa(int(statsdPort)))
199
+		switch statsdNetwork {
200
+		case "tcp":
201
+			addr, err = net.ResolveTCPAddr("tcp", hostPort)
202
+		case "udp":
203
+			addr, err = net.ResolveUDPAddr("udp", hostPort)
204
+		default:
205
+			err = errors.Errorf("Unknown network %s", statsdNetwork)
206
+		}
207
+		if err != nil {
208
+			return nil, errors.Annotate(err, "Cannot resolve statsd address")
209
+		}
210
+		conf.StatsD.Addr = addr
211
+
212
+		switch statsdTagsFormat {
213
+		case "datadog":
214
+			conf.StatsD.TagsFormat = statsd.Datadog
215
+		case "influxdb":
216
+			conf.StatsD.TagsFormat = statsd.InfluxDB
217
+		case "":
218
+		default:
219
+			return nil, errors.Errorf("Unknown tags format %s", statsdTagsFormat)
220
+		}
221
+	}
222
+
223
+	return conf, nil
224
+}

newconfig/global_ips.go → _config/global_ips.go 파일 보기

@@ -1,43 +1,29 @@
1
-package newconfig
1
+package config
2 2
 
3 3
 import (
4 4
 	"context"
5
-	"io"
6 5
 	"io/ioutil"
7 6
 	"net"
8 7
 	"net/http"
9 8
 	"strings"
10
-	"time"
11 9
 
12 10
 	"github.com/juju/errors"
13 11
 )
14 12
 
15
-const (
16
-	ifconfigAddress = "https://ifconfig.co/ip"
17
-	ifconfigTimeout = 10 * time.Second
18
-)
13
+const ifconfigAddress = "https://ifconfig.co/ip"
19 14
 
20 15
 func getGlobalIPv4() (net.IP, error) {
21
-	ip, err := fetchIP("tcp4")
22
-	if err != nil || ip.To4() == nil {
23
-		return nil, errors.Annotate(err, "Cannot find public ipv4 address")
24
-	}
25
-	return ip, nil
16
+	return fetchIP("tcp4")
26 17
 }
27 18
 
28 19
 func getGlobalIPv6() (net.IP, error) {
29
-	ip, err := fetchIP("tcp6")
30
-	if err != nil || ip.To4() != nil {
31
-		return nil, errors.Annotate(err, "Cannot find public ipv6 address")
32
-	}
33
-	return ip, nil
20
+	return fetchIP("tcp6")
34 21
 }
35 22
 
36 23
 func fetchIP(network string) (net.IP, error) {
37 24
 	dialer := &net.Dialer{FallbackDelay: -1}
38 25
 	client := &http.Client{
39
-		Jar:     nil,
40
-		Timeout: ifconfigTimeout,
26
+		Jar: nil,
41 27
 		Transport: &http.Transport{
42 28
 			DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
43 29
 				return dialer.DialContext(ctx, network, addr)
@@ -47,16 +33,13 @@ func fetchIP(network string) (net.IP, error) {
47 33
 
48 34
 	resp, err := client.Get(ifconfigAddress)
49 35
 	if err != nil {
50
-		if resp != nil {
51
-			io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
52
-		}
53
-		return nil, errors.Annotate(err, "Cannot perform a request")
36
+		return nil, err
54 37
 	}
55 38
 	defer resp.Body.Close() // nolint: errcheck
56 39
 
57 40
 	respDataBytes, err := ioutil.ReadAll(resp.Body)
58 41
 	if err != nil {
59
-		return nil, errors.Annotate(err, "Cannot read response body")
42
+		return nil, err
60 43
 	}
61 44
 	respData := strings.TrimSpace(string(respDataBytes))
62 45
 

newconfig/urls.go → _config/urls.go 파일 보기

@@ -1,42 +1,15 @@
1
-package newconfig
1
+package config
2 2
 
3 3
 import (
4
-	"encoding/hex"
4
+	"net"
5 5
 	"net/url"
6
+	"strconv"
6 7
 )
7 8
 
8
-type URLs struct {
9
-	TG        string `json:"tg_url"`
10
-	TMe       string `json:"tme_url"`
11
-	TGQRCode  string `json:"tg_qrcode"`
12
-	TMeQRCode string `json:"tme_qrcode"`
13
-}
14
-
15
-type IPURLs struct {
16
-	IPv4      URLs   `json:"ipv4"`
17
-	IPv6      URLs   `json:"ipv6"`
18
-	BotSecret string `json:"secret_for_mtproxybot"`
19
-}
20
-
21
-func GetURLs() (urls IPURLs) {
22
-	secret := ""
23
-	switch C.SecretMode {
24
-	case SecretModeSimple:
25
-		secret = hex.EncodeToString(C.Secret)
26
-	case SecretModeSecured:
27
-		secret = "dd" + hex.EncodeToString(C.Secret)
28
-	}
29
-
30
-	urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)
31
-	urls.IPv6 = makeURLs(&C.PublicIPv6Addr, secret)
32
-	urls.BotSecret = secret
33
-
34
-	return urls
35
-}
36
-
37
-func makeURLs(addr *Addr, secret string) (urls URLs) {
9
+func getURLs(addr net.IP, port uint16, secret string) (urls URLs) {
38 10
 	values := url.Values{}
39
-	values.Set("address", addr.String())
11
+	values.Set("server", addr.String())
12
+	values.Set("port", strconv.Itoa(int(port)))
40 13
 	values.Set("secret", secret)
41 14
 
42 15
 	urls.TG = makeTGURL(values)

mtproto/connection_options.go → _mtproto/connection_options.go 파일 보기


mtproto/rpc/handshake_request.go → _mtproto/rpc/handshake_request.go 파일 보기


mtproto/rpc/handshake_response.go → _mtproto/rpc/handshake_response.go 파일 보기


mtproto/rpc/nonce_request.go → _mtproto/rpc/nonce_request.go 파일 보기


mtproto/rpc/nonce_response.go → _mtproto/rpc/nonce_response.go 파일 보기


mtproto/rpc/proxy_flags.go → _mtproto/rpc/proxy_flags.go 파일 보기


mtproto/rpc/proxy_request.go → _mtproto/rpc/proxy_request.go 파일 보기


mtproto/rpc/rpc.go → _mtproto/rpc/rpc.go 파일 보기


+ 121
- 0
_obfuscated2/frame.go 파일 보기

@@ -0,0 +1,121 @@
1
+package obfuscated2
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/rand"
6
+	"encoding/binary"
7
+	"io"
8
+
9
+	"github.com/juju/errors"
10
+
11
+	"github.com/9seconds/mtg/mtproto"
12
+)
13
+
14
+// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
15
+const (
16
+	frameLenKey   = 32
17
+	frameLenIV    = 16
18
+	frameLenMagic = 4
19
+	frameLenDC    = 2
20
+
21
+	frameOffsetFirst = 8
22
+	frameOffsetKey   = frameOffsetFirst + frameLenKey
23
+	frameOffsetIV    = frameOffsetKey + frameLenIV
24
+	frameOffsetMagic = frameOffsetIV + frameLenMagic
25
+	frameOffsetDC    = frameOffsetMagic + frameLenDC
26
+
27
+	FrameLen = 64
28
+)
29
+
30
+// Frame represents handshake frame. Telegram sends 64 bytes of obfuscated2
31
+// initialization data first.
32
+// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
33
+type Frame []byte
34
+
35
+// Key returns AES encryption key.
36
+func (f Frame) Key() []byte {
37
+	return f[frameOffsetFirst:frameOffsetKey]
38
+}
39
+
40
+// IV returns AES encryption initialization vector
41
+func (f Frame) IV() []byte {
42
+	return f[frameOffsetKey:frameOffsetIV]
43
+}
44
+
45
+// Magic returns magic bytes from last 8 bytes of frame. Telegram checks
46
+// for values there. If after decryption magic is not as expected,
47
+// connection considered as failed.
48
+func (f Frame) Magic() []byte {
49
+	return f[frameOffsetIV:frameOffsetMagic]
50
+}
51
+
52
+// DC returns number of datacenter IP client wants to use.
53
+func (f Frame) DC() (n int16) {
54
+	buf := bytes.NewReader(f[frameOffsetMagic:frameOffsetDC])
55
+	if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
56
+		n = 1
57
+	}
58
+
59
+	return
60
+}
61
+
62
+// ConnectionType identifies connection type of the handshake frame.
63
+func (f Frame) ConnectionType() (mtproto.ConnectionType, error) {
64
+	return mtproto.ConnectionTagFromHandshake(f.Magic())
65
+}
66
+
67
+// Invert inverts frame for extracting encryption keys. Pkease check that link:
68
+// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
69
+func (f Frame) Invert() Frame {
70
+	reversed := make(Frame, FrameLen)
71
+	copy(reversed, f)
72
+
73
+	for i := 0; i < frameLenKey+frameLenIV; i++ {
74
+		reversed[frameOffsetFirst+i] = f[frameOffsetIV-1-i]
75
+	}
76
+
77
+	return reversed
78
+}
79
+
80
+// ExtractFrame extracts exact obfuscated2 handshake frame from given reader.
81
+func ExtractFrame(conn io.Reader) (Frame, error) {
82
+	frame := make(Frame, FrameLen)
83
+	buf := bytes.NewBuffer(frame)
84
+	buf.Reset()
85
+
86
+	if _, err := io.CopyN(buf, conn, FrameLen); err != nil {
87
+		return nil, errors.Annotate(err, "Cannot extract obfuscated header")
88
+	}
89
+	copy(frame, buf.Bytes())
90
+
91
+	return frame, nil
92
+}
93
+
94
+func generateFrame(connectionType mtproto.ConnectionType) Frame {
95
+	frame := make(Frame, FrameLen)
96
+
97
+	for {
98
+		if _, err := rand.Read(frame); err != nil {
99
+			continue
100
+		}
101
+		if frame[0] == 0xef {
102
+			continue
103
+		}
104
+
105
+		val := (uint32(frame[3]) << 24) | (uint32(frame[2]) << 16) | (uint32(frame[1]) << 8) | uint32(frame[0])
106
+		if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee {
107
+			continue
108
+		}
109
+
110
+		val = (uint32(frame[7]) << 24) | (uint32(frame[6]) << 16) | (uint32(frame[5]) << 8) | uint32(frame[4])
111
+		if val == 0x00000000 {
112
+			continue
113
+		}
114
+
115
+		// error has to be checked before calling this function
116
+		tag, _ := connectionType.Tag() // nolint: errcheck, gosec
117
+		copy(frame.Magic(), tag)
118
+
119
+		return frame
120
+	}
121
+}

obfuscated2/frame_test.go → _obfuscated2/frame_test.go 파일 보기


obfuscated2/obfuscated2.go → _obfuscated2/obfuscated2.go 파일 보기


obfuscated2/obfuscated2_test.go → _obfuscated2/obfuscated2_test.go 파일 보기


+ 178
- 0
_proxy/proxy.go 파일 보기

@@ -0,0 +1,178 @@
1
+package proxy
2
+
3
+import (
4
+	"context"
5
+	"io"
6
+	"net"
7
+	"sync"
8
+
9
+	"github.com/gofrs/uuid"
10
+	"github.com/juju/errors"
11
+	"go.uber.org/zap"
12
+
13
+	"github.com/9seconds/mtg/antireplay"
14
+	"github.com/9seconds/mtg/client"
15
+	"github.com/9seconds/mtg/config"
16
+	"github.com/9seconds/mtg/mtproto"
17
+	"github.com/9seconds/mtg/stats"
18
+	"github.com/9seconds/mtg/telegram"
19
+	"github.com/9seconds/mtg/wrappers"
20
+)
21
+
22
+// Proxy is a core of this program.
23
+type Proxy struct {
24
+	antiReplayCache antireplay.Cache
25
+	clientInit      client.Init
26
+	tg              telegram.Telegram
27
+	conf            *config.Config
28
+}
29
+
30
+// Serve runs TCP proxy server.
31
+func (p *Proxy) Serve() error {
32
+	lsock, err := net.Listen("tcp", p.conf.BindAddr())
33
+	if err != nil {
34
+		return errors.Annotate(err, "Cannot create listen socket")
35
+	}
36
+
37
+	for {
38
+		if conn, err := lsock.Accept(); err != nil {
39
+			zap.S().Errorw("Cannot allocate incoming connection", "error", err)
40
+		} else {
41
+			go p.accept(conn)
42
+		}
43
+	}
44
+}
45
+
46
+func (p *Proxy) accept(conn net.Conn) {
47
+	connID := uuid.Must(uuid.NewV4()).String()
48
+	log := zap.S().With("connection_id", connID).Named("main")
49
+	ctx, cancel := context.WithCancel(context.Background())
50
+
51
+	defer func() {
52
+		cancel()
53
+		conn.Close() // nolint: errcheck, gosec
54
+
55
+		if err := recover(); err != nil {
56
+			stats.NewCrash()
57
+			log.Errorw("Crash of accept handler", "error", err)
58
+		}
59
+	}()
60
+
61
+	log.Infow("Client connected", "addr", conn.RemoteAddr())
62
+
63
+	clientConn, opts, err := p.clientInit(ctx, cancel, conn, connID, p.antiReplayCache, p.conf)
64
+	if err != nil {
65
+		log.Errorw("Cannot initialize client connection", "error", err)
66
+		return
67
+	}
68
+	defer clientConn.(io.Closer).Close() // nolint: errcheck
69
+
70
+	if p.conf.SecureOnly && opts.ConnectionType != mtproto.ConnectionTypeSecure {
71
+		log.Errorw("Proxy supports only secure connections", "connection_type", opts.ConnectionType)
72
+		return
73
+	}
74
+
75
+	stats.ClientConnected(opts.ConnectionType, clientConn.RemoteAddr())
76
+	defer stats.ClientDisconnected(opts.ConnectionType, clientConn.RemoteAddr())
77
+
78
+	serverConn, err := p.getTelegramConn(ctx, cancel, opts, connID)
79
+	if err != nil {
80
+		log.Errorw("Cannot initialize server connection", "error", err)
81
+		return
82
+	}
83
+	defer serverConn.(io.Closer).Close() // nolint: errcheck
84
+
85
+	go func() {
86
+		<-ctx.Done()
87
+		serverConn.(io.Closer).Close() // nolint: gosec
88
+		clientConn.(io.Closer).Close() // nolint: gosec
89
+	}()
90
+
91
+	wait := &sync.WaitGroup{}
92
+	wait.Add(2)
93
+
94
+	if p.conf.UseMiddleProxy() {
95
+		clientPacket := clientConn.(wrappers.PacketReadWriteCloser)
96
+		serverPacket := serverConn.(wrappers.PacketReadWriteCloser)
97
+		go p.middlePipe(clientPacket, serverPacket, wait, &opts.ReadHacks)
98
+		p.middlePipe(serverPacket, clientPacket, wait, &opts.WriteHacks)
99
+	} else {
100
+		clientStream := clientConn.(wrappers.StreamReadWriteCloser)
101
+		serverStream := serverConn.(wrappers.StreamReadWriteCloser)
102
+		go p.directPipe(clientStream, serverStream, wait, p.conf.ReadBufferSize)
103
+		p.directPipe(serverStream, clientStream, wait, p.conf.WriteBufferSize)
104
+	}
105
+
106
+	wait.Wait()
107
+
108
+	log.Infow("Client disconnected", "addr", conn.RemoteAddr())
109
+}
110
+
111
+func (p *Proxy) getTelegramConn(ctx context.Context, cancel context.CancelFunc,
112
+	opts *mtproto.ConnectionOpts, connID string) (wrappers.Wrap, error) {
113
+	streamConn, err := p.tg.Dial(ctx, cancel, connID, opts)
114
+	if err != nil {
115
+		return nil, errors.Annotate(err, "Cannot dial to Telegram")
116
+	}
117
+
118
+	packetConn, err := p.tg.Init(opts, streamConn)
119
+	if err != nil {
120
+		return nil, errors.Annotate(err, "Cannot handshake telegram")
121
+	}
122
+
123
+	return packetConn, nil
124
+}
125
+
126
+func (p *Proxy) middlePipe(src wrappers.PacketReadCloser, dst io.Writer, wait *sync.WaitGroup, hacks *mtproto.Hacks) {
127
+	defer wait.Done()
128
+
129
+	for {
130
+		hacks.SimpleAck = false
131
+		hacks.QuickAck = false
132
+
133
+		packet, err := src.Read()
134
+		if err != nil {
135
+			src.Logger().Warnw("Cannot read packet", "error", err)
136
+			return
137
+		}
138
+		if _, err = dst.Write(packet); err != nil {
139
+			src.Logger().Warnw("Cannot write packet", "error", err)
140
+			return
141
+		}
142
+	}
143
+}
144
+
145
+func (p *Proxy) directPipe(src wrappers.StreamReadCloser, dst io.Writer, wait *sync.WaitGroup, bufferSize int) {
146
+	defer wait.Done()
147
+
148
+	buffer := make([]byte, bufferSize)
149
+	if _, err := io.CopyBuffer(dst, src, buffer); err != nil {
150
+		src.Logger().Warnw("Cannot pump sockets", "error", err)
151
+	}
152
+}
153
+
154
+// NewProxy returns new proxy instance.
155
+func NewProxy(conf *config.Config) (*Proxy, error) {
156
+	var clientInit client.Init
157
+	var tg telegram.Telegram
158
+
159
+	cache, err := antireplay.NewCache(conf)
160
+	if err != nil {
161
+		return nil, errors.Annotate(err, "Cannot make proxy")
162
+	}
163
+
164
+	if conf.UseMiddleProxy() {
165
+		clientInit = client.MiddleInit
166
+		tg = telegram.NewMiddleTelegram(conf)
167
+	} else {
168
+		clientInit = client.DirectInit
169
+		tg = telegram.NewDirectTelegram(conf)
170
+	}
171
+
172
+	return &Proxy{
173
+		antiReplayCache: cache,
174
+		conf:            conf,
175
+		clientInit:      clientInit,
176
+		tg:              tg,
177
+	}, nil
178
+}

stats/channels.go → _stats/channels.go 파일 보기


stats/init.go → _stats/init.go 파일 보기


stats/prometheus.go → _stats/prometheus.go 파일 보기


stats/server.go → _stats/server.go 파일 보기


+ 175
- 0
_stats/stats.go 파일 보기

@@ -0,0 +1,175 @@
1
+package stats
2
+
3
+import (
4
+	"encoding/json"
5
+	"fmt"
6
+	"strconv"
7
+	"time"
8
+
9
+	humanize "github.com/dustin/go-humanize"
10
+
11
+	"github.com/9seconds/mtg/config"
12
+	"github.com/9seconds/mtg/mtproto"
13
+)
14
+
15
+type uptime time.Time
16
+
17
+func (u uptime) MarshalJSON() ([]byte, error) {
18
+	duration := time.Since(time.Time(u))
19
+	value := map[string]string{
20
+		"seconds": strconv.Itoa(int(duration.Seconds())),
21
+		"human":   humanize.Time(time.Time(u)),
22
+	}
23
+
24
+	return json.Marshal(value)
25
+}
26
+
27
+type connectionType struct {
28
+	IPv6 uint32 `json:"ipv6"`
29
+	IPv4 uint32 `json:"ipv4"`
30
+}
31
+
32
+type baseConnections struct {
33
+	All          connectionType `json:"all"`
34
+	Abridged     connectionType `json:"abridged"`
35
+	Intermediate connectionType `json:"intermediate"`
36
+	Secure       connectionType `json:"secure"`
37
+}
38
+
39
+type connections struct {
40
+	baseConnections
41
+}
42
+
43
+func (c connections) MarshalJSON() ([]byte, error) {
44
+	c.All.IPv4 = c.Abridged.IPv4 + c.Intermediate.IPv4 + c.Secure.IPv4
45
+	c.All.IPv6 = c.Abridged.IPv6 + c.Intermediate.IPv6 + c.Secure.IPv6
46
+
47
+	return json.Marshal(c.baseConnections)
48
+}
49
+
50
+type traffic struct {
51
+	ingress uint64
52
+	egress  uint64
53
+}
54
+
55
+func (t *traffic) dumpValue(value uint64) map[string]interface{} {
56
+	return map[string]interface{}{
57
+		"bytes": value,
58
+		"human": humanize.Bytes(value),
59
+	}
60
+}
61
+
62
+func (t traffic) MarshalJSON() ([]byte, error) {
63
+	value := map[string]map[string]interface{}{
64
+		"ingress": t.dumpValue(t.ingress),
65
+		"egress":  t.dumpValue(t.egress),
66
+	}
67
+
68
+	return json.Marshal(value)
69
+}
70
+
71
+type speed struct {
72
+	ingress uint64
73
+	egress  uint64
74
+}
75
+
76
+func (s *speed) dumpValue(value uint64) map[string]interface{} {
77
+	return map[string]interface{}{
78
+		"bytes/s": value,
79
+		"human":   fmt.Sprintf("%s/s", humanize.Bytes(value)),
80
+	}
81
+}
82
+
83
+func (s speed) MarshalJSON() ([]byte, error) {
84
+	value := map[string]map[string]interface{}{
85
+		"ingress": s.dumpValue(s.ingress),
86
+		"egress":  s.dumpValue(s.egress),
87
+	}
88
+
89
+	return json.Marshal(value)
90
+}
91
+
92
+// Stats represents a statistics of the proxy.
93
+type Stats struct {
94
+	URLs        config.IPURLs `json:"urls"`
95
+	Connections connections   `json:"connections"`
96
+	Traffic     traffic       `json:"traffic"`
97
+	Speed       speed         `json:"speed"`
98
+	Uptime      uptime        `json:"uptime"`
99
+	Crashes     uint32        `json:"crashes"`
100
+
101
+	previousTraffic traffic
102
+}
103
+
104
+func (s *Stats) start() {
105
+	speedChan := time.Tick(time.Second)
106
+
107
+	for {
108
+		select {
109
+		case <-speedChan:
110
+			s.handleSpeed()
111
+		case event := <-trafficChan:
112
+			s.handleTraffic(event)
113
+		case event := <-connectionsChan:
114
+			s.handleConnection(event)
115
+		case getStatsChan := <-statsChan:
116
+			s.handleGetStats(getStatsChan)
117
+		case <-crashesChan:
118
+			s.handleCrash()
119
+		}
120
+	}
121
+}
122
+
123
+func (s *Stats) handleTraffic(evt trafficData) {
124
+	if evt.ingress {
125
+		s.Traffic.ingress += uint64(evt.traffic)
126
+	} else {
127
+		s.Traffic.egress += uint64(evt.traffic)
128
+	}
129
+}
130
+
131
+func (s *Stats) handleSpeed() {
132
+	s.Speed.ingress = s.Traffic.ingress - s.previousTraffic.ingress
133
+	s.Speed.egress = s.Traffic.egress - s.previousTraffic.egress
134
+	s.previousTraffic.ingress = s.Traffic.ingress
135
+	s.previousTraffic.egress = s.Traffic.egress
136
+}
137
+
138
+func (s *Stats) handleConnection(evt connectionData) {
139
+	var inc uint32 = 1
140
+	if !evt.connected {
141
+		inc = ^uint32(0)
142
+	}
143
+
144
+	var conn *connectionType
145
+	switch evt.connectionType {
146
+	case mtproto.ConnectionTypeAbridged:
147
+		conn = &s.Connections.Abridged
148
+	case mtproto.ConnectionTypeSecure:
149
+		conn = &s.Connections.Secure
150
+	default:
151
+		conn = &s.Connections.Intermediate
152
+	}
153
+
154
+	if evt.addr.IP.To4() != nil {
155
+		conn.IPv4 += inc
156
+	} else {
157
+		conn.IPv6 += inc
158
+	}
159
+}
160
+
161
+func (s *Stats) handleGetStats(getStatsChan chan<- Stats) {
162
+	getStatsChan <- *s
163
+}
164
+
165
+func (s *Stats) handleCrash() {
166
+	s.Crashes++
167
+}
168
+
169
+// NewStats creates a new instance of Stats structure.
170
+func NewStats(conf *config.Config) *Stats {
171
+	return &Stats{
172
+		URLs:   conf.GetURLs(),
173
+		Uptime: uptime(time.Now()),
174
+	}
175
+}

stats/statsd.go → _stats/statsd.go 파일 보기


telegram/dialer.go → _telegram/dialer.go 파일 보기


+ 79
- 0
_telegram/direct.go 파일 보기

@@ -0,0 +1,79 @@
1
+package telegram
2
+
3
+import (
4
+	"context"
5
+	"net"
6
+
7
+	"github.com/juju/errors"
8
+
9
+	"github.com/9seconds/mtg/config"
10
+	"github.com/9seconds/mtg/mtproto"
11
+	"github.com/9seconds/mtg/obfuscated2"
12
+	"github.com/9seconds/mtg/wrappers"
13
+)
14
+
15
+const (
16
+	directV4DefaultIdx = 1
17
+	directV6DefaultIdx = 1
18
+)
19
+
20
+var (
21
+	directV4Addresses = map[int16][]string{
22
+		0: {"149.154.175.50:443"},
23
+		1: {"149.154.167.51:443"},
24
+		2: {"149.154.175.100:443"},
25
+		3: {"149.154.167.91:443"},
26
+		4: {"149.154.171.5:443"},
27
+	}
28
+	directV6Addresses = map[int16][]string{
29
+		0: {"[2001:b28:f23d:f001::a]:443"},
30
+		1: {"[2001:67c:04e8:f002::a]:443"},
31
+		2: {"[2001:b28:f23d:f003::a]:443"},
32
+		3: {"[2001:67c:04e8:f004::a]:443"},
33
+		4: {"[2001:b28:f23f:f005::a]:443"},
34
+	}
35
+)
36
+
37
+type directTelegram struct {
38
+	baseTelegram
39
+}
40
+
41
+func (t *directTelegram) Dial(ctx context.Context, cancel context.CancelFunc,
42
+	connID string, connOpts *mtproto.ConnectionOpts) (wrappers.StreamReadWriteCloser, error) {
43
+	dc := connOpts.DC
44
+	if dc < 0 {
45
+		dc = -dc
46
+	} else if dc == 0 {
47
+		dc = 1
48
+	}
49
+
50
+	return t.baseTelegram.dial(ctx, cancel, dc-1, connID, connOpts.ConnectionProto)
51
+}
52
+
53
+func (t *directTelegram) Init(connOpts *mtproto.ConnectionOpts,
54
+	conn wrappers.StreamReadWriteCloser) (wrappers.Wrap, error) {
55
+	obfs2, frame := obfuscated2.MakeTelegramObfuscated2Frame(connOpts)
56
+
57
+	if _, err := conn.Write(frame); err != nil {
58
+		return nil, errors.Annotate(err, "Cannot write hadnshake frame")
59
+	}
60
+
61
+	return wrappers.NewStreamCipher(conn, obfs2.Encryptor, obfs2.Decryptor), nil
62
+}
63
+
64
+// NewDirectTelegram returns Telegram instance which connects directly
65
+// to Telegram bypassing middleproxies.
66
+func NewDirectTelegram(conf *config.Config) Telegram {
67
+	return &directTelegram{
68
+		baseTelegram: baseTelegram{
69
+			dialer: tgDialer{
70
+				Dialer: net.Dialer{Timeout: telegramDialTimeout},
71
+				conf:   conf,
72
+			},
73
+			v4DefaultIdx: directV4DefaultIdx,
74
+			v6DefaultIdx: directV6DefaultIdx,
75
+			v4Addresses:  directV4Addresses,
76
+			v6Addresses:  directV6Addresses,
77
+		},
78
+	}
79
+}

telegram/middle.go → _telegram/middle.go 파일 보기


telegram/middle_caller.go → _telegram/middle_caller.go 파일 보기


telegram/telegram.go → _telegram/telegram.go 파일 보기


utils/read_current_data.go → _utils/read_current_data.go 파일 보기


+ 15
- 0
_utils/reverse_bytes.go 파일 보기

@@ -0,0 +1,15 @@
1
+package utils
2
+
3
+// ReverseBytes is a common slice reverser.
4
+func ReverseBytes(data []byte) []byte {
5
+	dataLen := len(data)
6
+	rv := make([]byte, dataLen)
7
+
8
+	rv[dataLen/2] = data[dataLen/2]
9
+	for i := dataLen/2 - 1; i >= 0; i-- {
10
+		opp := dataLen - i - 1
11
+		rv[i], rv[opp] = data[opp], data[i]
12
+	}
13
+
14
+	return rv
15
+}

+ 15
- 0
_utils/uint24.go 파일 보기

@@ -0,0 +1,15 @@
1
+package utils
2
+
3
+// Uint24 is a replacement for the absent Go uint24 data type.
4
+// This data type is little endian.
5
+type Uint24 [3]byte
6
+
7
+// ToUint24 converts number to Uint24.
8
+func ToUint24(number uint32) Uint24 {
9
+	return Uint24{byte(number), byte(number >> 8), byte(number >> 16)}
10
+}
11
+
12
+// FromUint24 converts Uint24 to number.
13
+func FromUint24(number Uint24) uint32 {
14
+	return uint32(number[0]) + (uint32(number[1]) << 8) + (uint32(number[2]) << 16)
15
+}

wrappers/blockcipher.go → _wrappers/blockcipher.go 파일 보기


wrappers/conn.go → _wrappers/conn.go 파일 보기


wrappers/mtproto_abridged.go → _wrappers/mtproto_abridged.go 파일 보기


wrappers/mtproto_cipher.go → _wrappers/mtproto_cipher.go 파일 보기


wrappers/mtproto_frame.go → _wrappers/mtproto_frame.go 파일 보기


wrappers/mtproto_intermediate.go → _wrappers/mtproto_intermediate.go 파일 보기


wrappers/mtproto_intermediate_secure.go → _wrappers/mtproto_intermediate_secure.go 파일 보기


wrappers/mtproto_proxy.go → _wrappers/mtproto_proxy.go 파일 보기


wrappers/streamcipher.go → _wrappers/streamcipher.go 파일 보기


wrappers/streamcipher_pool.go → _wrappers/streamcipher_pool.go 파일 보기


wrappers/wrap.go → _wrappers/wrap.go 파일 보기


+ 11
- 18
antireplay/cache.go 파일 보기

@@ -2,36 +2,29 @@ package antireplay
2 2
 
3 3
 import (
4 4
 	"github.com/allegro/bigcache"
5
-	"github.com/juju/errors"
6 5
 
7 6
 	"github.com/9seconds/mtg/config"
8 7
 )
9 8
 
10
-// Cache defines storage for obfuscated2 handshake frames.
11
-type Cache struct {
12
-	cache *bigcache.BigCache
13
-}
9
+var cache *bigcache.BigCache
14 10
 
15
-func (a Cache) Add(frame []byte) {
16
-	a.cache.Set(string(frame), nil) // nolint: errcheck
11
+func Add(data []byte) {
12
+	cache.Set(string(data), nil) // nolint: errcheck
17 13
 }
18 14
 
19
-func (a Cache) Has(frame []byte) bool {
20
-	_, err := a.cache.Get(string(frame))
21
-
15
+func Has(data []byte) bool {
16
+	_, err := cache.Get(string(data))
22 17
 	return err == nil
23 18
 }
24 19
 
25
-func NewCache(config *config.Config) (Cache, error) {
26
-	cache, err := bigcache.NewBigCache(bigcache.Config{
20
+func Init() error {
21
+	c, err := bigcache.NewBigCache(bigcache.Config{
27 22
 		Shards:           1024,
28
-		LifeWindow:       config.AntiReplayEvictionTime,
23
+		LifeWindow:       config.C.AntiReplay.EvictionTime,
29 24
 		Hasher:           hasher{},
30
-		HardMaxCacheSize: config.AntiReplayMaxSize,
25
+		HardMaxCacheSize: config.C.AntiReplay.MaxSize,
31 26
 	})
32
-	if err != nil {
33
-		return Cache{}, errors.Annotate(err, "Cannot make cache")
34
-	}
27
+	cache = c
35 28
 
36
-	return Cache{cache}, nil
29
+	return err
37 30
 }

newcli/generate.go → cli/generate.go 파일 보기

@@ -1,14 +1,14 @@
1
-package newcli
1
+package cli
2 2
 
3 3
 import (
4 4
 	"crypto/rand"
5 5
 	"encoding/hex"
6 6
 
7
-	"github.com/9seconds/mtg/newconfig"
7
+	"github.com/9seconds/mtg/config"
8 8
 )
9 9
 
10 10
 func Generate(secretType string) {
11
-	data := make([]byte, newconfig.SimpleSecretLength)
11
+	data := make([]byte, config.SimpleSecretLength)
12 12
 	if _, err := rand.Read(data); err != nil {
13 13
 		panic(err)
14 14
 	}

+ 86
- 0
cli/proxy.go 파일 보기

@@ -0,0 +1,86 @@
1
+package cli
2
+
3
+import (
4
+	"net"
5
+	"os"
6
+	"time"
7
+
8
+	"go.uber.org/zap"
9
+	"go.uber.org/zap/zapcore"
10
+
11
+	"github.com/9seconds/mtg/antireplay"
12
+	"github.com/9seconds/mtg/config"
13
+	"github.com/9seconds/mtg/ntp"
14
+	"github.com/9seconds/mtg/obfuscated2"
15
+	"github.com/9seconds/mtg/proxy"
16
+	"github.com/9seconds/mtg/stats"
17
+	"github.com/9seconds/mtg/telegram"
18
+)
19
+
20
+func Proxy() error {
21
+	atom := zap.NewAtomicLevel()
22
+	switch {
23
+	case config.C.Debug:
24
+		atom.SetLevel(zapcore.DebugLevel)
25
+	case config.C.Verbose:
26
+		atom.SetLevel(zapcore.InfoLevel)
27
+	default:
28
+		atom.SetLevel(zapcore.ErrorLevel)
29
+	}
30
+
31
+	encoderCfg := zap.NewProductionEncoderConfig()
32
+	logger := zap.New(zapcore.NewCore(
33
+		zapcore.NewJSONEncoder(encoderCfg),
34
+		zapcore.Lock(os.Stderr),
35
+		atom,
36
+	))
37
+	zap.ReplaceGlobals(logger)
38
+	defer logger.Sync() // nolint: errcheck
39
+
40
+	if err := config.InitPublicAddress(); err != nil {
41
+		Fatal(err.Error())
42
+	}
43
+	zap.S().Debugw("Configuration", "config", config.C)
44
+
45
+	if len(config.C.AdTag) > 0 {
46
+		zap.S().Infow("Use middle proxy connection to Telegram")
47
+		diff, err := ntp.Fetch()
48
+		if err != nil {
49
+			Fatal("Cannot fetch time data from NTP")
50
+		}
51
+		if diff > time.Second {
52
+			Fatal("Your local time is skewed and drift is bigger than a second. Please sync your time.")
53
+		}
54
+		go ntp.AutoUpdate()
55
+	} else {
56
+		zap.S().Infow("Use direct connection to Telegram")
57
+	}
58
+
59
+	PrintJSONStdout(config.GetURLs())
60
+
61
+	if err := antireplay.Init(); err != nil {
62
+		Fatal(err.Error())
63
+	}
64
+	if err := stats.Init(); err != nil {
65
+		Fatal(err.Error())
66
+	}
67
+	proxyListener, err := net.Listen("tcp", config.C.ListenAddr.String())
68
+	if err != nil {
69
+		Fatal(err.Error())
70
+	}
71
+
72
+	app := &proxy.Proxy{
73
+		Logger: zap.S().Named("proxy"),
74
+	}
75
+	if len(config.C.AdTag) == 0 {
76
+		app.TelegramProtocolMaker = obfuscated2.MakeTelegramProtocol
77
+		app.TelegramDialer = telegram.NewDirectTelegram()
78
+	}
79
+	if config.C.SecretMode != config.SecretModeTLS {
80
+		app.ClientProtocolMaker = obfuscated2.MakeClientProtocol
81
+	}
82
+
83
+	app.Serve(proxyListener)
84
+
85
+	return nil
86
+}

newcli/utils.go → cli/utils.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newcli
1
+package cli
2 2
 
3 3
 import (
4 4
 	"encoding/json"

+ 208
- 174
config/config.go 파일 보기

@@ -2,223 +2,257 @@ package config
2 2
 
3 3
 import (
4 4
 	"bytes"
5
-	"encoding/hex"
6
-	"fmt"
5
+	"encoding/json"
7 6
 	"net"
8 7
 	"strconv"
9 8
 	"time"
10 9
 
11 10
 	"github.com/juju/errors"
11
+	"go.uber.org/zap"
12 12
 	statsd "gopkg.in/alexcesaro/statsd.v2"
13 13
 )
14 14
 
15
-// Config represents common configuration of mtg.
16
-type Config struct {
17
-	Debug      bool
18
-	Verbose    bool
19
-	SecureMode bool
20
-	SecureOnly bool
21
-
22
-	ReadBufferSize  int
23
-	WriteBufferSize int
24
-
25
-	BindPort       uint16
26
-	PublicIPv4Port uint16
27
-	PublicIPv6Port uint16
28
-	StatsPort      uint16
29
-
30
-	BindIP     net.IP
31
-	PublicIPv4 net.IP
32
-	PublicIPv6 net.IP
33
-	StatsIP    net.IP
34
-
35
-	AntiReplayMaxSize      int
36
-	AntiReplayEvictionTime time.Duration
37
-
38
-	StatsD struct {
39
-		Addr       net.Addr
40
-		Prefix     string
41
-		Tags       map[string]string
42
-		TagsFormat statsd.TagFormat
43
-		Enabled    bool
44
-	}
45
-	Prometheus struct {
46
-		Prefix string
47
-	}
15
+type SecretMode uint8
48 16
 
49
-	Secret []byte
50
-	AdTag  []byte
17
+func (s SecretMode) String() string {
18
+	switch s {
19
+	case SecretModeSimple:
20
+		return "simple"
21
+	case SecretModeSecured:
22
+		return "secured"
23
+	}
24
+	return "tls"
51 25
 }
52 26
 
53
-// URLs contains links to the proxy (tg://, t.me) and their QR codes.
54
-type URLs struct {
55
-	TG        string `json:"tg_url"`
56
-	TMe       string `json:"tme_url"`
57
-	TGQRCode  string `json:"tg_qrcode"`
58
-	TMeQRCode string `json:"tme_qrcode"`
27
+const (
28
+	SecretModeSimple SecretMode = iota
29
+	SecretModeSecured
30
+	SecretModeTLS
31
+)
32
+
33
+const SimpleSecretLength = 16
34
+
35
+type OptionType uint8
36
+
37
+const (
38
+	OptionTypeDebug OptionType = iota
39
+	OptionTypeVerbose
40
+
41
+	OptionTypeBindIP
42
+	OptionTypeBindPort
43
+	OptionTypePublicIPv4
44
+	OptionTypePublicIPv4Port
45
+	OptionTypePublicIPv6
46
+	OptionTypePublicIPv6Port
47
+	OptionTypeStatsIP
48
+	OptionTypeStatsPort
49
+
50
+	OptionTypeStatsdIP
51
+	OptionTypeStatsdPort
52
+	OptionTypeStatsdNetwork
53
+	OptionTypeStatsdPrefix
54
+	OptionTypeStatsdTagsFormat
55
+	OptionTypeStatsdTags
56
+	OptionTypePrometheusPrefix
57
+
58
+	OptionTypeWriteBufferSize
59
+	OptionTypeReadBufferSize
60
+
61
+	OptionTypeAntiReplayMaxSize
62
+	OptionTypeAntiReplayEvictionTime
63
+
64
+	OptionTypeSecret
65
+	OptionTypeAdtag
66
+)
67
+
68
+type BufferSize struct {
69
+	Read  int `json:"read"`
70
+	Write int `json:"write"`
59 71
 }
60 72
 
61
-// IPURLs contains links to both ipv4 and ipv6 of the proxy.
62
-type IPURLs struct {
63
-	IPv4      URLs   `json:"ipv4"`
64
-	IPv6      URLs   `json:"ipv6"`
65
-	BotSecret string `json:"secret_for_mtproxybot"`
73
+type AntiReplay struct {
74
+	MaxSize      int           `json:"max_size"`
75
+	EvictionTime time.Duration `json:"duration"`
66 76
 }
67 77
 
68
-// BindAddr returns connection for this server to bind to.
69
-func (c *Config) BindAddr() string {
70
-	return getAddr(c.BindIP, c.BindPort)
78
+type Stats struct {
79
+	Prefix string `json:"prefix"`
71 80
 }
72 81
 
73
-// StatAddr returns connection string to the stats API.
74
-func (c *Config) StatAddr() string {
75
-	return getAddr(c.StatsIP, c.StatsPort)
82
+type StatsdStats struct {
83
+	Stats
84
+
85
+	Addr       Addr              `json:"addr"`
86
+	Tags       map[string]string `json:"tags"`
87
+	TagsFormat statsd.TagFormat  `json:"format"`
76 88
 }
77 89
 
78
-// UseMiddleProxy defines if this proxy has to connect middle proxies
79
-// which supports promoted channels or directly access Telegram.
80
-func (c *Config) UseMiddleProxy() bool {
81
-	return len(c.AdTag) > 0
90
+type PrometheusStats struct {
91
+	Stats
82 92
 }
83 93
 
84
-// BotSecretString returns secret string which should work with MTProxybot.
85
-func (c *Config) BotSecretString() string {
86
-	return hex.EncodeToString(c.Secret)
94
+type Addr struct {
95
+	IP   net.IP `json:"ip"`
96
+	Port int    `json:"port"`
97
+	net  string
87 98
 }
88 99
 
89
-// SecretString returns a secret in a form entered on the start of the
90
-// application.
91
-func (c *Config) SecretString() string {
92
-	secret := c.BotSecretString()
93
-	if c.SecureMode {
94
-		return "dd" + secret
100
+func (a Addr) Network() string {
101
+	if a.net == "" {
102
+		return "tcp"
95 103
 	}
96
-	return secret
104
+	return a.net
97 105
 }
98 106
 
99
-// GetURLs returns configured IPURLs instance with links to this server.
100
-func (c *Config) GetURLs() IPURLs {
101
-	urls := IPURLs{}
102
-	secret := c.SecretString()
103
-	if c.PublicIPv4 != nil {
104
-		urls.IPv4 = getURLs(c.PublicIPv4, c.PublicIPv4Port, secret)
105
-	}
106
-	if c.PublicIPv6 != nil {
107
-		urls.IPv6 = getURLs(c.PublicIPv6, c.PublicIPv6Port, secret)
107
+func (a Addr) String() string {
108
+	return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
109
+}
110
+
111
+func (a Addr) MarshalJSON() ([]byte, error) {
112
+	data := map[string]string{
113
+		"network": a.Network(),
114
+		"addr":    a.String(),
108 115
 	}
109
-	urls.BotSecret = c.BotSecretString()
116
+	return json.Marshal(data)
117
+}
118
+
119
+type Config struct {
120
+	BufferSize BufferSize `json:"buffer_size"`
121
+	AntiReplay AntiReplay `json:"anti_replay"`
110 122
 
111
-	return urls
123
+	ListenAddr     Addr `json:"listen_addr"`
124
+	PublicIPv4Addr Addr `json:"public_ipv4_addr"`
125
+	PublicIPv6Addr Addr `json:"public_ipv6_addr"`
126
+	StatsAddr      Addr `json:"stats_addr"`
127
+
128
+	StatsdStats     StatsdStats     `json:"stats_statsd"`
129
+	PrometheusStats PrometheusStats `json:"stats_prometheus"`
130
+
131
+	Debug      bool       `json:"debug"`
132
+	Verbose    bool       `json:"verbose"`
133
+	SecretMode SecretMode `json:"secret_mode"`
134
+	Secret     []byte     `json:"secret"`
135
+	AdTag      []byte     `json:"adtag"`
112 136
 }
113 137
 
114
-func getAddr(host fmt.Stringer, port uint16) string {
115
-	return net.JoinHostPort(host.String(), strconv.Itoa(int(port)))
138
+func (c Config) String() string {
139
+	data, _ := json.Marshal(c)
140
+	return string(data)
116 141
 }
117 142
 
118
-// NewConfig returns new configuration. If required, it manages and
119
-// fetches data from external sources. Parameters passed to this
120
-// function, should come from command line arguments.
121
-func NewConfig(debug, verbose bool, // nolint: gocyclo
122
-	writeBufferSize, readBufferSize uint32,
123
-	bindIP, publicIPv4, publicIPv6, statsIP net.IP,
124
-	bindPort, publicIPv4Port, publicIPv6Port, statsPort, statsdPort uint16,
125
-	statsdIP, statsdNetwork, statsdPrefix, statsdTagsFormat string,
126
-	statsdTags map[string]string, prometheusPrefix string,
127
-	secureOnly bool,
128
-	antiReplayMaxSize int, antiReplayEvictionTime time.Duration,
129
-	secret, adtag []byte) (*Config, error) {
130
-	secureMode := secureOnly
131
-	if bytes.HasPrefix(secret, []byte{0xdd}) && len(secret) == 17 {
132
-		secureMode = true
133
-		secret = bytes.TrimPrefix(secret, []byte{0xdd})
134
-	} else if len(secret) != 16 {
135
-		return nil, errors.New("Telegram demands secret of length 32")
136
-	}
143
+type ConfigOpt struct {
144
+	Option OptionType
145
+	Value  interface{}
146
+}
137 147
 
138
-	var err error
139
-	if publicIPv4 == nil {
140
-		publicIPv4, err = getGlobalIPv4()
141
-		if err != nil {
142
-			publicIPv4 = nil
143
-		} else if publicIPv4.To4() == nil {
144
-			return nil, errors.Errorf("IP %s is not IPv4", publicIPv4.String())
145
-		}
146
-	}
147
-	if publicIPv4Port == 0 {
148
-		publicIPv4Port = bindPort
149
-	}
148
+var C = Config{}
150 149
 
151
-	if publicIPv6 == nil {
152
-		publicIPv6, err = getGlobalIPv6()
153
-		if err != nil {
154
-			publicIPv6 = nil
155
-		} else if publicIPv6.To4() != nil {
156
-			return nil, errors.Errorf("IP %s is not IPv6", publicIPv6.String())
150
+func Init(options ...ConfigOpt) error { // nolint: gocyclo
151
+	for _, opt := range options {
152
+		switch opt.Option {
153
+		case OptionTypeDebug:
154
+			C.Debug = opt.Value.(bool)
155
+		case OptionTypeVerbose:
156
+			C.Verbose = opt.Value.(bool)
157
+		case OptionTypeBindIP:
158
+			C.ListenAddr.IP = opt.Value.(net.IP)
159
+		case OptionTypeBindPort:
160
+			C.ListenAddr.Port = int(opt.Value.(uint16))
161
+		case OptionTypePublicIPv4:
162
+			C.PublicIPv4Addr.IP = opt.Value.(net.IP)
163
+		case OptionTypePublicIPv4Port:
164
+			C.PublicIPv4Addr.Port = int(opt.Value.(uint16))
165
+		case OptionTypePublicIPv6:
166
+			C.PublicIPv6Addr.IP = opt.Value.(net.IP)
167
+		case OptionTypePublicIPv6Port:
168
+			C.PublicIPv6Addr.Port = int(opt.Value.(uint16))
169
+		case OptionTypeStatsIP:
170
+			C.StatsAddr.IP = opt.Value.(net.IP)
171
+		case OptionTypeStatsPort:
172
+			C.StatsAddr.Port = int(opt.Value.(uint16))
173
+		case OptionTypeStatsdIP:
174
+			C.StatsdStats.Addr.IP = opt.Value.(net.IP)
175
+		case OptionTypeStatsdPort:
176
+			C.StatsdStats.Addr.Port = int(opt.Value.(uint16))
177
+		case OptionTypeStatsdNetwork:
178
+			C.StatsdStats.Addr.net = opt.Value.(string)
179
+		case OptionTypeStatsdPrefix:
180
+			C.StatsdStats.Prefix = opt.Value.(string)
181
+		case OptionTypeStatsdTagsFormat:
182
+			value := opt.Value.(string)
183
+			switch value {
184
+			case "datadog":
185
+				C.StatsdStats.TagsFormat = statsd.Datadog
186
+			case "influxdb":
187
+				C.StatsdStats.TagsFormat = statsd.InfluxDB
188
+			default:
189
+				return errors.Errorf("Incorrect statsd tag %s", value)
190
+			}
191
+		case OptionTypeStatsdTags:
192
+			C.StatsdStats.Tags = opt.Value.(map[string]string)
193
+		case OptionTypePrometheusPrefix:
194
+			C.PrometheusStats.Prefix = opt.Value.(string)
195
+		case OptionTypeWriteBufferSize:
196
+			C.BufferSize.Write = int(opt.Value.(uint32))
197
+		case OptionTypeReadBufferSize:
198
+			C.BufferSize.Read = int(opt.Value.(uint32))
199
+		case OptionTypeAntiReplayMaxSize:
200
+			C.AntiReplay.MaxSize = opt.Value.(int)
201
+		case OptionTypeAntiReplayEvictionTime:
202
+			C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
203
+		case OptionTypeSecret:
204
+			C.Secret = opt.Value.([]byte)
205
+		case OptionTypeAdtag:
206
+			C.AdTag = opt.Value.([]byte)
207
+		default:
208
+			return errors.Errorf("Unknown tag %v", opt.Option)
157 209
 		}
158 210
 	}
159
-	if publicIPv6Port == 0 {
160
-		publicIPv6Port = bindPort
161
-	}
162 211
 
163
-	if statsIP == nil {
164
-		statsIP = publicIPv4
212
+	switch {
213
+	case len(C.Secret) == 1+SimpleSecretLength && bytes.HasPrefix(C.Secret, []byte{0xdd}):
214
+		C.SecretMode = SecretModeSecured
215
+		C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
216
+	case len(C.Secret) == SimpleSecretLength:
217
+		C.SecretMode = SecretModeSimple
218
+	default:
219
+		return errors.New("Incorrect secret")
165 220
 	}
166 221
 
167
-	conf := &Config{
168
-		Debug:                  debug,
169
-		Verbose:                verbose,
170
-		SecureOnly:             secureOnly,
171
-		BindIP:                 bindIP,
172
-		BindPort:               bindPort,
173
-		PublicIPv4:             publicIPv4,
174
-		PublicIPv4Port:         publicIPv4Port,
175
-		PublicIPv6:             publicIPv6,
176
-		PublicIPv6Port:         publicIPv6Port,
177
-		StatsIP:                statsIP,
178
-		StatsPort:              statsPort,
179
-		Secret:                 secret,
180
-		AdTag:                  adtag,
181
-		SecureMode:             secureMode,
182
-		ReadBufferSize:         int(readBufferSize),
183
-		WriteBufferSize:        int(writeBufferSize),
184
-		AntiReplayMaxSize:      antiReplayMaxSize,
185
-		AntiReplayEvictionTime: antiReplayEvictionTime,
222
+	return nil
223
+}
224
+
225
+func InitPublicAddress() error {
226
+	if C.PublicIPv4Addr.Port == 0 {
227
+		C.PublicIPv4Addr.Port = C.ListenAddr.Port
186 228
 	}
187
-	conf.Prometheus.Prefix = prometheusPrefix
188
-
189
-	if statsdIP != "" {
190
-		conf.StatsD.Enabled = true
191
-		conf.StatsD.Prefix = statsdPrefix
192
-		conf.StatsD.Tags = statsdTags
193
-
194
-		var (
195
-			addr net.Addr
196
-			err  error
197
-		)
198
-		hostPort := net.JoinHostPort(statsdIP, strconv.Itoa(int(statsdPort)))
199
-		switch statsdNetwork {
200
-		case "tcp":
201
-			addr, err = net.ResolveTCPAddr("tcp", hostPort)
202
-		case "udp":
203
-			addr, err = net.ResolveUDPAddr("udp", hostPort)
204
-		default:
205
-			err = errors.Errorf("Unknown network %s", statsdNetwork)
206
-		}
229
+	if C.PublicIPv6Addr.Port == 0 {
230
+		C.PublicIPv6Addr.Port = C.ListenAddr.Port
231
+	}
232
+
233
+	foundAddress := C.PublicIPv4Addr.IP != nil || C.PublicIPv6Addr.IP != nil
234
+	if C.PublicIPv4Addr.IP == nil {
235
+		ip, err := getGlobalIPv4()
207 236
 		if err != nil {
208
-			return nil, errors.Annotate(err, "Cannot resolve statsd address")
237
+			zap.S().Warnw("Cannot resolve public address", "error", err)
238
+		} else {
239
+			C.PublicIPv4Addr.IP = ip
240
+			foundAddress = true
209 241
 		}
210
-		conf.StatsD.Addr = addr
211
-
212
-		switch statsdTagsFormat {
213
-		case "datadog":
214
-			conf.StatsD.TagsFormat = statsd.Datadog
215
-		case "influxdb":
216
-			conf.StatsD.TagsFormat = statsd.InfluxDB
217
-		case "":
218
-		default:
219
-			return nil, errors.Errorf("Unknown tags format %s", statsdTagsFormat)
242
+	}
243
+	if C.PublicIPv6Addr.IP == nil {
244
+		ip, err := getGlobalIPv6()
245
+		if err != nil {
246
+			zap.S().Warnw("Cannot resolve public address", "error", err)
247
+		} else {
248
+			C.PublicIPv6Addr.IP = ip
249
+			foundAddress = true
220 250
 		}
221 251
 	}
222 252
 
223
-	return conf, nil
253
+	if !foundAddress {
254
+		return errors.New("Cannot resolve any public address")
255
+	}
256
+
257
+	return nil
224 258
 }

+ 23
- 6
config/global_ips.go 파일 보기

@@ -2,28 +2,42 @@ package config
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"io"
5 6
 	"io/ioutil"
6 7
 	"net"
7 8
 	"net/http"
8 9
 	"strings"
10
+	"time"
9 11
 
10 12
 	"github.com/juju/errors"
11 13
 )
12 14
 
13
-const ifconfigAddress = "https://ifconfig.co/ip"
15
+const (
16
+	ifconfigAddress = "https://ifconfig.co/ip"
17
+	ifconfigTimeout = 10 * time.Second
18
+)
14 19
 
15 20
 func getGlobalIPv4() (net.IP, error) {
16
-	return fetchIP("tcp4")
21
+	ip, err := fetchIP("tcp4")
22
+	if err != nil || ip.To4() == nil {
23
+		return nil, errors.Annotate(err, "Cannot find public ipv4 address")
24
+	}
25
+	return ip, nil
17 26
 }
18 27
 
19 28
 func getGlobalIPv6() (net.IP, error) {
20
-	return fetchIP("tcp6")
29
+	ip, err := fetchIP("tcp6")
30
+	if err != nil || ip.To4() != nil {
31
+		return nil, errors.Annotate(err, "Cannot find public ipv6 address")
32
+	}
33
+	return ip, nil
21 34
 }
22 35
 
23 36
 func fetchIP(network string) (net.IP, error) {
24 37
 	dialer := &net.Dialer{FallbackDelay: -1}
25 38
 	client := &http.Client{
26
-		Jar: nil,
39
+		Jar:     nil,
40
+		Timeout: ifconfigTimeout,
27 41
 		Transport: &http.Transport{
28 42
 			DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
29 43
 				return dialer.DialContext(ctx, network, addr)
@@ -33,13 +47,16 @@ func fetchIP(network string) (net.IP, error) {
33 47
 
34 48
 	resp, err := client.Get(ifconfigAddress)
35 49
 	if err != nil {
36
-		return nil, err
50
+		if resp != nil {
51
+			io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
52
+		}
53
+		return nil, errors.Annotate(err, "Cannot perform a request")
37 54
 	}
38 55
 	defer resp.Body.Close() // nolint: errcheck
39 56
 
40 57
 	respDataBytes, err := ioutil.ReadAll(resp.Body)
41 58
 	if err != nil {
42
-		return nil, err
59
+		return nil, errors.Annotate(err, "Cannot read response body")
43 60
 	}
44 61
 	respData := strings.TrimSpace(string(respDataBytes))
45 62
 

+ 32
- 5
config/urls.go 파일 보기

@@ -1,15 +1,42 @@
1 1
 package config
2 2
 
3 3
 import (
4
-	"net"
4
+	"encoding/hex"
5 5
 	"net/url"
6
-	"strconv"
7 6
 )
8 7
 
9
-func getURLs(addr net.IP, port uint16, secret string) (urls URLs) {
8
+type URLs struct {
9
+	TG        string `json:"tg_url"`
10
+	TMe       string `json:"tme_url"`
11
+	TGQRCode  string `json:"tg_qrcode"`
12
+	TMeQRCode string `json:"tme_qrcode"`
13
+}
14
+
15
+type IPURLs struct {
16
+	IPv4      URLs   `json:"ipv4"`
17
+	IPv6      URLs   `json:"ipv6"`
18
+	BotSecret string `json:"secret_for_mtproxybot"`
19
+}
20
+
21
+func GetURLs() (urls IPURLs) {
22
+	secret := ""
23
+	switch C.SecretMode {
24
+	case SecretModeSimple:
25
+		secret = hex.EncodeToString(C.Secret)
26
+	case SecretModeSecured:
27
+		secret = "dd" + hex.EncodeToString(C.Secret)
28
+	}
29
+
30
+	urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)
31
+	urls.IPv6 = makeURLs(&C.PublicIPv6Addr, secret)
32
+	urls.BotSecret = secret
33
+
34
+	return urls
35
+}
36
+
37
+func makeURLs(addr *Addr, secret string) (urls URLs) {
10 38
 	values := url.Values{}
11
-	values.Set("server", addr.String())
12
-	values.Set("port", strconv.Itoa(int(port)))
39
+	values.Set("address", addr.String())
13 40
 	values.Set("secret", secret)
14 41
 
15 42
 	urls.TG = makeTGURL(values)

+ 5
- 0
conntypes/dc.go 파일 보기

@@ -0,0 +1,5 @@
1
+package conntypes
2
+
3
+type DC int16
4
+
5
+const DCDefaultIdx DC = 1

newprotocol/connection_protocol.go → conntypes/protocol.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newprotocol
1
+package conntypes
2 2
 
3 3
 type ConnectionProtocol uint8
4 4
 

newprotocol/connection_type.go → conntypes/type.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newprotocol
1
+package conntypes
2 2
 
3 3
 type ConnectionType uint8
4 4
 

+ 31
- 31
main.go 파일 보기

@@ -9,8 +9,8 @@ import (
9 9
 	"github.com/juju/errors"
10 10
 	kingpin "gopkg.in/alecthomas/kingpin.v2"
11 11
 
12
-	"github.com/9seconds/mtg/newcli"
13
-	"github.com/9seconds/mtg/newconfig"
12
+	"github.com/9seconds/mtg/cli"
13
+	"github.com/9seconds/mtg/config"
14 14
 )
15 15
 
16 16
 var version = "dev" // this has to be set by build ld flags
@@ -144,45 +144,45 @@ func main() {
144 144
 	app.HelpFlag.Short('h')
145 145
 
146 146
 	if err := setRLimit(); err != nil {
147
-		newcli.Fatal(err.Error())
147
+		cli.Fatal(err.Error())
148 148
 	}
149 149
 
150 150
 	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
151 151
 	case generateSecretCommand.FullCommand():
152
-		newcli.Generate(*generateSecretType)
152
+		cli.Generate(*generateSecretType)
153 153
 
154 154
 	case proxyCommand.FullCommand():
155
-		err := newconfig.Init(
156
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeDebug, Value: *proxyDebug},
157
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeVerbose, Value: *proxyVerbose},
158
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeBindIP, Value: *proxyBindIP},
159
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeBindPort, Value: *proxyBindPort},
160
-			newconfig.ConfigOpt{Option: newconfig.OptionTypePublicIPv4, Value: *proxyPublicIPv4},
161
-			newconfig.ConfigOpt{Option: newconfig.OptionTypePublicIPv4Port, Value: *proxyPublicIPv4Port},
162
-			newconfig.ConfigOpt{Option: newconfig.OptionTypePublicIPv6, Value: *proxyPublicIPv6},
163
-			newconfig.ConfigOpt{Option: newconfig.OptionTypePublicIPv6Port, Value: *proxyPublicIPv6Port},
164
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsIP, Value: *proxyStatsIP},
165
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsPort, Value: *proxyStatsPort},
166
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdIP, Value: *proxyStatsdIP},
167
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdPort, Value: *proxyStatsdPort},
168
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdNetwork, Value: *proxyStatsdNetwork},
169
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdPrefix, Value: *proxyStatsdPrefix},
170
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdTagsFormat, Value: *proxyStatsdTagsFormat},
171
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdTags, Value: *proxyStatsdTags},
172
-			newconfig.ConfigOpt{Option: newconfig.OptionTypePrometheusPrefix, Value: *proxyPrometheusPrefix},
173
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeWriteBufferSize, Value: *proxyWriteBufferSize},
174
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeReadBufferSize, Value: *proxyReadBufferSize},
175
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeAntiReplayMaxSize, Value: *proxyAntiReplayMaxSize},
176
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeAntiReplayEvictionTime, Value: *proxyAntiReplayEvictionTime},
177
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeSecret, Value: *proxySecret},
178
-			newconfig.ConfigOpt{Option: newconfig.OptionTypeAdtag, Value: *proxyAdtag},
155
+		err := config.Init(
156
+			config.ConfigOpt{Option: config.OptionTypeDebug, Value: *proxyDebug},
157
+			config.ConfigOpt{Option: config.OptionTypeVerbose, Value: *proxyVerbose},
158
+			config.ConfigOpt{Option: config.OptionTypeBindIP, Value: *proxyBindIP},
159
+			config.ConfigOpt{Option: config.OptionTypeBindPort, Value: *proxyBindPort},
160
+			config.ConfigOpt{Option: config.OptionTypePublicIPv4, Value: *proxyPublicIPv4},
161
+			config.ConfigOpt{Option: config.OptionTypePublicIPv4Port, Value: *proxyPublicIPv4Port},
162
+			config.ConfigOpt{Option: config.OptionTypePublicIPv6, Value: *proxyPublicIPv6},
163
+			config.ConfigOpt{Option: config.OptionTypePublicIPv6Port, Value: *proxyPublicIPv6Port},
164
+			config.ConfigOpt{Option: config.OptionTypeStatsIP, Value: *proxyStatsIP},
165
+			config.ConfigOpt{Option: config.OptionTypeStatsPort, Value: *proxyStatsPort},
166
+			config.ConfigOpt{Option: config.OptionTypeStatsdIP, Value: *proxyStatsdIP},
167
+			config.ConfigOpt{Option: config.OptionTypeStatsdPort, Value: *proxyStatsdPort},
168
+			config.ConfigOpt{Option: config.OptionTypeStatsdNetwork, Value: *proxyStatsdNetwork},
169
+			config.ConfigOpt{Option: config.OptionTypeStatsdPrefix, Value: *proxyStatsdPrefix},
170
+			config.ConfigOpt{Option: config.OptionTypeStatsdTagsFormat, Value: *proxyStatsdTagsFormat},
171
+			config.ConfigOpt{Option: config.OptionTypeStatsdTags, Value: *proxyStatsdTags},
172
+			config.ConfigOpt{Option: config.OptionTypePrometheusPrefix, Value: *proxyPrometheusPrefix},
173
+			config.ConfigOpt{Option: config.OptionTypeWriteBufferSize, Value: *proxyWriteBufferSize},
174
+			config.ConfigOpt{Option: config.OptionTypeReadBufferSize, Value: *proxyReadBufferSize},
175
+			config.ConfigOpt{Option: config.OptionTypeAntiReplayMaxSize, Value: *proxyAntiReplayMaxSize},
176
+			config.ConfigOpt{Option: config.OptionTypeAntiReplayEvictionTime, Value: *proxyAntiReplayEvictionTime},
177
+			config.ConfigOpt{Option: config.OptionTypeSecret, Value: *proxySecret},
178
+			config.ConfigOpt{Option: config.OptionTypeAdtag, Value: *proxyAdtag},
179 179
 		)
180 180
 		if err != nil {
181
-			newcli.Fatal(err.Error())
181
+			cli.Fatal(err.Error())
182 182
 		}
183 183
 
184
-		if err := newcli.Proxy(); err != nil {
185
-			newcli.Fatal(err.Error())
184
+		if err := cli.Proxy(); err != nil {
185
+			cli.Fatal(err.Error())
186 186
 		}
187 187
 	}
188 188
 }

+ 0
- 32
newantireplay/cache.go 파일 보기

@@ -1,32 +0,0 @@
1
-package newantireplay
2
-
3
-import (
4
-	"github.com/allegro/bigcache"
5
-
6
-	"github.com/9seconds/mtg/newconfig"
7
-)
8
-
9
-var cache *bigcache.BigCache
10
-
11
-func Add(data []byte) {
12
-	cache.Set(string(data), nil)
13
-}
14
-
15
-func Has(data []byte) bool {
16
-	_, err := cache.Get(string(data))
17
-	return err == nil
18
-}
19
-
20
-func Init() {
21
-	c, err := bigcache.NewBigCache(bigcache.Config{
22
-		Shards:           1024,
23
-		LifeWindow:       newconfig.C.AntiReplay.EvictionTime,
24
-		Hasher:           hasher{},
25
-		HardMaxCacheSize: newconfig.C.AntiReplay.MaxSize,
26
-	})
27
-	if err != nil {
28
-		panic(err)
29
-	}
30
-
31
-	cache = c
32
-}

+ 0
- 61
newcli/proxy.go 파일 보기

@@ -1,61 +0,0 @@
1
-package newcli
2
-
3
-import (
4
-	"os"
5
-	"time"
6
-
7
-	"go.uber.org/zap"
8
-	"go.uber.org/zap/zapcore"
9
-
10
-	"github.com/9seconds/mtg/newconfig"
11
-	"github.com/9seconds/mtg/newstats"
12
-	"github.com/9seconds/mtg/ntp"
13
-)
14
-
15
-func Proxy() error {
16
-	atom := zap.NewAtomicLevel()
17
-	switch {
18
-	case newconfig.C.Debug:
19
-		atom.SetLevel(zapcore.DebugLevel)
20
-	case newconfig.C.Verbose:
21
-		atom.SetLevel(zapcore.InfoLevel)
22
-	default:
23
-		atom.SetLevel(zapcore.ErrorLevel)
24
-	}
25
-
26
-	encoderCfg := zap.NewProductionEncoderConfig()
27
-	logger := zap.New(zapcore.NewCore(
28
-		zapcore.NewJSONEncoder(encoderCfg),
29
-		zapcore.Lock(os.Stderr),
30
-		atom,
31
-	))
32
-	zap.ReplaceGlobals(logger)
33
-	defer logger.Sync() // nolint: errcheck
34
-
35
-	if err := newconfig.InitPublicAddress(); err != nil {
36
-		Fatal(err.Error())
37
-	}
38
-	zap.S().Debugw("Configuration", "config", newconfig.C)
39
-
40
-	if len(newconfig.C.AdTag) > 0 {
41
-		zap.S().Infow("Use middle proxy connection to Telegram")
42
-		diff, err := ntp.Fetch()
43
-		if err != nil {
44
-			Fatal("Cannot fetch time data from NTP")
45
-		}
46
-		if diff > time.Second {
47
-			Fatal("Your local time is skewed and drift is bigger than a second. Please sync your time.")
48
-		}
49
-		go ntp.AutoUpdate()
50
-	} else {
51
-		zap.S().Infow("Use direct connection to Telegram")
52
-	}
53
-
54
-	PrintJSONStdout(newconfig.GetURLs())
55
-
56
-	if err := newstats.Init(); err != nil {
57
-		Fatal(err.Error())
58
-	}
59
-
60
-	return nil
61
-}

+ 0
- 258
newconfig/config.go 파일 보기

@@ -1,258 +0,0 @@
1
-package newconfig
2
-
3
-import (
4
-	"bytes"
5
-	"encoding/json"
6
-	"net"
7
-	"strconv"
8
-	"time"
9
-
10
-	"github.com/juju/errors"
11
-	"go.uber.org/zap"
12
-	statsd "gopkg.in/alexcesaro/statsd.v2"
13
-)
14
-
15
-type SecretMode uint8
16
-
17
-func (s SecretMode) String() string {
18
-	switch s {
19
-	case SecretModeSimple:
20
-		return "simple"
21
-	case SecretModeSecured:
22
-		return "secured"
23
-	}
24
-	return "tls"
25
-}
26
-
27
-const (
28
-	SecretModeSimple SecretMode = iota
29
-	SecretModeSecured
30
-	SecretModeTLS
31
-)
32
-
33
-const SimpleSecretLength = 16
34
-
35
-type OptionType uint8
36
-
37
-const (
38
-	OptionTypeDebug OptionType = iota
39
-	OptionTypeVerbose
40
-
41
-	OptionTypeBindIP
42
-	OptionTypeBindPort
43
-	OptionTypePublicIPv4
44
-	OptionTypePublicIPv4Port
45
-	OptionTypePublicIPv6
46
-	OptionTypePublicIPv6Port
47
-	OptionTypeStatsIP
48
-	OptionTypeStatsPort
49
-
50
-	OptionTypeStatsdIP
51
-	OptionTypeStatsdPort
52
-	OptionTypeStatsdNetwork
53
-	OptionTypeStatsdPrefix
54
-	OptionTypeStatsdTagsFormat
55
-	OptionTypeStatsdTags
56
-	OptionTypePrometheusPrefix
57
-
58
-	OptionTypeWriteBufferSize
59
-	OptionTypeReadBufferSize
60
-
61
-	OptionTypeAntiReplayMaxSize
62
-	OptionTypeAntiReplayEvictionTime
63
-
64
-	OptionTypeSecret
65
-	OptionTypeAdtag
66
-)
67
-
68
-type BufferSize struct {
69
-	Read  int `json:"read"`
70
-	Write int `json:"write"`
71
-}
72
-
73
-type AntiReplay struct {
74
-	MaxSize      int           `json:"max_size"`
75
-	EvictionTime time.Duration `json:"duration"`
76
-}
77
-
78
-type Stats struct {
79
-	Prefix string `json:"prefix"`
80
-}
81
-
82
-type StatsdStats struct {
83
-	Stats
84
-
85
-	Addr       Addr              `json:"addr"`
86
-	Tags       map[string]string `json:"tags"`
87
-	TagsFormat statsd.TagFormat  `json:"format"`
88
-}
89
-
90
-type PrometheusStats struct {
91
-	Stats
92
-}
93
-
94
-type Addr struct {
95
-	IP   net.IP `json:"ip"`
96
-	Port int    `json:"port"`
97
-	net  string
98
-}
99
-
100
-func (a Addr) Network() string {
101
-	if a.net == "" {
102
-		return "tcp"
103
-	}
104
-	return a.net
105
-}
106
-
107
-func (a Addr) String() string {
108
-	return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
109
-}
110
-
111
-func (a Addr) MarshalJSON() ([]byte, error) {
112
-	data := map[string]string{
113
-		"network": a.Network(),
114
-		"addr":    a.String(),
115
-	}
116
-	return json.Marshal(data)
117
-}
118
-
119
-type Config struct {
120
-	BufferSize BufferSize `json:"buffer_size"`
121
-	AntiReplay AntiReplay `json:"anti_replay"`
122
-
123
-	ListenAddr     Addr `json:"listen_addr"`
124
-	PublicIPv4Addr Addr `json:"public_ipv4_addr"`
125
-	PublicIPv6Addr Addr `json:"public_ipv6_addr"`
126
-	StatsAddr      Addr `json:"stats_addr"`
127
-
128
-	StatsdStats     StatsdStats     `json:"stats_statsd"`
129
-	PrometheusStats PrometheusStats `json:"stats_prometheus"`
130
-
131
-	Debug      bool       `json:"debug"`
132
-	Verbose    bool       `json:"verbose"`
133
-	SecretMode SecretMode `json:"secret_mode"`
134
-	Secret     []byte     `json:"secret"`
135
-	AdTag      []byte     `json:"adtag"`
136
-}
137
-
138
-func (c Config) String() string {
139
-	data, _ := json.Marshal(c)
140
-	return string(data)
141
-}
142
-
143
-type ConfigOpt struct {
144
-	Option OptionType
145
-	Value  interface{}
146
-}
147
-
148
-var C = Config{}
149
-
150
-func Init(options ...ConfigOpt) error { // nolint: gocyclo
151
-	for _, opt := range options {
152
-		switch opt.Option {
153
-		case OptionTypeDebug:
154
-			C.Debug = opt.Value.(bool)
155
-		case OptionTypeVerbose:
156
-			C.Verbose = opt.Value.(bool)
157
-		case OptionTypeBindIP:
158
-			C.ListenAddr.IP = opt.Value.(net.IP)
159
-		case OptionTypeBindPort:
160
-			C.ListenAddr.Port = int(opt.Value.(uint16))
161
-		case OptionTypePublicIPv4:
162
-			C.PublicIPv4Addr.IP = opt.Value.(net.IP)
163
-		case OptionTypePublicIPv4Port:
164
-			C.PublicIPv4Addr.Port = int(opt.Value.(uint16))
165
-		case OptionTypePublicIPv6:
166
-			C.PublicIPv6Addr.IP = opt.Value.(net.IP)
167
-		case OptionTypePublicIPv6Port:
168
-			C.PublicIPv6Addr.Port = int(opt.Value.(uint16))
169
-		case OptionTypeStatsIP:
170
-			C.StatsAddr.IP = opt.Value.(net.IP)
171
-		case OptionTypeStatsPort:
172
-			C.StatsAddr.Port = int(opt.Value.(uint16))
173
-		case OptionTypeStatsdIP:
174
-			C.StatsdStats.Addr.IP = opt.Value.(net.IP)
175
-		case OptionTypeStatsdPort:
176
-			C.StatsdStats.Addr.Port = int(opt.Value.(uint16))
177
-		case OptionTypeStatsdNetwork:
178
-			C.StatsdStats.Addr.net = opt.Value.(string)
179
-		case OptionTypeStatsdPrefix:
180
-			C.StatsdStats.Prefix = opt.Value.(string)
181
-		case OptionTypeStatsdTagsFormat:
182
-			value := opt.Value.(string)
183
-			switch value {
184
-			case "datadog":
185
-				C.StatsdStats.TagsFormat = statsd.Datadog
186
-			case "influxdb":
187
-				C.StatsdStats.TagsFormat = statsd.InfluxDB
188
-			default:
189
-				return errors.Errorf("Incorrect statsd tag %s", value)
190
-			}
191
-		case OptionTypeStatsdTags:
192
-			C.StatsdStats.Tags = opt.Value.(map[string]string)
193
-		case OptionTypePrometheusPrefix:
194
-			C.PrometheusStats.Prefix = opt.Value.(string)
195
-		case OptionTypeWriteBufferSize:
196
-			C.BufferSize.Write = int(opt.Value.(uint32))
197
-		case OptionTypeReadBufferSize:
198
-			C.BufferSize.Read = int(opt.Value.(uint32))
199
-		case OptionTypeAntiReplayMaxSize:
200
-			C.AntiReplay.MaxSize = opt.Value.(int)
201
-		case OptionTypeAntiReplayEvictionTime:
202
-			C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
203
-		case OptionTypeSecret:
204
-			C.Secret = opt.Value.([]byte)
205
-		case OptionTypeAdtag:
206
-			C.AdTag = opt.Value.([]byte)
207
-		default:
208
-			return errors.Errorf("Unknown tag %v", opt.Option)
209
-		}
210
-	}
211
-
212
-	switch {
213
-	case len(C.Secret) == 1+SimpleSecretLength && bytes.HasPrefix(C.Secret, []byte{0xdd}):
214
-		C.SecretMode = SecretModeSecured
215
-		C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
216
-	case len(C.Secret) == SimpleSecretLength:
217
-		C.SecretMode = SecretModeSimple
218
-	default:
219
-		return errors.New("Incorrect secret")
220
-	}
221
-
222
-	return nil
223
-}
224
-
225
-func InitPublicAddress() error {
226
-	if C.PublicIPv4Addr.Port == 0 {
227
-		C.PublicIPv4Addr.Port = C.ListenAddr.Port
228
-	}
229
-	if C.PublicIPv6Addr.Port == 0 {
230
-		C.PublicIPv6Addr.Port = C.ListenAddr.Port
231
-	}
232
-
233
-	foundAddress := C.PublicIPv4Addr.IP != nil || C.PublicIPv6Addr.IP != nil
234
-	if C.PublicIPv4Addr.IP == nil {
235
-		ip, err := getGlobalIPv4()
236
-		if err != nil {
237
-			zap.S().Warnw("Cannot resolve public address", "error", err)
238
-		} else {
239
-			C.PublicIPv4Addr.IP = ip
240
-			foundAddress = true
241
-		}
242
-	}
243
-	if C.PublicIPv6Addr.IP == nil {
244
-		ip, err := getGlobalIPv6()
245
-		if err != nil {
246
-			zap.S().Warnw("Cannot resolve public address", "error", err)
247
-		} else {
248
-			C.PublicIPv6Addr.IP = ip
249
-			foundAddress = true
250
-		}
251
-	}
252
-
253
-	if !foundAddress {
254
-		return errors.New("Cannot resolve any public address")
255
-	}
256
-
257
-	return nil
258
-}

+ 0
- 95
newobfuscated2/client_protocol.go 파일 보기

@@ -1,95 +0,0 @@
1
-package newobfuscated2
2
-
3
-import (
4
-	"bytes"
5
-	"crypto/aes"
6
-	"crypto/cipher"
7
-	"crypto/sha256"
8
-	"encoding/binary"
9
-	"io"
10
-	"time"
11
-
12
-	"github.com/juju/errors"
13
-
14
-	"github.com/9seconds/mtg/newantireplay"
15
-	"github.com/9seconds/mtg/newconfig"
16
-	"github.com/9seconds/mtg/newprotocol"
17
-	"github.com/9seconds/mtg/newwrappers"
18
-)
19
-
20
-const clientProtocolHandshakeTimeout = 10 * time.Second
21
-
22
-type ClientProtocol struct {
23
-	newprotocol.BaseProtocol
24
-}
25
-
26
-func (c *ClientProtocol) Handshake(socket newwrappers.StreamReadWriteCloser) (newwrappers.StreamReadWriteCloser, error) {
27
-	fm, err := c.ReadFrame(socket)
28
-	if err != nil {
29
-		return nil, errors.Annotate(err, "Cannot make client handshake")
30
-	}
31
-
32
-	decHasher := sha256.New()
33
-	decHasher.Write(fm.key())           // nolint: errcheck
34
-	decHasher.Write(newconfig.C.Secret) // nolint: errcheck
35
-	decryptor := makeStreamCipher(decHasher.Sum(nil), fm.iv())
36
-
37
-	invertedFrame := fm.invert()
38
-	encHasher := sha256.New()
39
-	encHasher.Write(invertedFrame.key()) // nolint: errcheck
40
-	encHasher.Write(newconfig.C.Secret)  // nolint: errcheck
41
-	encryptor := makeStreamCipher(encHasher.Sum(nil), invertedFrame.iv())
42
-
43
-	decryptedFrame := frame{}
44
-	decryptor.XORKeyStream(decryptedFrame.bytes(), fm.bytes())
45
-
46
-	magic := decryptedFrame.magic()
47
-	switch {
48
-	case bytes.Equal(magic, newprotocol.ConnectionTagAbridged):
49
-		c.ConnectionType = newprotocol.ConnectionTypeAbridged
50
-	case bytes.Equal(magic, newprotocol.ConnectionTagIntermediate):
51
-		c.ConnectionType = newprotocol.ConnectionTypeIntermediate
52
-	case bytes.Equal(magic, newprotocol.ConnectionTagSecure):
53
-		c.ConnectionType = newprotocol.ConnectionTypeSecure
54
-	default:
55
-		return nil, errors.New("Unknown connection type")
56
-	}
57
-
58
-	c.ConnectionProtocol = newprotocol.ConnectionProtocolIPv4
59
-	if socket.LocalAddr().IP.To4() == nil {
60
-		c.ConnectionProtocol = newprotocol.ConnectionProtocolIPv6
61
-	}
62
-
63
-	buf := bytes.NewReader(decryptedFrame.dc())
64
-	if err := binary.Read(buf, binary.LittleEndian, &c.DC); err != nil {
65
-		c.DC = 1
66
-	}
67
-
68
-	antiReplayKey := decryptedFrame.unique()
69
-	if newantireplay.Has(antiReplayKey) {
70
-		return nil, errors.New("Replay attack is detected")
71
-	}
72
-	newantireplay.Add(antiReplayKey)
73
-
74
-	return newwrappers.NewObfuscated2(socket, encryptor, decryptor), nil
75
-}
76
-
77
-func (c *ClientProtocol) ReadFrame(socket newwrappers.StreamReader) (fm frame, err error) {
78
-	if _, err := io.ReadFull(handshakeReader{socket}, fm.bytes()); err != nil {
79
-		err = errors.Annotate(err, "Cannot extract obfuscated2 frame")
80
-	}
81
-	return
82
-}
83
-
84
-type handshakeReader struct {
85
-	parent newwrappers.StreamReader
86
-}
87
-
88
-func (h handshakeReader) Read(p []byte) (int, error) {
89
-	return h.parent.ReadTimeout(p, clientProtocolHandshakeTimeout)
90
-}
91
-
92
-func makeStreamCipher(key, iv []byte) cipher.Stream {
93
-	block, _ := aes.NewCipher(key) // nolint: gosec
94
-	return cipher.NewCTR(block, iv)
95
-}

+ 0
- 54
newobfuscated2/frame.go 파일 보기

@@ -1,54 +0,0 @@
1
-package newobfuscated2
2
-
3
-const (
4
-	frameLenKey   = 32
5
-	frameLenIV    = 16
6
-	frameLenMagic = 4
7
-	frameLenDC    = 2
8
-
9
-	frameOffsetFirst = 8
10
-	frameOffsetKey   = frameOffsetFirst + frameLenKey
11
-	frameOffsetIV    = frameOffsetKey + frameLenIV
12
-	frameOffsetMagic = frameOffsetIV + frameLenMagic
13
-	frameOffsetDC    = frameOffsetMagic + frameLenDC
14
-
15
-	frameLen = 64
16
-)
17
-
18
-// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
19
-type frame struct {
20
-	data [frameLen]byte
21
-}
22
-
23
-func (f *frame) bytes() []byte {
24
-	return f.data[:]
25
-}
26
-
27
-func (f *frame) key() []byte {
28
-	return f.data[frameOffsetFirst:frameOffsetKey]
29
-}
30
-
31
-func (f *frame) iv() []byte {
32
-	return f.data[frameOffsetKey:frameOffsetIV]
33
-}
34
-
35
-func (f *frame) magic() []byte {
36
-	return f.data[frameOffsetIV:frameOffsetMagic]
37
-}
38
-
39
-func (f *frame) dc() []byte {
40
-	return f.data[frameOffsetMagic:frameOffsetDC]
41
-}
42
-
43
-func (f *frame) unique() []byte {
44
-	return f.data[frameOffsetFirst:frameOffsetDC]
45
-}
46
-
47
-func (f *frame) invert() (nf frame) {
48
-	nf = *f
49
-	for i := 0; i < frameLenKey+frameLenIV; i++ {
50
-		nf.data[frameOffsetFirst+i] = nf.data[frameOffsetIV-1-i]
51
-	}
52
-
53
-	return
54
-}

+ 0
- 61
newobfuscated2/telegram_protocol.go 파일 보기

@@ -1,61 +0,0 @@
1
-package newobfuscated2
2
-
3
-import (
4
-	"crypto/rand"
5
-
6
-	"github.com/juju/errors"
7
-
8
-	"github.com/9seconds/mtg/newprotocol"
9
-	"github.com/9seconds/mtg/newwrappers"
10
-)
11
-
12
-type TelegramProtocol struct {
13
-	newprotocol.BaseProtocol
14
-}
15
-
16
-func (t *TelegramProtocol) Handshake(socketRaw newwrappers.Wrap, client *ClientProtocol) (newwrappers.StreamReadWriteCloser, error) {
17
-	socket := socketRaw.(newwrappers.StreamReadWriteCloser)
18
-	fm := generateFrame(client)
19
-	data := fm.bytes()
20
-
21
-	encryptor := makeStreamCipher(fm.key(), fm.iv())
22
-	decryptedFrame := fm.invert()
23
-	decryptor := makeStreamCipher(decryptedFrame.key(), decryptedFrame.iv())
24
-
25
-	copyFrame := make([]byte, frameLen)
26
-	copy(copyFrame[:frameOffsetIV], data[:frameOffsetIV])
27
-	encryptor.XORKeyStream(data, data)
28
-	copy(data[:frameOffsetIV], copyFrame[:frameOffsetIV])
29
-
30
-	if _, err := socket.Write(data); err != nil {
31
-		return nil, errors.Annotate(err, "Cannot write handshate frame to Telegram")
32
-	}
33
-
34
-	return newwrappers.NewObfuscated2(socket, encryptor, decryptor), nil
35
-}
36
-
37
-func generateFrame(client *ClientProtocol) (fm frame) {
38
-	for {
39
-		data := fm.bytes()
40
-		if _, err := rand.Read(data); err != nil {
41
-			continue
42
-		}
43
-		if data[0] == 0xef {
44
-			continue
45
-		}
46
-
47
-		val := (uint32(data[3]) << 24) | (uint32(data[2]) << 16) | (uint32(data[1]) << 8) | uint32(data[0])
48
-		if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee {
49
-			continue
50
-		}
51
-
52
-		val = (uint32(data[7]) << 24) | (uint32(data[6]) << 16) | (uint32(data[5]) << 8) | uint32(data[4])
53
-		if val == 0x00000000 {
54
-			continue
55
-		}
56
-
57
-		copy(fm.magic(), client.ConnectionType.Tag())
58
-
59
-		return
60
-	}
61
-}

+ 0
- 7
newprotocol/base_protocol.go 파일 보기

@@ -1,7 +0,0 @@
1
-package newprotocol
2
-
3
-type BaseProtocol struct {
4
-	ConnectionType     ConnectionType
5
-	ConnectionProtocol ConnectionProtocol
6
-	DC                 int16
7
-}

+ 0
- 1
newproxy/proxy.go 파일 보기

@@ -1 +0,0 @@
1
-package newproxy

+ 0
- 93
newstats/stats.go 파일 보기

@@ -1,93 +0,0 @@
1
-package newstats
2
-
3
-import (
4
-	"net"
5
-	"net/http"
6
-
7
-	"github.com/juju/errors"
8
-
9
-	"github.com/9seconds/mtg/newconfig"
10
-	"github.com/9seconds/mtg/newprotocol"
11
-)
12
-
13
-type Stats interface {
14
-	IngressTraffic(int)
15
-	EgressTraffic(int)
16
-	ClientConnected(newprotocol.ConnectionType, *net.TCPAddr)
17
-	ClientDisconnected(newprotocol.ConnectionType, *net.TCPAddr)
18
-	Crash()
19
-	AntiReplayDetected()
20
-}
21
-
22
-type multiStats []Stats
23
-
24
-func (m multiStats) IngressTraffic(traffic int) {
25
-	for i := range m {
26
-		go m[i].IngressTraffic(traffic)
27
-	}
28
-}
29
-
30
-func (m multiStats) EgressTraffic(traffic int) {
31
-	for i := range m {
32
-		go m[i].EgressTraffic(traffic)
33
-	}
34
-}
35
-
36
-func (m multiStats) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
37
-	for i := range m {
38
-		go m[i].ClientConnected(connectionType, addr)
39
-	}
40
-}
41
-
42
-func (m multiStats) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
43
-	for i := range m {
44
-		go m[i].ClientDisconnected(connectionType, addr)
45
-	}
46
-}
47
-
48
-func (m multiStats) Crash() {
49
-	for i := range m {
50
-		go m[i].Crash()
51
-	}
52
-}
53
-
54
-func (m multiStats) AntiReplayDetected() {
55
-	for i := range m {
56
-		go m[i].AntiReplayDetected()
57
-	}
58
-}
59
-
60
-var S Stats
61
-
62
-func Init() error {
63
-	mux := http.NewServeMux()
64
-
65
-	instanceJSON := newStatsJSON(mux)
66
-	instancePrometheus, err := newStatsPrometheus(mux)
67
-	if err != nil {
68
-		return errors.Annotate(err, "Cannot initialize Prometheus")
69
-	}
70
-
71
-	stats := []Stats{instanceJSON, instancePrometheus}
72
-	if newconfig.C.StatsdStats.Addr.IP != nil {
73
-		instanceStatsd, err := newStatsStatsd()
74
-		if err != nil {
75
-			return errors.Annotate(err, "Cannot initialize StatsD")
76
-		}
77
-		stats = append(stats, instanceStatsd)
78
-	}
79
-
80
-	listener, err := net.Listen("tcp", newconfig.C.StatsAddr.String())
81
-	if err != nil {
82
-		return errors.Annotate(err, "Cannot initialize stats server")
83
-	}
84
-
85
-	srv := http.Server{
86
-		Handler: mux,
87
-	}
88
-	go srv.Serve(listener) // nolint: errcheck
89
-
90
-	S = multiStats(stats)
91
-
92
-	return nil
93
-}

+ 94
- 0
obfuscated2/client_protocol.go 파일 보기

@@ -0,0 +1,94 @@
1
+package obfuscated2
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/sha256"
6
+	"encoding/binary"
7
+	"io"
8
+	"time"
9
+
10
+	"github.com/juju/errors"
11
+
12
+	"github.com/9seconds/mtg/antireplay"
13
+	"github.com/9seconds/mtg/config"
14
+	"github.com/9seconds/mtg/conntypes"
15
+	"github.com/9seconds/mtg/protocol"
16
+	"github.com/9seconds/mtg/utils"
17
+	"github.com/9seconds/mtg/wrappers"
18
+)
19
+
20
+const clientProtocolHandshakeTimeout = 10 * time.Second
21
+
22
+type ClientProtocol struct {
23
+	protocol.BaseProtocol
24
+}
25
+
26
+func (c *ClientProtocol) Handshake(socket wrappers.StreamReadWriteCloser) (wrappers.StreamReadWriteCloser, error) {
27
+	fm, err := c.ReadFrame(socket)
28
+	if err != nil {
29
+		return nil, errors.Annotate(err, "Cannot make client handshake")
30
+	}
31
+
32
+	decHasher := sha256.New()
33
+	decHasher.Write(fm.Key())        // nolint: errcheck
34
+	decHasher.Write(config.C.Secret) // nolint: errcheck
35
+	decryptor := utils.MakeStreamCipher(decHasher.Sum(nil), fm.IV())
36
+
37
+	invertedFrame := fm.Invert()
38
+	encHasher := sha256.New()
39
+	encHasher.Write(invertedFrame.Key()) // nolint: errcheck
40
+	encHasher.Write(config.C.Secret)     // nolint: errcheck
41
+	encryptor := utils.MakeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
42
+
43
+	decryptedFrame := Frame{}
44
+	decryptor.XORKeyStream(decryptedFrame.Bytes(), fm.Bytes())
45
+
46
+	magic := decryptedFrame.Magic()
47
+	switch {
48
+	case bytes.Equal(magic, conntypes.ConnectionTagAbridged):
49
+		c.ConnectionType = conntypes.ConnectionTypeAbridged
50
+	case bytes.Equal(magic, conntypes.ConnectionTagIntermediate):
51
+		c.ConnectionType = conntypes.ConnectionTypeIntermediate
52
+	case bytes.Equal(magic, conntypes.ConnectionTagSecure):
53
+		c.ConnectionType = conntypes.ConnectionTypeSecure
54
+	default:
55
+		return nil, errors.New("Unknown connection type")
56
+	}
57
+
58
+	c.ConnectionProtocol = conntypes.ConnectionProtocolIPv4
59
+	if socket.LocalAddr().IP.To4() == nil {
60
+		c.ConnectionProtocol = conntypes.ConnectionProtocolIPv6
61
+	}
62
+
63
+	buf := bytes.NewReader(decryptedFrame.DC())
64
+	if err := binary.Read(buf, binary.LittleEndian, &c.DC); err != nil {
65
+		c.DC = conntypes.DCDefaultIdx
66
+	}
67
+
68
+	antiReplayKey := decryptedFrame.Unique()
69
+	if antireplay.Has(antiReplayKey) {
70
+		return nil, errors.New("Replay attack is detected")
71
+	}
72
+	antireplay.Add(antiReplayKey)
73
+
74
+	return wrappers.NewObfuscated2(socket, encryptor, decryptor), nil
75
+}
76
+
77
+func (c *ClientProtocol) ReadFrame(socket wrappers.StreamReader) (fm Frame, err error) {
78
+	if _, err = io.ReadFull(handshakeReader{socket}, fm.Bytes()); err != nil {
79
+		err = errors.Annotate(err, "Cannot extract obfuscated2 frame")
80
+	}
81
+	return
82
+}
83
+
84
+type handshakeReader struct {
85
+	parent wrappers.StreamReader
86
+}
87
+
88
+func (h handshakeReader) Read(p []byte) (int, error) {
89
+	return h.parent.ReadTimeout(p, clientProtocolHandshakeTimeout)
90
+}
91
+
92
+func MakeClientProtocol() protocol.ClientProtocol {
93
+	return &ClientProtocol{}
94
+}

+ 22
- 89
obfuscated2/frame.go 파일 보기

@@ -1,17 +1,5 @@
1 1
 package obfuscated2
2 2
 
3
-import (
4
-	"bytes"
5
-	"crypto/rand"
6
-	"encoding/binary"
7
-	"io"
8
-
9
-	"github.com/juju/errors"
10
-
11
-	"github.com/9seconds/mtg/mtproto"
12
-)
13
-
14
-// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
15 3
 const (
16 4
 	frameLenKey   = 32
17 5
 	frameLenIV    = 16
@@ -24,98 +12,43 @@ const (
24 12
 	frameOffsetMagic = frameOffsetIV + frameLenMagic
25 13
 	frameOffsetDC    = frameOffsetMagic + frameLenDC
26 14
 
27
-	FrameLen = 64
15
+	frameLen = 64
28 16
 )
29 17
 
30
-// Frame represents handshake frame. Telegram sends 64 bytes of obfuscated2
31
-// initialization data first.
32
-// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
33
-type Frame []byte
34
-
35
-// Key returns AES encryption key.
36
-func (f Frame) Key() []byte {
37
-	return f[frameOffsetFirst:frameOffsetKey]
18
+// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
19
+type Frame struct {
20
+	data [frameLen]byte
38 21
 }
39 22
 
40
-// IV returns AES encryption initialization vector
41
-func (f Frame) IV() []byte {
42
-	return f[frameOffsetKey:frameOffsetIV]
23
+func (f *Frame) Bytes() []byte {
24
+	return f.data[:]
43 25
 }
44 26
 
45
-// Magic returns magic bytes from last 8 bytes of frame. Telegram checks
46
-// for values there. If after decryption magic is not as expected,
47
-// connection considered as failed.
48
-func (f Frame) Magic() []byte {
49
-	return f[frameOffsetIV:frameOffsetMagic]
27
+func (f *Frame) Key() []byte {
28
+	return f.data[frameOffsetFirst:frameOffsetKey]
50 29
 }
51 30
 
52
-// DC returns number of datacenter IP client wants to use.
53
-func (f Frame) DC() (n int16) {
54
-	buf := bytes.NewReader(f[frameOffsetMagic:frameOffsetDC])
55
-	if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
56
-		n = 1
57
-	}
58
-
59
-	return
31
+func (f *Frame) IV() []byte {
32
+	return f.data[frameOffsetKey:frameOffsetIV]
60 33
 }
61 34
 
62
-// ConnectionType identifies connection type of the handshake frame.
63
-func (f Frame) ConnectionType() (mtproto.ConnectionType, error) {
64
-	return mtproto.ConnectionTagFromHandshake(f.Magic())
35
+func (f *Frame) Magic() []byte {
36
+	return f.data[frameOffsetIV:frameOffsetMagic]
65 37
 }
66 38
 
67
-// Invert inverts frame for extracting encryption keys. Pkease check that link:
68
-// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
69
-func (f Frame) Invert() Frame {
70
-	reversed := make(Frame, FrameLen)
71
-	copy(reversed, f)
72
-
73
-	for i := 0; i < frameLenKey+frameLenIV; i++ {
74
-		reversed[frameOffsetFirst+i] = f[frameOffsetIV-1-i]
75
-	}
76
-
77
-	return reversed
39
+func (f *Frame) DC() []byte {
40
+	return f.data[frameOffsetMagic:frameOffsetDC]
78 41
 }
79 42
 
80
-// ExtractFrame extracts exact obfuscated2 handshake frame from given reader.
81
-func ExtractFrame(conn io.Reader) (Frame, error) {
82
-	frame := make(Frame, FrameLen)
83
-	buf := bytes.NewBuffer(frame)
84
-	buf.Reset()
85
-
86
-	if _, err := io.CopyN(buf, conn, FrameLen); err != nil {
87
-		return nil, errors.Annotate(err, "Cannot extract obfuscated header")
88
-	}
89
-	copy(frame, buf.Bytes())
90
-
91
-	return frame, nil
43
+func (f *Frame) Unique() []byte {
44
+	return f.data[frameOffsetFirst:frameOffsetDC]
92 45
 }
93 46
 
94
-func generateFrame(connectionType mtproto.ConnectionType) Frame {
95
-	frame := make(Frame, FrameLen)
96
-
97
-	for {
98
-		if _, err := rand.Read(frame); err != nil {
99
-			continue
100
-		}
101
-		if frame[0] == 0xef {
102
-			continue
103
-		}
104
-
105
-		val := (uint32(frame[3]) << 24) | (uint32(frame[2]) << 16) | (uint32(frame[1]) << 8) | uint32(frame[0])
106
-		if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee {
107
-			continue
108
-		}
109
-
110
-		val = (uint32(frame[7]) << 24) | (uint32(frame[6]) << 16) | (uint32(frame[5]) << 8) | uint32(frame[4])
111
-		if val == 0x00000000 {
112
-			continue
113
-		}
114
-
115
-		// error has to be checked before calling this function
116
-		tag, _ := connectionType.Tag() // nolint: errcheck, gosec
117
-		copy(frame.Magic(), tag)
118
-
119
-		return frame
47
+func (f *Frame) Invert() (nf Frame) {
48
+	nf = *f
49
+	for i := 0; i < frameLenKey+frameLenIV; i++ {
50
+		nf.data[frameOffsetFirst+i] = f.data[frameOffsetIV-1-i]
120 51
 	}
52
+
53
+	return
121 54
 }

+ 78
- 0
obfuscated2/telegram_protocol.go 파일 보기

@@ -0,0 +1,78 @@
1
+package obfuscated2
2
+
3
+import (
4
+	"crypto/rand"
5
+
6
+	"github.com/juju/errors"
7
+
8
+	"github.com/9seconds/mtg/protocol"
9
+	"github.com/9seconds/mtg/telegram"
10
+	"github.com/9seconds/mtg/utils"
11
+	"github.com/9seconds/mtg/wrappers"
12
+)
13
+
14
+type TelegramProtocol struct {
15
+	protocol.BaseProtocol
16
+
17
+	dialer telegram.Telegram
18
+}
19
+
20
+func (t *TelegramProtocol) Handshake(req *protocol.TelegramRequest) (wrappers.Wrap, error) {
21
+	socket, err := t.dialer.Dial(req.Ctx,
22
+		req.Cancel,
23
+		req.ClientProtocol.GetDC(),
24
+		req.ClientProtocol.GetConnectionProtocol())
25
+	if err != nil {
26
+		return nil, errors.Annotate(err, "Cannot dial to Telegram")
27
+	}
28
+	fm := generateFrame(req.ClientProtocol)
29
+	data := fm.Bytes()
30
+
31
+	encryptor := utils.MakeStreamCipher(fm.Key(), fm.IV())
32
+	decryptedFrame := fm.Invert()
33
+	decryptor := utils.MakeStreamCipher(decryptedFrame.Key(), decryptedFrame.IV())
34
+
35
+	copyFrame := make([]byte, frameLen)
36
+	copy(copyFrame[:frameOffsetIV], data[:frameOffsetIV])
37
+	encryptor.XORKeyStream(data, data)
38
+	copy(data[:frameOffsetIV], copyFrame[:frameOffsetIV])
39
+
40
+	if _, err := socket.Write(data); err != nil {
41
+		return nil, errors.Annotate(err, "Cannot write handshate frame to Telegram")
42
+	}
43
+
44
+	return wrappers.NewObfuscated2(socket, encryptor, decryptor), nil
45
+}
46
+
47
+func MakeTelegramProtocol(dialer telegram.Telegram) protocol.TelegramProtocol {
48
+	return &TelegramProtocol{
49
+		dialer: dialer,
50
+	}
51
+}
52
+
53
+func generateFrame(cp protocol.ClientProtocol) (fm Frame) {
54
+	data := fm.Bytes()
55
+
56
+	for {
57
+		if _, err := rand.Read(data); err != nil {
58
+			continue
59
+		}
60
+		if data[0] == 0xef {
61
+			continue
62
+		}
63
+
64
+		val := (uint32(data[3]) << 24) | (uint32(data[2]) << 16) | (uint32(data[1]) << 8) | uint32(data[0])
65
+		if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee {
66
+			continue
67
+		}
68
+
69
+		val = (uint32(data[7]) << 24) | (uint32(data[6]) << 16) | (uint32(data[5]) << 8) | uint32(data[4])
70
+		if val == 0x00000000 {
71
+			continue
72
+		}
73
+
74
+		copy(fm.Magic(), cp.GetConnectionType().Tag())
75
+
76
+		return
77
+	}
78
+}

+ 21
- 0
protocol/base_protocol.go 파일 보기

@@ -0,0 +1,21 @@
1
+package protocol
2
+
3
+import "github.com/9seconds/mtg/conntypes"
4
+
5
+type BaseProtocol struct {
6
+	ConnectionType     conntypes.ConnectionType
7
+	ConnectionProtocol conntypes.ConnectionProtocol
8
+	DC                 conntypes.DC
9
+}
10
+
11
+func (b *BaseProtocol) GetConnectionType() conntypes.ConnectionType {
12
+	return b.ConnectionType
13
+}
14
+
15
+func (b *BaseProtocol) GetConnectionProtocol() conntypes.ConnectionProtocol {
16
+	return b.ConnectionProtocol
17
+}
18
+
19
+func (b *BaseProtocol) GetDC() conntypes.DC {
20
+	return b.DC
21
+}

+ 22
- 0
protocol/interfaces.go 파일 보기

@@ -0,0 +1,22 @@
1
+package protocol
2
+
3
+import (
4
+	"github.com/9seconds/mtg/conntypes"
5
+	"github.com/9seconds/mtg/telegram"
6
+	"github.com/9seconds/mtg/wrappers"
7
+)
8
+
9
+type ClientProtocol interface {
10
+	Handshake(wrappers.StreamReadWriteCloser) (wrappers.StreamReadWriteCloser, error)
11
+	GetConnectionType() conntypes.ConnectionType
12
+	GetConnectionProtocol() conntypes.ConnectionProtocol
13
+	GetDC() conntypes.DC
14
+}
15
+
16
+type ClientProtocolMaker func() ClientProtocol
17
+
18
+type TelegramProtocol interface {
19
+	Handshake(*TelegramRequest) (wrappers.Wrap, error)
20
+}
21
+
22
+type TelegramProtocolMaker func(telegram.Telegram) TelegramProtocol

+ 18
- 0
protocol/request.go 파일 보기

@@ -0,0 +1,18 @@
1
+package protocol
2
+
3
+import (
4
+	"context"
5
+
6
+	"go.uber.org/zap"
7
+
8
+	"github.com/9seconds/mtg/wrappers"
9
+)
10
+
11
+type TelegramRequest struct {
12
+	Logger         *zap.SugaredLogger
13
+	ClientConn     wrappers.StreamReadWriteCloser
14
+	ConnID         wrappers.ConnID
15
+	Ctx            context.Context
16
+	Cancel         context.CancelFunc
17
+	ClientProtocol ClientProtocol
18
+}

+ 70
- 120
proxy/proxy.go 파일 보기

@@ -6,173 +6,123 @@ import (
6 6
 	"net"
7 7
 	"sync"
8 8
 
9
-	"github.com/gofrs/uuid"
10
-	"github.com/juju/errors"
11 9
 	"go.uber.org/zap"
12 10
 
13
-	"github.com/9seconds/mtg/antireplay"
14
-	"github.com/9seconds/mtg/client"
15 11
 	"github.com/9seconds/mtg/config"
16
-	"github.com/9seconds/mtg/mtproto"
12
+	"github.com/9seconds/mtg/protocol"
17 13
 	"github.com/9seconds/mtg/stats"
18 14
 	"github.com/9seconds/mtg/telegram"
15
+	"github.com/9seconds/mtg/utils"
19 16
 	"github.com/9seconds/mtg/wrappers"
20 17
 )
21 18
 
22
-// Proxy is a core of this program.
19
+const directPipeBufferSize = 1024 * 1024
20
+
23 21
 type Proxy struct {
24
-	antiReplayCache antireplay.Cache
25
-	clientInit      client.Init
26
-	tg              telegram.Telegram
27
-	conf            *config.Config
22
+	Logger                *zap.SugaredLogger
23
+	ClientProtocolMaker   protocol.ClientProtocolMaker
24
+	TelegramProtocolMaker protocol.TelegramProtocolMaker
25
+	TelegramDialer        telegram.Telegram
28 26
 }
29 27
 
30
-// Serve runs TCP proxy server.
31
-func (p *Proxy) Serve() error {
32
-	lsock, err := net.Listen("tcp", p.conf.BindAddr())
33
-	if err != nil {
34
-		return errors.Annotate(err, "Cannot create listen socket")
35
-	}
36
-
28
+func (p *Proxy) Serve(listener net.Listener) {
37 29
 	for {
38
-		if conn, err := lsock.Accept(); err != nil {
39
-			zap.S().Errorw("Cannot allocate incoming connection", "error", err)
40
-		} else {
41
-			go p.accept(conn)
30
+		conn, err := listener.Accept()
31
+		if err != nil {
32
+			p.Logger.Errorw("Cannot allocate incoming connection", "error", err)
33
+			continue
42 34
 		}
35
+		go p.accept(conn)
43 36
 	}
44 37
 }
45 38
 
46 39
 func (p *Proxy) accept(conn net.Conn) {
47
-	connID := uuid.Must(uuid.NewV4()).String()
48
-	log := zap.S().With("connection_id", connID).Named("main")
49
-	ctx, cancel := context.WithCancel(context.Background())
50
-
51 40
 	defer func() {
52
-		cancel()
53
-		conn.Close() // nolint: errcheck, gosec
54
-
41
+		conn.Close()
55 42
 		if err := recover(); err != nil {
56
-			stats.NewCrash()
57
-			log.Errorw("Crash of accept handler", "error", err)
43
+			stats.S.Crash()
44
+			p.Logger.Errorw("Crash of accept handler", "error", err)
58 45
 		}
59 46
 	}()
60 47
 
61
-	log.Infow("Client connected", "addr", conn.RemoteAddr())
48
+	connID := wrappers.NewConnID()
49
+	logger := p.Logger.With("connection_id", connID)
62 50
 
63
-	clientConn, opts, err := p.clientInit(ctx, cancel, conn, connID, p.antiReplayCache, p.conf)
64
-	if err != nil {
65
-		log.Errorw("Cannot initialize client connection", "error", err)
51
+	if err := utils.InitTCP(conn); err != nil {
52
+		logger.Errorw("Cannot initialize client TCP connection", "error", err)
66 53
 		return
67 54
 	}
68
-	defer clientConn.(io.Closer).Close() // nolint: errcheck
69 55
 
70
-	if p.conf.SecureOnly && opts.ConnectionType != mtproto.ConnectionTypeSecure {
71
-		log.Errorw("Proxy supports only secure connections", "connection_type", opts.ConnectionType)
72
-		return
73
-	}
56
+	ctx, cancel := context.WithCancel(context.Background())
57
+	defer cancel()
74 58
 
75
-	stats.ClientConnected(opts.ConnectionType, clientConn.RemoteAddr())
76
-	defer stats.ClientDisconnected(opts.ConnectionType, clientConn.RemoteAddr())
59
+	wrappedConn := wrappers.NewClientConn(ctx, cancel, conn, connID)
60
+	wrappedConn = wrappers.NewTraffic(wrappedConn)
61
+	defer wrappedConn.Close()
77 62
 
78
-	serverConn, err := p.getTelegramConn(ctx, cancel, opts, connID)
63
+	clientProtocol := p.ClientProtocolMaker()
64
+	wrappedConn, err := clientProtocol.Handshake(wrappedConn)
79 65
 	if err != nil {
80
-		log.Errorw("Cannot initialize server connection", "error", err)
66
+		logger.Warnw("Cannot perform client handshake", "error", err)
81 67
 		return
82 68
 	}
83
-	defer serverConn.(io.Closer).Close() // nolint: errcheck
84
-
85
-	go func() {
86
-		<-ctx.Done()
87
-		serverConn.(io.Closer).Close() // nolint: gosec
88
-		clientConn.(io.Closer).Close() // nolint: gosec
89
-	}()
90
-
91
-	wait := &sync.WaitGroup{}
92
-	wait.Add(2)
69
+	defer wrappedConn.Close()
70
+
71
+	stats.S.ClientConnected(clientProtocol.GetConnectionType(), wrappedConn.RemoteAddr())
72
+	defer stats.S.ClientDisconnected(clientProtocol.GetConnectionType(), wrappedConn.RemoteAddr())
73
+	logger.Infow("Client connected", "addr", conn.RemoteAddr())
74
+
75
+	req := &protocol.TelegramRequest{
76
+		Logger:         logger,
77
+		ClientConn:     wrappedConn,
78
+		ConnID:         connID,
79
+		Ctx:            ctx,
80
+		Cancel:         cancel,
81
+		ClientProtocol: clientProtocol,
82
+	}
93 83
 
94
-	if p.conf.UseMiddleProxy() {
95
-		clientPacket := clientConn.(wrappers.PacketReadWriteCloser)
96
-		serverPacket := serverConn.(wrappers.PacketReadWriteCloser)
97
-		go p.middlePipe(clientPacket, serverPacket, wait, &opts.ReadHacks)
98
-		p.middlePipe(serverPacket, clientPacket, wait, &opts.WriteHacks)
84
+	if len(config.C.AdTag) > 0 {
85
+		err = p.acceptMiddleProxyConnection(req)
99 86
 	} else {
100
-		clientStream := clientConn.(wrappers.StreamReadWriteCloser)
101
-		serverStream := serverConn.(wrappers.StreamReadWriteCloser)
102
-		go p.directPipe(clientStream, serverStream, wait, p.conf.ReadBufferSize)
103
-		p.directPipe(serverStream, clientStream, wait, p.conf.WriteBufferSize)
87
+		err = p.acceptDirectConnection(req)
104 88
 	}
105 89
 
106
-	wait.Wait()
107
-
108
-	log.Infow("Client disconnected", "addr", conn.RemoteAddr())
90
+	logger.Infow("Client disconnected", "error", err, "addr", conn.RemoteAddr())
109 91
 }
110 92
 
111
-func (p *Proxy) getTelegramConn(ctx context.Context, cancel context.CancelFunc,
112
-	opts *mtproto.ConnectionOpts, connID string) (wrappers.Wrap, error) {
113
-	streamConn, err := p.tg.Dial(ctx, cancel, connID, opts)
93
+func (p *Proxy) acceptDirectConnection(request *protocol.TelegramRequest) error {
94
+	telegramProtocol := p.TelegramProtocolMaker(p.TelegramDialer)
95
+	telegramConnRaw, err := telegramProtocol.Handshake(request)
114 96
 	if err != nil {
115
-		return nil, errors.Annotate(err, "Cannot dial to Telegram")
97
+		return err
116 98
 	}
99
+	telegramConn := telegramConnRaw.(wrappers.StreamReadWriteCloser)
100
+	defer telegramConn.Close()
117 101
 
118
-	packetConn, err := p.tg.Init(opts, streamConn)
119
-	if err != nil {
120
-		return nil, errors.Annotate(err, "Cannot handshake telegram")
121
-	}
122
-
123
-	return packetConn, nil
124
-}
102
+	wg := &sync.WaitGroup{}
103
+	wg.Add(2)
125 104
 
126
-func (p *Proxy) middlePipe(src wrappers.PacketReadCloser, dst io.Writer, wait *sync.WaitGroup, hacks *mtproto.Hacks) {
127
-	defer wait.Done()
105
+	go p.directPipe(telegramConn, request.ClientConn, wg, request.Logger)
106
+	go p.directPipe(request.ClientConn, telegramConn, wg, request.Logger)
128 107
 
129
-	for {
130
-		hacks.SimpleAck = false
131
-		hacks.QuickAck = false
108
+	<-request.Ctx.Done()
109
+	wg.Wait()
132 110
 
133
-		packet, err := src.Read()
134
-		if err != nil {
135
-			src.Logger().Warnw("Cannot read packet", "error", err)
136
-			return
137
-		}
138
-		if _, err = dst.Write(packet); err != nil {
139
-			src.Logger().Warnw("Cannot write packet", "error", err)
140
-			return
141
-		}
142
-	}
111
+	return request.Ctx.Err()
143 112
 }
144 113
 
145
-func (p *Proxy) directPipe(src wrappers.StreamReadCloser, dst io.Writer, wait *sync.WaitGroup, bufferSize int) {
146
-	defer wait.Done()
114
+func (p *Proxy) directPipe(dst io.Writer,
115
+	src io.Reader,
116
+	wg *sync.WaitGroup,
117
+	logger *zap.SugaredLogger) {
118
+	defer wg.Done()
147 119
 
148
-	buffer := make([]byte, bufferSize)
149
-	if _, err := io.CopyBuffer(dst, src, buffer); err != nil {
150
-		src.Logger().Warnw("Cannot pump sockets", "error", err)
120
+	buf := make([]byte, directPipeBufferSize)
121
+	if _, err := io.CopyBuffer(dst, src, buf); err != nil {
122
+		logger.Debugw("Cannot pump sockets", "error", err)
151 123
 	}
152 124
 }
153 125
 
154
-// NewProxy returns new proxy instance.
155
-func NewProxy(conf *config.Config) (*Proxy, error) {
156
-	var clientInit client.Init
157
-	var tg telegram.Telegram
158
-
159
-	cache, err := antireplay.NewCache(conf)
160
-	if err != nil {
161
-		return nil, errors.Annotate(err, "Cannot make proxy")
162
-	}
163
-
164
-	if conf.UseMiddleProxy() {
165
-		clientInit = client.MiddleInit
166
-		tg = telegram.NewMiddleTelegram(conf)
167
-	} else {
168
-		clientInit = client.DirectInit
169
-		tg = telegram.NewDirectTelegram(conf)
170
-	}
171
-
172
-	return &Proxy{
173
-		antiReplayCache: cache,
174
-		conf:            conf,
175
-		clientInit:      clientInit,
176
-		tg:              tg,
177
-	}, nil
126
+func (p *Proxy) acceptMiddleProxyConnection(request *protocol.TelegramRequest) error {
127
+	return nil
178 128
 }

+ 54
- 136
stats/stats.go 파일 보기

@@ -1,175 +1,93 @@
1 1
 package stats
2 2
 
3 3
 import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"strconv"
7
-	"time"
4
+	"net"
5
+	"net/http"
8 6
 
9
-	humanize "github.com/dustin/go-humanize"
7
+	"github.com/juju/errors"
10 8
 
11 9
 	"github.com/9seconds/mtg/config"
12
-	"github.com/9seconds/mtg/mtproto"
10
+	"github.com/9seconds/mtg/conntypes"
13 11
 )
14 12
 
15
-type uptime time.Time
16
-
17
-func (u uptime) MarshalJSON() ([]byte, error) {
18
-	duration := time.Since(time.Time(u))
19
-	value := map[string]string{
20
-		"seconds": strconv.Itoa(int(duration.Seconds())),
21
-		"human":   humanize.Time(time.Time(u)),
22
-	}
23
-
24
-	return json.Marshal(value)
25
-}
26
-
27
-type connectionType struct {
28
-	IPv6 uint32 `json:"ipv6"`
29
-	IPv4 uint32 `json:"ipv4"`
30
-}
31
-
32
-type baseConnections struct {
33
-	All          connectionType `json:"all"`
34
-	Abridged     connectionType `json:"abridged"`
35
-	Intermediate connectionType `json:"intermediate"`
36
-	Secure       connectionType `json:"secure"`
13
+type Stats interface {
14
+	IngressTraffic(int)
15
+	EgressTraffic(int)
16
+	ClientConnected(conntypes.ConnectionType, *net.TCPAddr)
17
+	ClientDisconnected(conntypes.ConnectionType, *net.TCPAddr)
18
+	Crash()
19
+	AntiReplayDetected()
37 20
 }
38 21
 
39
-type connections struct {
40
-	baseConnections
41
-}
22
+type multiStats []Stats
42 23
 
43
-func (c connections) MarshalJSON() ([]byte, error) {
44
-	c.All.IPv4 = c.Abridged.IPv4 + c.Intermediate.IPv4 + c.Secure.IPv4
45
-	c.All.IPv6 = c.Abridged.IPv6 + c.Intermediate.IPv6 + c.Secure.IPv6
46
-
47
-	return json.Marshal(c.baseConnections)
48
-}
49
-
50
-type traffic struct {
51
-	ingress uint64
52
-	egress  uint64
53
-}
54
-
55
-func (t *traffic) dumpValue(value uint64) map[string]interface{} {
56
-	return map[string]interface{}{
57
-		"bytes": value,
58
-		"human": humanize.Bytes(value),
24
+func (m multiStats) IngressTraffic(traffic int) {
25
+	for i := range m {
26
+		go m[i].IngressTraffic(traffic)
59 27
 	}
60 28
 }
61 29
 
62
-func (t traffic) MarshalJSON() ([]byte, error) {
63
-	value := map[string]map[string]interface{}{
64
-		"ingress": t.dumpValue(t.ingress),
65
-		"egress":  t.dumpValue(t.egress),
30
+func (m multiStats) EgressTraffic(traffic int) {
31
+	for i := range m {
32
+		go m[i].EgressTraffic(traffic)
66 33
 	}
67
-
68
-	return json.Marshal(value)
69 34
 }
70 35
 
71
-type speed struct {
72
-	ingress uint64
73
-	egress  uint64
74
-}
75
-
76
-func (s *speed) dumpValue(value uint64) map[string]interface{} {
77
-	return map[string]interface{}{
78
-		"bytes/s": value,
79
-		"human":   fmt.Sprintf("%s/s", humanize.Bytes(value)),
36
+func (m multiStats) ClientConnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
37
+	for i := range m {
38
+		go m[i].ClientConnected(connectionType, addr)
80 39
 	}
81 40
 }
82 41
 
83
-func (s speed) MarshalJSON() ([]byte, error) {
84
-	value := map[string]map[string]interface{}{
85
-		"ingress": s.dumpValue(s.ingress),
86
-		"egress":  s.dumpValue(s.egress),
42
+func (m multiStats) ClientDisconnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
43
+	for i := range m {
44
+		go m[i].ClientDisconnected(connectionType, addr)
87 45
 	}
88
-
89
-	return json.Marshal(value)
90 46
 }
91 47
 
92
-// Stats represents a statistics of the proxy.
93
-type Stats struct {
94
-	URLs        config.IPURLs `json:"urls"`
95
-	Connections connections   `json:"connections"`
96
-	Traffic     traffic       `json:"traffic"`
97
-	Speed       speed         `json:"speed"`
98
-	Uptime      uptime        `json:"uptime"`
99
-	Crashes     uint32        `json:"crashes"`
100
-
101
-	previousTraffic traffic
102
-}
103
-
104
-func (s *Stats) start() {
105
-	speedChan := time.Tick(time.Second)
106
-
107
-	for {
108
-		select {
109
-		case <-speedChan:
110
-			s.handleSpeed()
111
-		case event := <-trafficChan:
112
-			s.handleTraffic(event)
113
-		case event := <-connectionsChan:
114
-			s.handleConnection(event)
115
-		case getStatsChan := <-statsChan:
116
-			s.handleGetStats(getStatsChan)
117
-		case <-crashesChan:
118
-			s.handleCrash()
119
-		}
48
+func (m multiStats) Crash() {
49
+	for i := range m {
50
+		go m[i].Crash()
120 51
 	}
121 52
 }
122 53
 
123
-func (s *Stats) handleTraffic(evt trafficData) {
124
-	if evt.ingress {
125
-		s.Traffic.ingress += uint64(evt.traffic)
126
-	} else {
127
-		s.Traffic.egress += uint64(evt.traffic)
54
+func (m multiStats) AntiReplayDetected() {
55
+	for i := range m {
56
+		go m[i].AntiReplayDetected()
128 57
 	}
129 58
 }
130 59
 
131
-func (s *Stats) handleSpeed() {
132
-	s.Speed.ingress = s.Traffic.ingress - s.previousTraffic.ingress
133
-	s.Speed.egress = s.Traffic.egress - s.previousTraffic.egress
134
-	s.previousTraffic.ingress = s.Traffic.ingress
135
-	s.previousTraffic.egress = s.Traffic.egress
136
-}
60
+var S Stats
137 61
 
138
-func (s *Stats) handleConnection(evt connectionData) {
139
-	var inc uint32 = 1
140
-	if !evt.connected {
141
-		inc = ^uint32(0)
62
+func Init() error {
63
+	mux := http.NewServeMux()
64
+
65
+	instanceJSON := newStatsJSON(mux)
66
+	instancePrometheus, err := newStatsPrometheus(mux)
67
+	if err != nil {
68
+		return errors.Annotate(err, "Cannot initialize Prometheus")
142 69
 	}
143 70
 
144
-	var conn *connectionType
145
-	switch evt.connectionType {
146
-	case mtproto.ConnectionTypeAbridged:
147
-		conn = &s.Connections.Abridged
148
-	case mtproto.ConnectionTypeSecure:
149
-		conn = &s.Connections.Secure
150
-	default:
151
-		conn = &s.Connections.Intermediate
71
+	stats := []Stats{instanceJSON, instancePrometheus}
72
+	if config.C.StatsdStats.Addr.IP != nil {
73
+		instanceStatsd, err := newStatsStatsd()
74
+		if err != nil {
75
+			return errors.Annotate(err, "Cannot initialize StatsD")
76
+		}
77
+		stats = append(stats, instanceStatsd)
152 78
 	}
153 79
 
154
-	if evt.addr.IP.To4() != nil {
155
-		conn.IPv4 += inc
156
-	} else {
157
-		conn.IPv6 += inc
80
+	listener, err := net.Listen("tcp", config.C.StatsAddr.String())
81
+	if err != nil {
82
+		return errors.Annotate(err, "Cannot initialize stats server")
158 83
 	}
159
-}
160 84
 
161
-func (s *Stats) handleGetStats(getStatsChan chan<- Stats) {
162
-	getStatsChan <- *s
163
-}
85
+	srv := http.Server{
86
+		Handler: mux,
87
+	}
88
+	go srv.Serve(listener) // nolint: errcheck
164 89
 
165
-func (s *Stats) handleCrash() {
166
-	s.Crashes++
167
-}
90
+	S = multiStats(stats)
168 91
 
169
-// NewStats creates a new instance of Stats structure.
170
-func NewStats(conf *config.Config) *Stats {
171
-	return &Stats{
172
-		URLs:   conf.GetURLs(),
173
-		Uptime: uptime(time.Now()),
174
-	}
92
+	return nil
175 93
 }

newstats/stats_json.go → stats/stats_json.go 파일 보기

@@ -1,15 +1,16 @@
1
-package newstats
1
+package stats
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"net"
6 6
 	"net/http"
7
+	"strconv"
7 8
 	"sync/atomic"
8 9
 	"time"
9 10
 
10 11
 	"go.uber.org/zap"
11 12
 
12
-	"github.com/9seconds/mtg/newprotocol"
13
+	"github.com/9seconds/mtg/conntypes"
13 14
 )
14 15
 
15 16
 type statsJSON struct {
@@ -51,7 +52,8 @@ type statsJSONTraffic struct {
51 52
 type statsJSONUptime time.Time
52 53
 
53 54
 func (s statsJSONUptime) MarshalJSON() ([]byte, error) {
54
-	return json.Marshal(time.Since(time.Time(s)).Seconds())
55
+	seconds := strconv.Itoa(int(time.Since(time.Time(s)).Seconds()))
56
+	return []byte(seconds), nil
55 57
 }
56 58
 
57 59
 func (s *statsJSON) IngressTraffic(traffic int) {
@@ -62,27 +64,27 @@ func (s *statsJSON) EgressTraffic(traffic int) {
62 64
 	atomic.AddUint64(&s.Traffic.Egress, uint64(traffic))
63 65
 }
64 66
 
65
-func (s *statsJSON) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
67
+func (s *statsJSON) ClientConnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
66 68
 	s.changeConnections(connectionType, addr, 1)
67 69
 }
68 70
 
69
-func (s *statsJSON) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
71
+func (s *statsJSON) ClientDisconnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
70 72
 	s.changeConnections(connectionType, addr, ^uint32(0))
71 73
 }
72 74
 
73
-func (s *statsJSON) changeConnections(connectionType newprotocol.ConnectionType, addr *net.TCPAddr, value uint32) {
75
+func (s *statsJSON) changeConnections(connectionType conntypes.ConnectionType, addr *net.TCPAddr, value uint32) {
74 76
 	var connections *statsJSONConnectionType
75 77
 
76 78
 	switch connectionType {
77
-	case newprotocol.ConnectionTypeAbridged:
79
+	case conntypes.ConnectionTypeAbridged:
78 80
 		connections = &s.Connections.Abridged
79
-	case newprotocol.ConnectionTypeSecure:
81
+	case conntypes.ConnectionTypeSecure:
80 82
 		connections = &s.Connections.Secured
81 83
 	default:
82 84
 		connections = &s.Connections.Intermediate
83 85
 	}
84 86
 
85
-	if addr.IP.To4() == nil {
87
+	if addr.IP.To4() != nil {
86 88
 		atomic.AddUint32(&connections.IPv4, value)
87 89
 	} else {
88 90
 		atomic.AddUint32(&connections.IPv6, value)
@@ -98,7 +100,9 @@ func (s *statsJSON) AntiReplayDetected() {
98 100
 }
99 101
 
100 102
 func newStatsJSON(mux *http.ServeMux) Stats {
101
-	instance := &statsJSON{}
103
+	instance := &statsJSON{
104
+		Uptime: statsJSONUptime(time.Now()),
105
+	}
102 106
 	logger := zap.S().Named("stats")
103 107
 
104 108
 	mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {

newstats/stats_prometheus.go → stats/stats_prometheus.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newstats
1
+package stats
2 2
 
3 3
 import (
4 4
 	"net"
@@ -8,8 +8,8 @@ import (
8 8
 	"github.com/prometheus/client_golang/prometheus"
9 9
 	"github.com/prometheus/client_golang/prometheus/promhttp"
10 10
 
11
-	"github.com/9seconds/mtg/newconfig"
12
-	"github.com/9seconds/mtg/newprotocol"
11
+	"github.com/9seconds/mtg/config"
12
+	"github.com/9seconds/mtg/conntypes"
13 13
 )
14 14
 
15 15
 type statsPrometheus struct {
@@ -27,23 +27,23 @@ func (s *statsPrometheus) EgressTraffic(traffic int) {
27 27
 	s.traffic.WithLabelValues("egress").Add(float64(traffic))
28 28
 }
29 29
 
30
-func (s *statsPrometheus) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
30
+func (s *statsPrometheus) ClientConnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
31 31
 	s.changeConnections(connectionType, addr, 1.0)
32 32
 }
33 33
 
34
-func (s *statsPrometheus) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
34
+func (s *statsPrometheus) ClientDisconnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
35 35
 	s.changeConnections(connectionType, addr, -1.0)
36 36
 }
37 37
 
38
-func (s *statsPrometheus) changeConnections(connectionType newprotocol.ConnectionType,
38
+func (s *statsPrometheus) changeConnections(connectionType conntypes.ConnectionType,
39 39
 	addr *net.TCPAddr,
40 40
 	increment float64) {
41 41
 	var labels [2]string
42 42
 
43 43
 	switch connectionType {
44
-	case newprotocol.ConnectionTypeAbridged:
44
+	case conntypes.ConnectionTypeAbridged:
45 45
 		labels[0] = "abridged"
46
-	case newprotocol.ConnectionTypeSecure:
46
+	case conntypes.ConnectionTypeSecure:
47 47
 		labels[0] = "secured"
48 48
 	default:
49 49
 		labels[0] = "intermediate"
@@ -69,22 +69,22 @@ func newStatsPrometheus(mux *http.ServeMux) (Stats, error) {
69 69
 	registry := prometheus.NewRegistry()
70 70
 	instance := &statsPrometheus{
71 71
 		connections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
72
-			Namespace: newconfig.C.PrometheusStats.Prefix,
72
+			Namespace: config.C.PrometheusStats.Prefix,
73 73
 			Name:      "connections",
74 74
 			Help:      "Current number of connections to the proxy.",
75 75
 		}, []string{"type", "protocol"}),
76 76
 		traffic: prometheus.NewGaugeVec(prometheus.GaugeOpts{
77
-			Namespace: newconfig.C.PrometheusStats.Prefix,
77
+			Namespace: config.C.PrometheusStats.Prefix,
78 78
 			Name:      "traffic",
79 79
 			Help:      "Traffic passed through the proxy in bytes.",
80 80
 		}, []string{"direction"}),
81 81
 		crashes: prometheus.NewGauge(prometheus.GaugeOpts{
82
-			Namespace: newconfig.C.PrometheusStats.Prefix,
82
+			Namespace: config.C.PrometheusStats.Prefix,
83 83
 			Name:      "crashes",
84 84
 			Help:      "How many crashes happened.",
85 85
 		}),
86 86
 		antiReplays: prometheus.NewGauge(prometheus.GaugeOpts{
87
-			Namespace: newconfig.C.PrometheusStats.Prefix,
87
+			Namespace: config.C.PrometheusStats.Prefix,
88 88
 			Name:      "anti_replays",
89 89
 			Help:      "How many anti replay attacks were prevented.",
90 90
 		}),

newstats/stats_statsd.go → stats/stats_statsd.go 파일 보기

@@ -1,14 +1,14 @@
1
-package newstats
1
+package stats
2 2
 
3 3
 import (
4 4
 	"net"
5 5
 	"strings"
6 6
 
7
+	"github.com/juju/errors"
7 8
 	"gopkg.in/alexcesaro/statsd.v2"
8 9
 
9
-	"github.com/9seconds/mtg/newconfig"
10
-	"github.com/9seconds/mtg/newprotocol"
11
-	"github.com/juju/errors"
10
+	"github.com/9seconds/mtg/config"
11
+	"github.com/9seconds/mtg/conntypes"
12 12
 )
13 13
 
14 14
 type statsStatsd struct {
@@ -23,22 +23,22 @@ func (s *statsStatsd) EgressTraffic(traffic int) {
23 23
 	s.client.Count("traffic.egress", traffic)
24 24
 }
25 25
 
26
-func (s *statsStatsd) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
26
+func (s *statsStatsd) ClientConnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
27 27
 	s.changeConnections(connectionType, addr, 1)
28 28
 }
29 29
 
30
-func (s *statsStatsd) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
30
+func (s *statsStatsd) ClientDisconnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
31 31
 	s.changeConnections(connectionType, addr, -1)
32 32
 }
33 33
 
34
-func (s *statsStatsd) changeConnections(connectionType newprotocol.ConnectionType, addr *net.TCPAddr, value int) {
34
+func (s *statsStatsd) changeConnections(connectionType conntypes.ConnectionType, addr *net.TCPAddr, value int) {
35 35
 	var labels [3]string
36 36
 
37 37
 	labels[0] = "connections"
38 38
 	switch connectionType {
39
-	case newprotocol.ConnectionTypeAbridged:
39
+	case conntypes.ConnectionTypeAbridged:
40 40
 		labels[1] = "abridged"
41
-	case newprotocol.ConnectionTypeSecure:
41
+	case conntypes.ConnectionTypeSecure:
42 42
 		labels[1] = "secured"
43 43
 	default:
44 44
 		labels[1] = "intermediate"
@@ -62,15 +62,15 @@ func (s *statsStatsd) AntiReplayDetected() {
62 62
 
63 63
 func newStatsStatsd() (Stats, error) {
64 64
 	options := []statsd.Option{
65
-		statsd.Prefix(newconfig.C.StatsdStats.Prefix),
66
-		statsd.Network(newconfig.C.StatsdStats.Addr.Network()),
67
-		statsd.Address(newconfig.C.StatsdStats.Addr.String()),
68
-		statsd.TagsFormat(newconfig.C.StatsdStats.TagsFormat),
65
+		statsd.Prefix(config.C.StatsdStats.Prefix),
66
+		statsd.Network(config.C.StatsdStats.Addr.Network()),
67
+		statsd.Address(config.C.StatsdStats.Addr.String()),
68
+		statsd.TagsFormat(config.C.StatsdStats.TagsFormat),
69 69
 	}
70 70
 
71
-	if len(newconfig.C.StatsdStats.Tags) > 0 {
72
-		tags := make([]string, len(newconfig.C.StatsdStats.Tags)*2)
73
-		for k, v := range newconfig.C.StatsdStats.Tags {
71
+	if len(config.C.StatsdStats.Tags) > 0 {
72
+		tags := make([]string, len(config.C.StatsdStats.Tags)*2)
73
+		for k, v := range config.C.StatsdStats.Tags {
74 74
 			tags = append(tags, k, v)
75 75
 		}
76 76
 		options = append(options, statsd.Tags(tags...))

+ 70
- 0
telegram/base.go 파일 보기

@@ -0,0 +1,70 @@
1
+package telegram
2
+
3
+import (
4
+	"context"
5
+	"math/rand"
6
+	"net"
7
+	"time"
8
+
9
+	"github.com/juju/errors"
10
+
11
+	"github.com/9seconds/mtg/conntypes"
12
+	"github.com/9seconds/mtg/utils"
13
+	"github.com/9seconds/mtg/wrappers"
14
+)
15
+
16
+const telegramDialTimeout = 10 * time.Second
17
+
18
+type baseTelegram struct {
19
+	dialer net.Dialer
20
+
21
+	v4DefaultDC conntypes.DC
22
+	V6DefaultDC conntypes.DC
23
+	v4Addresses map[conntypes.DC][]string
24
+	v6Addresses map[conntypes.DC][]string
25
+}
26
+
27
+func (b *baseTelegram) dialToAddress(ctx context.Context,
28
+	cancel context.CancelFunc,
29
+	addr string) (wrappers.StreamReadWriteCloser, error) {
30
+	conn, err := b.dialer.Dial("tcp", addr)
31
+	if err != nil {
32
+		return nil, errors.Annotate(err, "Dial has failed")
33
+	}
34
+
35
+	if err := utils.InitTCP(conn); err != nil {
36
+		return nil, errors.Annotate(err, "Cannot initialize TCP socket")
37
+	}
38
+
39
+	return wrappers.NewTelegramConn(ctx, cancel, conn), nil
40
+}
41
+
42
+func (b *baseTelegram) dial(ctx context.Context,
43
+	cancel context.CancelFunc,
44
+	dc conntypes.DC,
45
+	protocol conntypes.ConnectionProtocol) (wrappers.StreamReadWriteCloser, error) {
46
+	addr := ""
47
+
48
+	switch protocol {
49
+	case conntypes.ConnectionProtocolIPv4:
50
+		addr = b.chooseAddress(b.v4Addresses, dc, b.v4DefaultDC)
51
+	default:
52
+		addr = b.chooseAddress(b.v6Addresses, dc, b.V6DefaultDC)
53
+	}
54
+
55
+	return b.dialToAddress(ctx, cancel, addr)
56
+}
57
+
58
+func (b *baseTelegram) chooseAddress(addresses map[conntypes.DC][]string,
59
+	dc, defaultDC conntypes.DC) string {
60
+	addrs, ok := addresses[dc]
61
+	if !ok {
62
+		addrs, _ = addresses[defaultDC]
63
+	}
64
+
65
+	if len(addrs) > 0 {
66
+		return addrs[rand.Intn(len(addrs))]
67
+	}
68
+
69
+	return ""
70
+}

+ 20
- 38
telegram/direct.go 파일 보기

@@ -4,28 +4,24 @@ import (
4 4
 	"context"
5 5
 	"net"
6 6
 
7
-	"github.com/juju/errors"
8
-
9
-	"github.com/9seconds/mtg/config"
10
-	"github.com/9seconds/mtg/mtproto"
11
-	"github.com/9seconds/mtg/obfuscated2"
7
+	"github.com/9seconds/mtg/conntypes"
12 8
 	"github.com/9seconds/mtg/wrappers"
13 9
 )
14 10
 
15 11
 const (
16
-	directV4DefaultIdx = 1
17
-	directV6DefaultIdx = 1
12
+	directV4DefaultIdx conntypes.DC = 1
13
+	directV6DefaultIdx conntypes.DC = 1
18 14
 )
19 15
 
20 16
 var (
21
-	directV4Addresses = map[int16][]string{
17
+	directV4Addresses = map[conntypes.DC][]string{
22 18
 		0: {"149.154.175.50:443"},
23 19
 		1: {"149.154.167.51:443"},
24 20
 		2: {"149.154.175.100:443"},
25 21
 		3: {"149.154.167.91:443"},
26 22
 		4: {"149.154.171.5:443"},
27 23
 	}
28
-	directV6Addresses = map[int16][]string{
24
+	directV6Addresses = map[conntypes.DC][]string{
29 25
 		0: {"[2001:b28:f23d:f001::a]:443"},
30 26
 		1: {"[2001:67c:04e8:f002::a]:443"},
31 27
 		2: {"[2001:b28:f23d:f003::a]:443"},
@@ -38,42 +34,28 @@ type directTelegram struct {
38 34
 	baseTelegram
39 35
 }
40 36
 
41
-func (t *directTelegram) Dial(ctx context.Context, cancel context.CancelFunc,
42
-	connID string, connOpts *mtproto.ConnectionOpts) (wrappers.StreamReadWriteCloser, error) {
43
-	dc := connOpts.DC
44
-	if dc < 0 {
37
+func (d *directTelegram) Dial(ctx context.Context,
38
+	cancel context.CancelFunc,
39
+	dc conntypes.DC,
40
+	protocol conntypes.ConnectionProtocol) (wrappers.StreamReadWriteCloser, error) {
41
+	switch {
42
+	case dc < 0:
45 43
 		dc = -dc
46
-	} else if dc == 0 {
47
-		dc = 1
48
-	}
49
-
50
-	return t.baseTelegram.dial(ctx, cancel, dc-1, connID, connOpts.ConnectionProto)
51
-}
52
-
53
-func (t *directTelegram) Init(connOpts *mtproto.ConnectionOpts,
54
-	conn wrappers.StreamReadWriteCloser) (wrappers.Wrap, error) {
55
-	obfs2, frame := obfuscated2.MakeTelegramObfuscated2Frame(connOpts)
56
-
57
-	if _, err := conn.Write(frame); err != nil {
58
-		return nil, errors.Annotate(err, "Cannot write hadnshake frame")
44
+	case dc == 0:
45
+		dc = conntypes.DCDefaultIdx
59 46
 	}
60 47
 
61
-	return wrappers.NewStreamCipher(conn, obfs2.Encryptor, obfs2.Decryptor), nil
48
+	return d.baseTelegram.dial(ctx, cancel, dc-1, protocol)
62 49
 }
63 50
 
64
-// NewDirectTelegram returns Telegram instance which connects directly
65
-// to Telegram bypassing middleproxies.
66
-func NewDirectTelegram(conf *config.Config) Telegram {
51
+func NewDirectTelegram() Telegram {
67 52
 	return &directTelegram{
68 53
 		baseTelegram: baseTelegram{
69
-			dialer: tgDialer{
70
-				Dialer: net.Dialer{Timeout: telegramDialTimeout},
71
-				conf:   conf,
72
-			},
73
-			v4DefaultIdx: directV4DefaultIdx,
74
-			v6DefaultIdx: directV6DefaultIdx,
75
-			v4Addresses:  directV4Addresses,
76
-			v6Addresses:  directV6Addresses,
54
+			dialer:      net.Dialer{Timeout: telegramDialTimeout},
55
+			v4DefaultDC: directV4DefaultIdx,
56
+			V6DefaultDC: directV6DefaultIdx,
57
+			v4Addresses: directV4Addresses,
58
+			v6Addresses: directV6Addresses,
77 59
 		},
78 60
 	}
79 61
 }

+ 15
- 0
telegram/interfaces.go 파일 보기

@@ -0,0 +1,15 @@
1
+package telegram
2
+
3
+import (
4
+	"context"
5
+
6
+	"github.com/9seconds/mtg/conntypes"
7
+	"github.com/9seconds/mtg/wrappers"
8
+)
9
+
10
+type Telegram interface {
11
+	Dial(context.Context,
12
+		context.CancelFunc,
13
+		conntypes.DC,
14
+		conntypes.ConnectionProtocol) (wrappers.StreamReadWriteCloser, error)
15
+}

+ 25
- 0
utils/init_tcp.go 파일 보기

@@ -0,0 +1,25 @@
1
+package utils
2
+
3
+import (
4
+	"net"
5
+
6
+	"github.com/juju/errors"
7
+
8
+	"github.com/9seconds/mtg/config"
9
+)
10
+
11
+func InitTCP(conn net.Conn) error {
12
+	tcpConn := conn.(*net.TCPConn)
13
+
14
+	if err := tcpConn.SetNoDelay(true); err != nil {
15
+		return errors.Annotate(err, "Cannot set NO_DELAY")
16
+	}
17
+	if err := tcpConn.SetReadBuffer(config.C.BufferSize.Read); err != nil {
18
+		return errors.Annotate(err, "Cannot set read buffer size")
19
+	}
20
+	if err := tcpConn.SetWriteBuffer(config.C.BufferSize.Write); err != nil {
21
+		return errors.Annotate(err, "Cannot set write buffer size")
22
+	}
23
+
24
+	return nil
25
+}

+ 11
- 0
utils/stream_cipher.go 파일 보기

@@ -0,0 +1,11 @@
1
+package utils
2
+
3
+import (
4
+	"crypto/aes"
5
+	"crypto/cipher"
6
+)
7
+
8
+func MakeStreamCipher(key, iv []byte) cipher.Stream {
9
+	block, _ := aes.NewCipher(key) // nolint: gosec
10
+	return cipher.NewCTR(block, iv)
11
+}

+ 0
- 4
utils/uint24.go 파일 보기

@@ -1,15 +1,11 @@
1 1
 package utils
2 2
 
3
-// Uint24 is a replacement for the absent Go uint24 data type.
4
-// This data type is little endian.
5 3
 type Uint24 [3]byte
6 4
 
7
-// ToUint24 converts number to Uint24.
8 5
 func ToUint24(number uint32) Uint24 {
9 6
 	return Uint24{byte(number), byte(number >> 8), byte(number >> 16)}
10 7
 }
11 8
 
12
-// FromUint24 converts Uint24 to number.
13 9
 func FromUint24(number Uint24) uint32 {
14 10
 	return uint32(number[0]) + (uint32(number[1]) << 8) + (uint32(number[2]) << 16)
15 11
 }

newwrappers/interfaces.go → wrappers/interfaces.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newwrappers
1
+package wrappers
2 2
 
3 3
 import (
4 4
 	"io"

newwrappers/wrapper_conn.go → wrappers/wrapper_conn.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newwrappers
1
+package wrappers
2 2
 
3 3
 import (
4 4
 	"context"
@@ -10,7 +10,7 @@ import (
10 10
 	"github.com/juju/errors"
11 11
 	"go.uber.org/zap"
12 12
 
13
-	"github.com/9seconds/mtg/newconfig"
13
+	"github.com/9seconds/mtg/config"
14 14
 )
15 15
 
16 16
 const ConnIDLength = 8
@@ -126,11 +126,11 @@ func newConn(ctx context.Context,
126 126
 	localAddr := *parent.LocalAddr().(*net.TCPAddr)
127 127
 
128 128
 	if parent.RemoteAddr().(*net.TCPAddr).IP.To4() != nil {
129
-		if newconfig.C.PublicIPv4Addr.IP != nil {
130
-			localAddr.IP = newconfig.C.PublicIPv4Addr.IP
129
+		if config.C.PublicIPv4Addr.IP != nil {
130
+			localAddr.IP = config.C.PublicIPv4Addr.IP
131 131
 		}
132
-	} else if newconfig.C.PublicIPv6Addr.IP != nil {
133
-		localAddr.IP = newconfig.C.PublicIPv6Addr.IP
132
+	} else if config.C.PublicIPv6Addr.IP != nil {
133
+		localAddr.IP = config.C.PublicIPv6Addr.IP
134 134
 	}
135 135
 
136 136
 	logger := zap.S().With(
@@ -162,9 +162,8 @@ func NewClientConn(ctx context.Context,
162 162
 
163 163
 func NewTelegramConn(ctx context.Context,
164 164
 	cancel context.CancelFunc,
165
-	parent net.Conn,
166
-	connID ConnID) StreamReadWriteCloser {
167
-	return newConn(ctx, cancel, parent, connID, connPurposeTelegram)
165
+	parent net.Conn) StreamReadWriteCloser {
166
+	return newConn(ctx, cancel, parent, ConnID{}, connPurposeTelegram)
168 167
 }
169 168
 
170 169
 func NewConnID() ConnID {

newwrappers/wrapper_obfuscated2.go → wrappers/wrapper_obfuscated2.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newwrappers
1
+package wrappers
2 2
 
3 3
 import (
4 4
 	"crypto/cipher"

newwrappers/wrapper_stats.go → wrappers/wrapper_stats.go 파일 보기

@@ -1,4 +1,4 @@
1
-package newwrappers
1
+package wrappers
2 2
 
3 3
 import (
4 4
 	"net"
@@ -6,7 +6,7 @@ import (
6 6
 
7 7
 	"go.uber.org/zap"
8 8
 
9
-	"github.com/9seconds/mtg/newstats"
9
+	"github.com/9seconds/mtg/stats"
10 10
 )
11 11
 
12 12
 type wrapperStats struct {
@@ -15,28 +15,28 @@ type wrapperStats struct {
15 15
 
16 16
 func (w *wrapperStats) Write(p []byte) (int, error) {
17 17
 	n, err := w.parent.Write(p)
18
-	newstats.S.EgressTraffic(n)
18
+	stats.S.EgressTraffic(n)
19 19
 
20 20
 	return n, err
21 21
 }
22 22
 
23 23
 func (w *wrapperStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
24 24
 	n, err := w.parent.WriteTimeout(p, timeout)
25
-	newstats.S.EgressTraffic(n)
25
+	stats.S.EgressTraffic(n)
26 26
 
27 27
 	return n, err
28 28
 }
29 29
 
30 30
 func (w *wrapperStats) Read(p []byte) (int, error) {
31 31
 	n, err := w.parent.Read(p)
32
-	newstats.S.IngressTraffic(n)
32
+	stats.S.IngressTraffic(n)
33 33
 
34 34
 	return n, err
35 35
 }
36 36
 
37 37
 func (w *wrapperStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
38 38
 	n, err := w.parent.ReadTimeout(p, timeout)
39
-	newstats.S.IngressTraffic(n)
39
+	stats.S.IngressTraffic(n)
40 40
 
41 41
 	return n, err
42 42
 }

Loading…
취소
저장