Browse Source

Reworked base

tags/1.0^2
9seconds 6 years ago
parent
commit
07985cf418

+ 2
- 0
go.mod View File

15
 	github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
15
 	github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
16
 	github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect
16
 	github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect
17
 	github.com/kr/pretty v0.1.0 // indirect
17
 	github.com/kr/pretty v0.1.0 // indirect
18
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
19
+	github.com/modern-go/reflect2 v1.0.1 // indirect
18
 	github.com/pkg/errors v0.8.1 // indirect
20
 	github.com/pkg/errors v0.8.1 // indirect
19
 	github.com/prometheus/client_golang v1.1.0
21
 	github.com/prometheus/client_golang v1.1.0
20
 	github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
22
 	github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect

+ 76
- 128
main.go View File

1
 package main
1
 package main
2
 
2
 
3
 import (
3
 import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"io"
7
 	"math/rand"
4
 	"math/rand"
8
 	"os"
5
 	"os"
9
 	"syscall"
6
 	"syscall"
10
 	"time"
7
 	"time"
11
 
8
 
12
 	"github.com/juju/errors"
9
 	"github.com/juju/errors"
13
-	"go.uber.org/zap"
14
-	"go.uber.org/zap/zapcore"
15
 	kingpin "gopkg.in/alecthomas/kingpin.v2"
10
 	kingpin "gopkg.in/alecthomas/kingpin.v2"
16
 
11
 
17
-	"github.com/9seconds/mtg/config"
18
-	"github.com/9seconds/mtg/ntp"
19
-	"github.com/9seconds/mtg/proxy"
20
-	"github.com/9seconds/mtg/stats"
12
+	"github.com/9seconds/mtg/newcli"
13
+	"github.com/9seconds/mtg/newconfig"
21
 )
14
 )
22
 
15
 
23
 var version = "dev" // this has to be set by build ld flags
16
 var version = "dev" // this has to be set by build ld flags
25
 var (
18
 var (
26
 	app = kingpin.New("mtg", "Simple MTPROTO proxy.")
19
 	app = kingpin.New("mtg", "Simple MTPROTO proxy.")
27
 
20
 
28
-	debug = app.Flag("debug",
21
+	generateSecretCommand = app.Command("generate-secret",
22
+		"Generate new secret")
23
+	generateSecretType = generateSecretCommand.Arg("type",
24
+		"A type of secret to generate. Valid options are 'simple', 'secured' and 'tls'").
25
+		Required().
26
+		Enum("simple", "secured", "tls")
27
+
28
+	proxyCommand = app.Command("proxy",
29
+		"Run new proxy instance")
30
+	proxyDebug = proxyCommand.Flag("debug",
29
 		"Run in debug mode.").
31
 		"Run in debug mode.").
30
 		Short('d').
32
 		Short('d').
31
 		Envar("MTG_DEBUG").
33
 		Envar("MTG_DEBUG").
32
 		Bool()
34
 		Bool()
33
-	verbose = app.Flag("verbose",
35
+	proxyVerbose = proxyCommand.Flag("verbose",
34
 		"Run in verbose mode.").
36
 		"Run in verbose mode.").
35
 		Short('v').
37
 		Short('v').
36
 		Envar("MTG_VERBOSE").
38
 		Envar("MTG_VERBOSE").
37
 		Bool()
39
 		Bool()
38
-
39
-	bindIP = app.Flag("bind-ip",
40
+	proxyBindIP = proxyCommand.Flag("bind-ip",
40
 		"Which IP to bind to.").
41
 		"Which IP to bind to.").
41
 		Short('b').
42
 		Short('b').
42
 		Envar("MTG_IP").
43
 		Envar("MTG_IP").
43
 		Default("127.0.0.1").
44
 		Default("127.0.0.1").
44
 		IP()
45
 		IP()
45
-	bindPort = app.Flag("bind-port",
46
+	proxyBindPort = proxyCommand.Flag("bind-port",
46
 		"Which port to bind to.").
47
 		"Which port to bind to.").
47
 		Short('p').
48
 		Short('p').
48
 		Envar("MTG_PORT").
49
 		Envar("MTG_PORT").
49
 		Default("3128").
50
 		Default("3128").
50
 		Uint16()
51
 		Uint16()
51
-
52
-	publicIPv4 = app.Flag("public-ipv4",
52
+	proxyPublicIPv4 = proxyCommand.Flag("public-ipv4",
53
 		"Which IPv4 address is public.").
53
 		"Which IPv4 address is public.").
54
 		Short('4').
54
 		Short('4').
55
 		Envar("MTG_IPV4").
55
 		Envar("MTG_IPV4").
56
 		IP()
56
 		IP()
57
-	publicIPv4Port = app.Flag("public-ipv4-port",
57
+	proxyPublicIPv4Port = proxyCommand.Flag("public-ipv4-port",
58
 		"Which IPv4 port is public. Default is 'bind-port' value.").
58
 		"Which IPv4 port is public. Default is 'bind-port' value.").
59
 		Envar("MTG_IPV4_PORT").
59
 		Envar("MTG_IPV4_PORT").
60
 		Uint16()
60
 		Uint16()
61
-
62
-	publicIPv6 = app.Flag("public-ipv6",
61
+	proxyPublicIPv6 = proxyCommand.Flag("public-ipv6",
63
 		"Which IPv6 address is public.").
62
 		"Which IPv6 address is public.").
64
 		Short('6').
63
 		Short('6').
65
 		Envar("MTG_IPV6").
64
 		Envar("MTG_IPV6").
66
 		IP()
65
 		IP()
67
-	publicIPv6Port = app.Flag("public-ipv6-port",
66
+	proxyPublicIPv6Port = proxyCommand.Flag("public-ipv6-port",
68
 		"Which IPv6 port is public. Default is 'bind-port' value.").
67
 		"Which IPv6 port is public. Default is 'bind-port' value.").
69
 		Envar("MTG_IPV6_PORT").
68
 		Envar("MTG_IPV6_PORT").
70
 		Uint16()
69
 		Uint16()
71
-
72
-	statsIP = app.Flag("stats-ip",
70
+	proxyStatsIP = proxyCommand.Flag("stats-ip",
73
 		"Which IP bind stats server to.").
71
 		"Which IP bind stats server to.").
74
 		Short('t').
72
 		Short('t').
75
 		Envar("MTG_STATS_IP").
73
 		Envar("MTG_STATS_IP").
76
 		Default("127.0.0.1").
74
 		Default("127.0.0.1").
77
 		IP()
75
 		IP()
78
-	statsPort = app.Flag("stats-port",
76
+	proxyStatsPort = proxyCommand.Flag("stats-port",
79
 		"Which port bind stats to.").
77
 		"Which port bind stats to.").
80
 		Short('q').
78
 		Short('q').
81
 		Envar("MTG_STATS_PORT").
79
 		Envar("MTG_STATS_PORT").
82
 		Default("3129").
80
 		Default("3129").
83
 		Uint16()
81
 		Uint16()
84
-
85
-	statsdIP = app.Flag("statsd-ip",
82
+	proxyStatsdIP = proxyCommand.Flag("statsd-ip",
86
 		"Which IP should we use for working with statsd.").
83
 		"Which IP should we use for working with statsd.").
87
 		Envar("MTG_STATSD_IP").
84
 		Envar("MTG_STATSD_IP").
88
-		String()
89
-	statsdPort = app.Flag("statsd-port",
85
+		IP()
86
+	proxyStatsdPort = proxyCommand.Flag("statsd-port",
90
 		"Which port should we use for working with statsd.").
87
 		"Which port should we use for working with statsd.").
91
 		Envar("MTG_STATSD_PORT").
88
 		Envar("MTG_STATSD_PORT").
92
 		Default("8125").
89
 		Default("8125").
93
 		Uint16()
90
 		Uint16()
94
-	statsdNetwork = app.Flag("statsd-network",
91
+	proxyStatsdNetwork = proxyCommand.Flag("statsd-network",
95
 		"Which network is used to work with statsd. Only 'tcp' and 'udp' are supported.").
92
 		"Which network is used to work with statsd. Only 'tcp' and 'udp' are supported.").
96
 		Envar("MTG_STATSD_NETWORK").
93
 		Envar("MTG_STATSD_NETWORK").
97
 		Default("udp").
94
 		Default("udp").
98
-		String()
99
-	statsdPrefix = app.Flag("statsd-prefix",
95
+		Enum("udp", "tcp")
96
+	proxyStatsdPrefix = proxyCommand.Flag("statsd-prefix",
100
 		"Which bucket prefix should we use for sending stats to statsd.").
97
 		"Which bucket prefix should we use for sending stats to statsd.").
101
 		Envar("MTG_STATSD_PREFIX").
98
 		Envar("MTG_STATSD_PREFIX").
102
 		Default("mtg").
99
 		Default("mtg").
103
 		String()
100
 		String()
104
-	statsdTagsFormat = app.Flag("statsd-tags-format",
101
+	proxyStatsdTagsFormat = proxyCommand.Flag("statsd-tags-format",
105
 		"Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
102
 		"Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
106
 		Envar("MTG_STATSD_TAGS_FORMAT").
103
 		Envar("MTG_STATSD_TAGS_FORMAT").
107
-		String()
108
-	statsdTags = app.Flag("statsd-tags",
104
+		Default("influxdb").
105
+		Enum("datadog", "influxdb")
106
+	proxyStatsdTags = proxyCommand.Flag("statsd-tags",
109
 		"Tags to use for working with statsd (specified as 'key=value').").
107
 		"Tags to use for working with statsd (specified as 'key=value').").
110
 		Envar("MTG_STATSD_TAGS").
108
 		Envar("MTG_STATSD_TAGS").
111
 		StringMap()
109
 		StringMap()
112
-
113
-	prometheusPrefix = app.Flag("prometheus-prefix",
110
+	proxyPrometheusPrefix = proxyCommand.Flag("prometheus-prefix",
114
 		"Which namespace to use to send stats to Prometheus.").
111
 		"Which namespace to use to send stats to Prometheus.").
115
 		Envar("MTG_PROMETHEUS_PREFIX").
112
 		Envar("MTG_PROMETHEUS_PREFIX").
116
 		Default("mtg").
113
 		Default("mtg").
117
 		String()
114
 		String()
118
-
119
-	writeBufferSize = app.Flag("write-buffer",
115
+	proxyWriteBufferSize = proxyCommand.Flag("write-buffer",
120
 		"Write buffer size in bytes. You can think about it as a buffer from client to Telegram.").
116
 		"Write buffer size in bytes. You can think about it as a buffer from client to Telegram.").
121
 		Short('w').
117
 		Short('w').
122
 		Envar("MTG_BUFFER_WRITE").
118
 		Envar("MTG_BUFFER_WRITE").
123
 		Default("65536").
119
 		Default("65536").
124
 		Uint32()
120
 		Uint32()
125
-	readBufferSize = app.Flag("read-buffer",
121
+	proxyReadBufferSize = proxyCommand.Flag("read-buffer",
126
 		"Read buffer size in bytes. You can think about it as a buffer from Telegram to client.").
122
 		"Read buffer size in bytes. You can think about it as a buffer from Telegram to client.").
127
 		Short('r').
123
 		Short('r').
128
 		Envar("MTG_BUFFER_READ").
124
 		Envar("MTG_BUFFER_READ").
129
 		Default("131072").
125
 		Default("131072").
130
 		Uint32()
126
 		Uint32()
131
-	secureOnly = app.Flag("secure-only",
132
-		"Support clients with dd-secrets only.").
133
-		Short('s').
134
-		Envar("MTG_SECURE_ONLY").
135
-		Bool()
136
-
137
-	antiReplayMaxSize = app.Flag("anti-replay-max-size",
127
+	proxyAntiReplayMaxSize = proxyCommand.Flag("anti-replay-max-size",
138
 		"Max size of antireplay cache in megabytes.").
128
 		"Max size of antireplay cache in megabytes.").
139
 		Envar("MTG_ANTIREPLAY_MAXSIZE").
129
 		Envar("MTG_ANTIREPLAY_MAXSIZE").
140
 		Default("128").
130
 		Default("128").
141
 		Int()
131
 		Int()
142
-	antiReplayEvictionTime = app.Flag("anti-replay-eviction-time",
132
+	proxyAntiReplayEvictionTime = proxyCommand.Flag("anti-replay-eviction-time",
143
 		"Eviction time period for obfuscated2 handshakes").
133
 		"Eviction time period for obfuscated2 handshakes").
144
 		Envar("MTG_ANTIREPLAY_EVICTIONTIME").
134
 		Envar("MTG_ANTIREPLAY_EVICTIONTIME").
145
 		Default("168h").
135
 		Default("168h").
146
 		Duration()
136
 		Duration()
147
-
148
-	secret = app.Arg("secret", "Secret of this proxy.").Required().HexBytes()
149
-	adtag  = app.Arg("adtag", "ADTag of the proxy.").HexBytes()
137
+	proxySecret = proxyCommand.Arg("secret", "Secret of this proxy.").Required().HexBytes()
138
+	proxyAdtag  = proxyCommand.Arg("adtag", "ADTag of the proxy.").HexBytes()
150
 )
139
 )
151
 
140
 
152
-func main() { // nolint: gocyclo
141
+func main() {
153
 	rand.Seed(time.Now().UTC().UnixNano())
142
 	rand.Seed(time.Now().UTC().UnixNano())
154
 	app.Version(version)
143
 	app.Version(version)
155
 	app.HelpFlag.Short('h')
144
 	app.HelpFlag.Short('h')
156
 
145
 
157
-	kingpin.MustParse(app.Parse(os.Args[1:]))
158
-
159
-	err := setRLimit()
160
-	if err != nil {
161
-		usage(err.Error())
162
-	}
163
-
164
-	conf, err := config.NewConfig(*debug, *verbose,
165
-		*writeBufferSize, *readBufferSize,
166
-		*bindIP, *publicIPv4, *publicIPv6, *statsIP,
167
-		*bindPort, *publicIPv4Port, *publicIPv6Port, *statsPort, *statsdPort,
168
-		*statsdIP, *statsdNetwork, *statsdPrefix, *statsdTagsFormat,
169
-		*statsdTags, *prometheusPrefix, *secureOnly,
170
-		*antiReplayMaxSize, *antiReplayEvictionTime,
171
-		*secret, *adtag,
172
-	)
173
-	if err != nil {
174
-		usage(err.Error())
146
+	if err := setRLimit(); err != nil {
147
+		newcli.Fatal(err.Error())
175
 	}
148
 	}
176
 
149
 
177
-	atom := zap.NewAtomicLevel()
178
-	switch {
179
-	case conf.Debug:
180
-		atom.SetLevel(zapcore.DebugLevel)
181
-	case conf.Verbose:
182
-		atom.SetLevel(zapcore.InfoLevel)
183
-	default:
184
-		atom.SetLevel(zapcore.ErrorLevel)
185
-	}
186
-	encoderCfg := zap.NewProductionEncoderConfig()
187
-	logger := zap.New(zapcore.NewCore(
188
-		zapcore.NewJSONEncoder(encoderCfg),
189
-		zapcore.Lock(os.Stderr),
190
-		atom,
191
-	))
192
-	zap.ReplaceGlobals(logger)
193
-	defer logger.Sync() // nolint: errcheck
194
-
195
-	printURLs(conf.GetURLs())
196
-	zap.S().Debugw("Configuration", "config", conf)
197
-
198
-	if conf.UseMiddleProxy() {
199
-		zap.S().Infow("Use middle proxy connection to Telegram")
200
-		if diff, err := ntp.Fetch(); err != nil {
201
-			zap.S().Warnw("Could not fetch time data from NTP")
202
-		} else {
203
-			if diff >= time.Second {
204
-				usage(fmt.Sprintf("You choose to use middle proxy but your clock drift (%s) "+
205
-					"is bigger than 1 second. Please, sync your time", diff))
206
-			}
207
-			go ntp.AutoUpdate()
150
+	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
151
+	case generateSecretCommand.FullCommand():
152
+		newcli.Generate(*generateSecretType)
153
+
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},
179
+		)
180
+		if err != nil {
181
+			newcli.Fatal(err.Error())
208
 		}
182
 		}
209
-	} else {
210
-		zap.S().Infow("Use direct connection to Telegram")
211
-	}
212
-
213
-	if err := stats.Init(conf); err != nil {
214
-		panic(err)
215
-	}
216
 
183
 
217
-	server, err := proxy.NewProxy(conf)
218
-	if err != nil {
219
-		panic(err)
220
-	}
221
-	if err := server.Serve(); err != nil {
222
-		zap.S().Fatalw("Server stopped", "error", err)
184
+		if err := newcli.Proxy(); err != nil {
185
+			newcli.Fatal(err.Error())
186
+		}
223
 	}
187
 	}
224
 }
188
 }
225
 
189
 
239
 
203
 
240
 	return
204
 	return
241
 }
205
 }
242
-
243
-func printURLs(data interface{}) {
244
-	encoder := json.NewEncoder(os.Stdout)
245
-	encoder.SetEscapeHTML(false)
246
-	encoder.SetIndent("", "  ")
247
-
248
-	err := encoder.Encode(data)
249
-	if err != nil {
250
-		panic(err)
251
-	}
252
-}
253
-
254
-func usage(msg string) {
255
-	io.WriteString(os.Stderr, msg+"\n") // nolint: errcheck, gosec
256
-	os.Exit(1)
257
-}

+ 32
- 0
newantireplay/cache.go View File

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
+}

+ 9
- 0
newantireplay/hasher.go View File

1
+package newantireplay
2
+
3
+import "github.com/cespare/xxhash"
4
+
5
+type hasher struct{}
6
+
7
+func (h hasher) Sum64(value string) uint64 {
8
+	return xxhash.Sum64String(value)
9
+}

+ 25
- 0
newcli/generate.go View File

1
+package newcli
2
+
3
+import (
4
+	"crypto/rand"
5
+	"encoding/hex"
6
+
7
+	"github.com/9seconds/mtg/newconfig"
8
+)
9
+
10
+func Generate(secretType string) {
11
+	data := make([]byte, newconfig.SimpleSecretLength)
12
+	if _, err := rand.Read(data); err != nil {
13
+		panic(err)
14
+	}
15
+	secret := hex.EncodeToString(data)
16
+
17
+	switch secretType {
18
+	case "simple":
19
+		PrintStdout(secret)
20
+	case "secured":
21
+		PrintStdout("dd" + secret)
22
+	default:
23
+		Fatal("Unknown secret type " + secret)
24
+	}
25
+}

+ 61
- 0
newcli/proxy.go View File

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
+}

+ 39
- 0
newcli/utils.go View File

1
+package newcli
2
+
3
+import (
4
+	"encoding/json"
5
+	"fmt"
6
+	"io"
7
+	"os"
8
+)
9
+
10
+func Fatal(args ...interface{}) {
11
+	PrintStderr(args...)
12
+	os.Exit(1)
13
+}
14
+
15
+func PrintStderr(args ...interface{}) {
16
+	fmt.Fprintln(os.Stderr, args...)
17
+}
18
+
19
+func PrintStdout(args ...interface{}) {
20
+	fmt.Println(args...)
21
+}
22
+
23
+func PrintJSONStderr(data interface{}) {
24
+	printJSON(os.Stderr, data)
25
+}
26
+
27
+func PrintJSONStdout(data interface{}) {
28
+	printJSON(os.Stdout, data)
29
+}
30
+
31
+func printJSON(writer io.Writer, data interface{}) {
32
+	encoder := json.NewEncoder(writer)
33
+	encoder.SetEscapeHTML(false)
34
+	encoder.SetIndent("", "  ")
35
+
36
+	if err := encoder.Encode(data); err != nil {
37
+		panic(err)
38
+	}
39
+}

config2/config.go → newconfig/config.go View File

1
-package config2
1
+package newconfig
2
 
2
 
3
 import (
3
 import (
4
 	"bytes"
4
 	"bytes"
5
-	"context"
6
 	"encoding/json"
5
 	"encoding/json"
7
 	"net"
6
 	"net"
8
 	"strconv"
7
 	"strconv"
9
-	"sync"
10
 	"time"
8
 	"time"
11
 
9
 
12
 	"github.com/juju/errors"
10
 	"github.com/juju/errors"
11
+	"go.uber.org/zap"
13
 	statsd "gopkg.in/alexcesaro/statsd.v2"
12
 	statsd "gopkg.in/alexcesaro/statsd.v2"
14
 )
13
 )
15
 
14
 
16
-type SecretType byte
15
+type SecretMode uint8
17
 
16
 
18
-func (s SecretType) String() string {
17
+func (s SecretMode) String() string {
19
 	switch s {
18
 	switch s {
20
-	case SecretTypeMain:
21
-		return "main"
22
-	case SecretTypeSecured:
19
+	case SecretModeSimple:
20
+		return "simple"
21
+	case SecretModeSecured:
23
 		return "secured"
22
 		return "secured"
24
-	default:
25
-		return "tls"
26
 	}
23
 	}
24
+	return "tls"
27
 }
25
 }
28
 
26
 
29
 const (
27
 const (
30
-	SecretTypeMain = 1 << iota
31
-	SecretTypeSecured
32
-	SecretTypeTLS
28
+	SecretModeSimple SecretMode = iota
29
+	SecretModeSecured
30
+	SecretModeTLS
33
 )
31
 )
34
 
32
 
35
-const (
36
-	FlagDebug   = "debug"
37
-	FlagVerbose = "verbose"
38
-
39
-	FlagBindIP         = "bind-ip"
40
-	FlagBindPort       = "bind-port"
41
-	FlagPublicIPv4     = "public-ipv4"
42
-	FlagPublicIPv4Port = "public-ipv4-port"
43
-	FlagPublicIPv6     = "public-ipv6"
44
-	FlagPublicIPv6Port = "public-ipv6-port"
45
-	FlagStatsIP        = "stats-ip"
46
-	FlagStatsPort      = "stats-port"
47
-
48
-	FlagStatsdIP         = "statsd-ip"
49
-	FlagStatsdPort       = "statsd-port"
50
-	FlagStatsdNetwork    = "statsd-network"
51
-	FlagStatsdPrefix     = "statsd-prefix"
52
-	FlagStatsdTagsFormat = "statsd-tags-format"
53
-	FlagStatsdTags       = "statsd-tags"
54
-
55
-	FlagPrometheusPrefix = "prometheus-prefix"
33
+const SimpleSecretLength = 16
56
 
34
 
57
-	FlagWriteBufferSize = "write-buffer"
58
-	FlagReadBufferSize  = "read-buffer"
35
+type OptionType uint8
59
 
36
 
60
-	FlagSecureOnly = "secure-only"
61
-
62
-	FlagAntiReplayMaxSize      = "anti-replay-max-size"
63
-	FlagAntiReplayEvictionTime = "anti-replay-eviction-time"
64
-
65
-	FlagSecret = "secret"
66
-	FlagAdtag  = "adtag"
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
67
 )
66
 )
68
 
67
 
69
 type BufferSize struct {
68
 type BufferSize struct {
77
 }
76
 }
78
 
77
 
79
 type Stats struct {
78
 type Stats struct {
80
-	Prefix  string `json:"prefix"`
81
-	Enabled bool   `json:"enabled"`
79
+	Prefix string `json:"prefix"`
82
 }
80
 }
83
 
81
 
84
 type StatsdStats struct {
82
 type StatsdStats struct {
132
 
130
 
133
 	Debug      bool       `json:"debug"`
131
 	Debug      bool       `json:"debug"`
134
 	Verbose    bool       `json:"verbose"`
132
 	Verbose    bool       `json:"verbose"`
135
-	SecureOnly bool       `json:"secure_only"`
136
-	SecretType SecretType `json:"secret_type"`
133
+	SecretMode SecretMode `json:"secret_mode"`
137
 	Secret     []byte     `json:"secret"`
134
 	Secret     []byte     `json:"secret"`
138
 	AdTag      []byte     `json:"adtag"`
135
 	AdTag      []byte     `json:"adtag"`
139
 }
136
 }
144
 }
141
 }
145
 
142
 
146
 type ConfigOpt struct {
143
 type ConfigOpt struct {
147
-	Name  string
148
-	Value interface{}
144
+	Option OptionType
145
+	Value  interface{}
149
 }
146
 }
150
 
147
 
151
 var C = Config{}
148
 var C = Config{}
152
 
149
 
153
 func Init(options ...ConfigOpt) error { // nolint: gocyclo
150
 func Init(options ...ConfigOpt) error { // nolint: gocyclo
154
 	for _, opt := range options {
151
 	for _, opt := range options {
155
-		switch opt.Name {
156
-		case FlagDebug:
152
+		switch opt.Option {
153
+		case OptionTypeDebug:
157
 			C.Debug = opt.Value.(bool)
154
 			C.Debug = opt.Value.(bool)
158
-		case FlagVerbose:
155
+		case OptionTypeVerbose:
159
 			C.Verbose = opt.Value.(bool)
156
 			C.Verbose = opt.Value.(bool)
160
-		case FlagBindIP:
157
+		case OptionTypeBindIP:
161
 			C.ListenAddr.IP = opt.Value.(net.IP)
158
 			C.ListenAddr.IP = opt.Value.(net.IP)
162
-		case FlagBindPort:
163
-			C.ListenAddr.Port = opt.Value.(int)
164
-		case FlagPublicIPv4:
159
+		case OptionTypeBindPort:
160
+			C.ListenAddr.Port = int(opt.Value.(uint16))
161
+		case OptionTypePublicIPv4:
165
 			C.PublicIPv4Addr.IP = opt.Value.(net.IP)
162
 			C.PublicIPv4Addr.IP = opt.Value.(net.IP)
166
-		case FlagPublicIPv4Port:
167
-			C.PublicIPv4Addr.Port = opt.Value.(int)
168
-		case FlagPublicIPv6:
163
+		case OptionTypePublicIPv4Port:
164
+			C.PublicIPv4Addr.Port = int(opt.Value.(uint16))
165
+		case OptionTypePublicIPv6:
169
 			C.PublicIPv6Addr.IP = opt.Value.(net.IP)
166
 			C.PublicIPv6Addr.IP = opt.Value.(net.IP)
170
-		case FlagPublicIPv6Port:
171
-			C.PublicIPv6Addr.Port = opt.Value.(int)
172
-		case FlagStatsIP:
167
+		case OptionTypePublicIPv6Port:
168
+			C.PublicIPv6Addr.Port = int(opt.Value.(uint16))
169
+		case OptionTypeStatsIP:
173
 			C.StatsAddr.IP = opt.Value.(net.IP)
170
 			C.StatsAddr.IP = opt.Value.(net.IP)
174
-		case FlagStatsPort:
175
-			C.StatsAddr.Port = opt.Value.(int)
176
-		case FlagStatsdIP:
171
+		case OptionTypeStatsPort:
172
+			C.StatsAddr.Port = int(opt.Value.(uint16))
173
+		case OptionTypeStatsdIP:
177
 			C.StatsdStats.Addr.IP = opt.Value.(net.IP)
174
 			C.StatsdStats.Addr.IP = opt.Value.(net.IP)
178
-		case FlagStatsdPort:
179
-			C.StatsdStats.Addr.Port = opt.Value.(int)
180
-		case FlagStatsdNetwork:
175
+		case OptionTypeStatsdPort:
176
+			C.StatsdStats.Addr.Port = int(opt.Value.(uint16))
177
+		case OptionTypeStatsdNetwork:
181
 			C.StatsdStats.Addr.net = opt.Value.(string)
178
 			C.StatsdStats.Addr.net = opt.Value.(string)
182
-		case FlagStatsdPrefix:
179
+		case OptionTypeStatsdPrefix:
183
 			C.StatsdStats.Prefix = opt.Value.(string)
180
 			C.StatsdStats.Prefix = opt.Value.(string)
184
-		case FlagStatsdTagsFormat:
181
+		case OptionTypeStatsdTagsFormat:
185
 			value := opt.Value.(string)
182
 			value := opt.Value.(string)
186
 			switch value {
183
 			switch value {
187
 			case "datadog":
184
 			case "datadog":
191
 			default:
188
 			default:
192
 				return errors.Errorf("Incorrect statsd tag %s", value)
189
 				return errors.Errorf("Incorrect statsd tag %s", value)
193
 			}
190
 			}
194
-		case FlagStatsdTags:
191
+		case OptionTypeStatsdTags:
195
 			C.StatsdStats.Tags = opt.Value.(map[string]string)
192
 			C.StatsdStats.Tags = opt.Value.(map[string]string)
196
-		case FlagPrometheusPrefix:
193
+		case OptionTypePrometheusPrefix:
197
 			C.PrometheusStats.Prefix = opt.Value.(string)
194
 			C.PrometheusStats.Prefix = opt.Value.(string)
198
-		case FlagWriteBufferSize:
199
-			C.BufferSize.Write = opt.Value.(int)
200
-		case FlagReadBufferSize:
201
-			C.BufferSize.Read = opt.Value.(int)
202
-		case FlagAntiReplayMaxSize:
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:
203
 			C.AntiReplay.MaxSize = opt.Value.(int)
200
 			C.AntiReplay.MaxSize = opt.Value.(int)
204
-		case FlagAntiReplayEvictionTime:
201
+		case OptionTypeAntiReplayEvictionTime:
205
 			C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
202
 			C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
206
-		case FlagSecureOnly:
207
-			C.SecureOnly = opt.Value.(bool)
208
-		case FlagSecret:
203
+		case OptionTypeSecret:
209
 			C.Secret = opt.Value.([]byte)
204
 			C.Secret = opt.Value.([]byte)
210
-		case FlagAdtag:
205
+		case OptionTypeAdtag:
211
 			C.AdTag = opt.Value.([]byte)
206
 			C.AdTag = opt.Value.([]byte)
207
+		default:
208
+			return errors.Errorf("Unknown tag %v", opt.Option)
212
 		}
209
 		}
213
 	}
210
 	}
214
 
211
 
215
-	var defaultStatsdTags statsd.TagFormat
216
-	if C.StatsdStats.TagsFormat == defaultStatsdTags {
217
-		C.StatsdStats.TagsFormat = statsd.Datadog
218
-	}
219
-	if C.StatsdStats.Addr.net == "" {
220
-		C.StatsdStats.Addr.net = "udp"
221
-	}
222
-
223
 	switch {
212
 	switch {
224
-	case len(C.Secret) == 17 && bytes.HasPrefix(C.Secret, []byte{0xdd}):
225
-		C.SecretType = SecretTypeSecured
213
+	case len(C.Secret) == 1+SimpleSecretLength && bytes.HasPrefix(C.Secret, []byte{0xdd}):
214
+		C.SecretMode = SecretModeSecured
226
 		C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
215
 		C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
227
-	case len(C.Secret) == 16:
228
-		C.SecretType = SecretTypeMain
216
+	case len(C.Secret) == SimpleSecretLength:
217
+		C.SecretMode = SecretModeSimple
229
 	default:
218
 	default:
230
 		return errors.New("Incorrect secret")
219
 		return errors.New("Incorrect secret")
231
 	}
220
 	}
241
 		C.PublicIPv6Addr.Port = C.ListenAddr.Port
230
 		C.PublicIPv6Addr.Port = C.ListenAddr.Port
242
 	}
231
 	}
243
 
232
 
244
-	ctx, cancel := context.WithCancel(context.Background())
245
-	defer cancel()
246
-	wg := &sync.WaitGroup{}
247
-	done := make(chan struct{})
248
-
233
+	foundAddress := C.PublicIPv4Addr.IP != nil || C.PublicIPv6Addr.IP != nil
249
 	if C.PublicIPv4Addr.IP == nil {
234
 	if C.PublicIPv4Addr.IP == nil {
250
-		wg.Add(1)
251
-		go func() {
252
-			getGlobalIPv4(ctx, cancel)
253
-			wg.Done()
254
-		}()
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
+		}
255
 	}
242
 	}
256
 	if C.PublicIPv6Addr.IP == nil {
243
 	if C.PublicIPv6Addr.IP == nil {
257
-		wg.Add(1)
258
-		go func() {
259
-			getGlobalIPv6(ctx, cancel)
260
-			wg.Done()
261
-
262
-		}()
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
+		}
263
 	}
251
 	}
264
-	go func() {
265
-		wg.Wait()
266
-		close(done)
267
-	}()
268
-
269
-	select {
270
-	case <-done:
271
-		return nil
272
-	case <-ctx.Done():
273
-		return ctx.Err()
252
+
253
+	if !foundAddress {
254
+		return errors.New("Cannot resolve any public address")
274
 	}
255
 	}
256
+
257
+	return nil
275
 }
258
 }

config2/global_ips.go → newconfig/global_ips.go View File

1
-package config2
1
+package newconfig
2
 
2
 
3
 import (
3
 import (
4
 	"context"
4
 	"context"
10
 	"time"
10
 	"time"
11
 
11
 
12
 	"github.com/juju/errors"
12
 	"github.com/juju/errors"
13
-	"go.uber.org/zap"
14
 )
13
 )
15
 
14
 
16
 const (
15
 const (
18
 	ifconfigTimeout = 10 * time.Second
17
 	ifconfigTimeout = 10 * time.Second
19
 )
18
 )
20
 
19
 
21
-func getGlobalIPv4(ctx context.Context, cancel context.CancelFunc) {
22
-	ip, err := fetchIP(ctx, "tcp4")
20
+func getGlobalIPv4() (net.IP, error) {
21
+	ip, err := fetchIP("tcp4")
23
 	if err != nil || ip.To4() == nil {
22
 	if err != nil || ip.To4() == nil {
24
-		cancel()
25
-		zap.S().Errorw("Cannot find public ipv4 address", "error", err)
26
-		return
23
+		return nil, errors.Annotate(err, "Cannot find public ipv4 address")
27
 	}
24
 	}
28
-	C.PublicIPv4Addr.IP = ip
25
+	return ip, nil
29
 }
26
 }
30
 
27
 
31
-func getGlobalIPv6(ctx context.Context, cancel context.CancelFunc) {
32
-	ip, err := fetchIP(ctx, "tcp6")
28
+func getGlobalIPv6() (net.IP, error) {
29
+	ip, err := fetchIP("tcp6")
33
 	if err != nil || ip.To4() != nil {
30
 	if err != nil || ip.To4() != nil {
34
-		cancel()
35
-		zap.S().Errorw("Cannot find public ipv6 address", "error", err)
36
-		return
31
+		return nil, errors.Annotate(err, "Cannot find public ipv6 address")
37
 	}
32
 	}
38
-	C.PublicIPv6Addr.IP = ip
33
+	return ip, nil
39
 }
34
 }
40
 
35
 
41
-func fetchIP(ctx context.Context, network string) (net.IP, error) {
36
+func fetchIP(network string) (net.IP, error) {
42
 	dialer := &net.Dialer{FallbackDelay: -1}
37
 	dialer := &net.Dialer{FallbackDelay: -1}
43
 	client := &http.Client{
38
 	client := &http.Client{
44
 		Jar:     nil,
39
 		Jar:     nil,
50
 		},
45
 		},
51
 	}
46
 	}
52
 
47
 
53
-	req, err := http.NewRequest("GET", ifconfigAddress, nil)
54
-	if err != nil {
55
-		panic(err)
56
-	}
57
-
58
-	resp, err := client.Do(req.WithContext(ctx))
48
+	resp, err := client.Get(ifconfigAddress)
59
 	if err != nil {
49
 	if err != nil {
60
-		if resp.Body != nil {
50
+		if resp != nil {
61
 			io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
51
 			io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
62
 		}
52
 		}
63
 		return nil, errors.Annotate(err, "Cannot perform a request")
53
 		return nil, errors.Annotate(err, "Cannot perform a request")

config2/urls.go → newconfig/urls.go View File

1
-package config2
1
+package newconfig
2
 
2
 
3
 import (
3
 import (
4
 	"encoding/hex"
4
 	"encoding/hex"
20
 
20
 
21
 func GetURLs() (urls IPURLs) {
21
 func GetURLs() (urls IPURLs) {
22
 	secret := ""
22
 	secret := ""
23
-	switch C.SecretType {
24
-	case SecretTypeMain, SecretTypeSecured:
23
+	switch C.SecretMode {
24
+	case SecretModeSimple:
25
 		secret = hex.EncodeToString(C.Secret)
25
 		secret = hex.EncodeToString(C.Secret)
26
-		if C.SecureOnly {
27
-			secret = "dd" + secret
28
-		}
26
+	case SecretModeSecured:
27
+		secret = "dd" + hex.EncodeToString(C.Secret)
29
 	}
28
 	}
30
 
29
 
31
 	urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)
30
 	urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)

+ 95
- 0
newobfuscated2/client_protocol.go View File

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
+}

+ 54
- 0
newobfuscated2/frame.go View File

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
+}

+ 61
- 0
newobfuscated2/telegram_protocol.go View File

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
+}

+ 7
- 0
newprotocol/base_protocol.go View File

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

+ 19
- 0
newprotocol/connection_protocol.go View File

1
+package newprotocol
2
+
3
+type ConnectionProtocol uint8
4
+
5
+func (c ConnectionProtocol) String() string {
6
+	switch c {
7
+	case ConnectionProtocolAny:
8
+		return "any"
9
+	case ConnectionProtocolIPv4:
10
+		return "ipv4"
11
+	}
12
+	return "ipv6"
13
+}
14
+
15
+const (
16
+	ConnectionProtocolIPv4 ConnectionProtocol = 1
17
+	ConnectionProtocolIPv6                    = ConnectionProtocolIPv4 << 1
18
+	ConnectionProtocolAny                     = ConnectionProtocolIPv4 | ConnectionProtocolIPv6
19
+)

+ 27
- 0
newprotocol/connection_type.go View File

1
+package newprotocol
2
+
3
+type ConnectionType uint8
4
+
5
+const (
6
+	ConnectionTypeUnknown ConnectionType = iota
7
+	ConnectionTypeAbridged
8
+	ConnectionTypeIntermediate
9
+	ConnectionTypeSecure
10
+)
11
+
12
+var (
13
+	ConnectionTagAbridged     = []byte{0xef, 0xef, 0xef, 0xef}
14
+	ConnectionTagIntermediate = []byte{0xee, 0xee, 0xee, 0xee}
15
+	ConnectionTagSecure       = []byte{0xdd, 0xdd, 0xdd, 0xdd}
16
+)
17
+
18
+func (t ConnectionType) Tag() []byte {
19
+	switch t {
20
+	case ConnectionTypeAbridged:
21
+		return ConnectionTagAbridged
22
+	case ConnectionTypeIntermediate:
23
+		return ConnectionTagIntermediate
24
+	default:
25
+		return ConnectionTagSecure
26
+	}
27
+}

+ 1
- 0
newproxy/proxy.go View File

1
+package newproxy

+ 93
- 0
newstats/stats.go View File

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
+}

+ 127
- 0
newstats/stats_json.go View File

1
+package newstats
2
+
3
+import (
4
+	"encoding/json"
5
+	"net"
6
+	"net/http"
7
+	"sync/atomic"
8
+	"time"
9
+
10
+	"go.uber.org/zap"
11
+
12
+	"github.com/9seconds/mtg/newprotocol"
13
+)
14
+
15
+type statsJSON struct {
16
+	Connections statsJSONConnections `json:"connections"`
17
+	Traffic     statsJSONTraffic     `json:"traffic"`
18
+	Uptime      statsJSONUptime      `json:"uptime"`
19
+	Crashes     uint32               `json:"crashes"`
20
+	AntiReplays uint32               `json:"anti_replay_detected"`
21
+}
22
+
23
+type statsBaseJSONConnections struct {
24
+	All          statsJSONConnectionType `json:"all"`
25
+	Abridged     statsJSONConnectionType `json:"abridged"`
26
+	Intermediate statsJSONConnectionType `json:"intermediate"`
27
+	Secured      statsJSONConnectionType `json:"secured"`
28
+}
29
+
30
+type statsJSONConnections struct {
31
+	statsBaseJSONConnections
32
+}
33
+
34
+type statsJSONConnectionType struct {
35
+	IPv4 uint32 `json:"ipv4"`
36
+	IPv6 uint32 `json:"ipv6"`
37
+}
38
+
39
+func (c statsJSONConnections) MarshalJSON() ([]byte, error) {
40
+	c.All.IPv4 = c.Abridged.IPv4 + c.Intermediate.IPv4 + c.Secured.IPv4
41
+	c.All.IPv6 = c.Abridged.IPv6 + c.Intermediate.IPv6 + c.Secured.IPv6
42
+
43
+	return json.Marshal(c.statsBaseJSONConnections)
44
+}
45
+
46
+type statsJSONTraffic struct {
47
+	Ingress uint64 `json:"ingress"`
48
+	Egress  uint64 `json:"egress"`
49
+}
50
+
51
+type statsJSONUptime time.Time
52
+
53
+func (s statsJSONUptime) MarshalJSON() ([]byte, error) {
54
+	return json.Marshal(time.Since(time.Time(s)).Seconds())
55
+}
56
+
57
+func (s *statsJSON) IngressTraffic(traffic int) {
58
+	atomic.AddUint64(&s.Traffic.Ingress, uint64(traffic))
59
+}
60
+
61
+func (s *statsJSON) EgressTraffic(traffic int) {
62
+	atomic.AddUint64(&s.Traffic.Egress, uint64(traffic))
63
+}
64
+
65
+func (s *statsJSON) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
66
+	s.changeConnections(connectionType, addr, 1)
67
+}
68
+
69
+func (s *statsJSON) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
70
+	s.changeConnections(connectionType, addr, ^uint32(0))
71
+}
72
+
73
+func (s *statsJSON) changeConnections(connectionType newprotocol.ConnectionType, addr *net.TCPAddr, value uint32) {
74
+	var connections *statsJSONConnectionType
75
+
76
+	switch connectionType {
77
+	case newprotocol.ConnectionTypeAbridged:
78
+		connections = &s.Connections.Abridged
79
+	case newprotocol.ConnectionTypeSecure:
80
+		connections = &s.Connections.Secured
81
+	default:
82
+		connections = &s.Connections.Intermediate
83
+	}
84
+
85
+	if addr.IP.To4() == nil {
86
+		atomic.AddUint32(&connections.IPv4, value)
87
+	} else {
88
+		atomic.AddUint32(&connections.IPv6, value)
89
+	}
90
+}
91
+
92
+func (s *statsJSON) Crash() {
93
+	atomic.AddUint32(&s.Crashes, 1)
94
+}
95
+
96
+func (s *statsJSON) AntiReplayDetected() {
97
+	atomic.AddUint32(&s.AntiReplays, 1)
98
+}
99
+
100
+func newStatsJSON(mux *http.ServeMux) Stats {
101
+	instance := &statsJSON{}
102
+	logger := zap.S().Named("stats")
103
+
104
+	mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
105
+		w.Header().Set("Content-Type", "application/json")
106
+		first, err := json.Marshal(instance)
107
+		if err != nil {
108
+			logger.Errorw("Cannot encode json", "error", err)
109
+			http.Error(w, "Internal server error", http.StatusServiceUnavailable)
110
+			return
111
+		}
112
+
113
+		interim := map[string]interface{}{}
114
+		if err := json.Unmarshal(first, &interim); err != nil {
115
+			panic(err)
116
+		}
117
+
118
+		encoder := json.NewEncoder(w)
119
+		encoder.SetEscapeHTML(false)
120
+		encoder.SetIndent("", "  ")
121
+		if err := encoder.Encode(interim); err != nil {
122
+			logger.Errorw("Cannot encode json", "error", err)
123
+		}
124
+	})
125
+
126
+	return instance
127
+}

+ 110
- 0
newstats/stats_prometheus.go View File

1
+package newstats
2
+
3
+import (
4
+	"net"
5
+	"net/http"
6
+
7
+	"github.com/juju/errors"
8
+	"github.com/prometheus/client_golang/prometheus"
9
+	"github.com/prometheus/client_golang/prometheus/promhttp"
10
+
11
+	"github.com/9seconds/mtg/newconfig"
12
+	"github.com/9seconds/mtg/newprotocol"
13
+)
14
+
15
+type statsPrometheus struct {
16
+	connections *prometheus.GaugeVec
17
+	traffic     *prometheus.GaugeVec
18
+	crashes     prometheus.Gauge
19
+	antiReplays prometheus.Gauge
20
+}
21
+
22
+func (s *statsPrometheus) IngressTraffic(traffic int) {
23
+	s.traffic.WithLabelValues("ingress").Add(float64(traffic))
24
+}
25
+
26
+func (s *statsPrometheus) EgressTraffic(traffic int) {
27
+	s.traffic.WithLabelValues("egress").Add(float64(traffic))
28
+}
29
+
30
+func (s *statsPrometheus) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
31
+	s.changeConnections(connectionType, addr, 1.0)
32
+}
33
+
34
+func (s *statsPrometheus) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
35
+	s.changeConnections(connectionType, addr, -1.0)
36
+}
37
+
38
+func (s *statsPrometheus) changeConnections(connectionType newprotocol.ConnectionType,
39
+	addr *net.TCPAddr,
40
+	increment float64) {
41
+	var labels [2]string
42
+
43
+	switch connectionType {
44
+	case newprotocol.ConnectionTypeAbridged:
45
+		labels[0] = "abridged"
46
+	case newprotocol.ConnectionTypeSecure:
47
+		labels[0] = "secured"
48
+	default:
49
+		labels[0] = "intermediate"
50
+	}
51
+
52
+	labels[1] = "ipv4"
53
+	if addr.IP.To4() == nil {
54
+		labels[1] = "ipv6"
55
+	}
56
+
57
+	s.connections.WithLabelValues(labels[:]...).Add(increment)
58
+}
59
+
60
+func (s *statsPrometheus) Crash() {
61
+	s.crashes.Inc()
62
+}
63
+
64
+func (s *statsPrometheus) AntiReplayDetected() {
65
+	s.antiReplays.Inc()
66
+}
67
+
68
+func newStatsPrometheus(mux *http.ServeMux) (Stats, error) {
69
+	registry := prometheus.NewRegistry()
70
+	instance := &statsPrometheus{
71
+		connections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
72
+			Namespace: newconfig.C.PrometheusStats.Prefix,
73
+			Name:      "connections",
74
+			Help:      "Current number of connections to the proxy.",
75
+		}, []string{"type", "protocol"}),
76
+		traffic: prometheus.NewGaugeVec(prometheus.GaugeOpts{
77
+			Namespace: newconfig.C.PrometheusStats.Prefix,
78
+			Name:      "traffic",
79
+			Help:      "Traffic passed through the proxy in bytes.",
80
+		}, []string{"direction"}),
81
+		crashes: prometheus.NewGauge(prometheus.GaugeOpts{
82
+			Namespace: newconfig.C.PrometheusStats.Prefix,
83
+			Name:      "crashes",
84
+			Help:      "How many crashes happened.",
85
+		}),
86
+		antiReplays: prometheus.NewGauge(prometheus.GaugeOpts{
87
+			Namespace: newconfig.C.PrometheusStats.Prefix,
88
+			Name:      "anti_replays",
89
+			Help:      "How many anti replay attacks were prevented.",
90
+		}),
91
+	}
92
+
93
+	if err := registry.Register(instance.connections); err != nil {
94
+		return nil, errors.Annotate(err, "Cannot register metrics for connections")
95
+	}
96
+	if err := registry.Register(instance.traffic); err != nil {
97
+		return nil, errors.Annotate(err, "Cannot register metrics for traffic")
98
+	}
99
+	if err := registry.Register(instance.crashes); err != nil {
100
+		return nil, errors.Annotate(err, "Cannot register metrics for crashes")
101
+	}
102
+	if err := registry.Register(instance.antiReplays); err != nil {
103
+		return nil, errors.Annotate(err, "Cannot register metrics for anti replays")
104
+	}
105
+
106
+	handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
107
+	mux.Handle("/prometheus", handler)
108
+
109
+	return instance, nil
110
+}

+ 87
- 0
newstats/stats_statsd.go View File

1
+package newstats
2
+
3
+import (
4
+	"net"
5
+	"strings"
6
+
7
+	"gopkg.in/alexcesaro/statsd.v2"
8
+
9
+	"github.com/9seconds/mtg/newconfig"
10
+	"github.com/9seconds/mtg/newprotocol"
11
+	"github.com/juju/errors"
12
+)
13
+
14
+type statsStatsd struct {
15
+	client *statsd.Client
16
+}
17
+
18
+func (s *statsStatsd) IngressTraffic(traffic int) {
19
+	s.client.Count("traffic.ingress", traffic)
20
+}
21
+
22
+func (s *statsStatsd) EgressTraffic(traffic int) {
23
+	s.client.Count("traffic.egress", traffic)
24
+}
25
+
26
+func (s *statsStatsd) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
27
+	s.changeConnections(connectionType, addr, 1)
28
+}
29
+
30
+func (s *statsStatsd) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
31
+	s.changeConnections(connectionType, addr, -1)
32
+}
33
+
34
+func (s *statsStatsd) changeConnections(connectionType newprotocol.ConnectionType, addr *net.TCPAddr, value int) {
35
+	var labels [3]string
36
+
37
+	labels[0] = "connections"
38
+	switch connectionType {
39
+	case newprotocol.ConnectionTypeAbridged:
40
+		labels[1] = "abridged"
41
+	case newprotocol.ConnectionTypeSecure:
42
+		labels[1] = "secured"
43
+	default:
44
+		labels[1] = "intermediate"
45
+	}
46
+
47
+	labels[2] = "ipv4"
48
+	if addr.IP.To4() == nil {
49
+		labels[2] = "ipv6"
50
+	}
51
+
52
+	s.client.Count(strings.Join(labels[:], "."), value)
53
+}
54
+
55
+func (s *statsStatsd) Crash() {
56
+	s.client.Increment("crashes")
57
+}
58
+
59
+func (s *statsStatsd) AntiReplayDetected() {
60
+	s.client.Increment("anti_replays")
61
+}
62
+
63
+func newStatsStatsd() (Stats, error) {
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),
69
+	}
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 {
74
+			tags = append(tags, k, v)
75
+		}
76
+		options = append(options, statsd.Tags(tags...))
77
+	}
78
+
79
+	client, err := statsd.New(options...)
80
+	if err != nil {
81
+		return nil, errors.Annotate(err, "Cannot initialize a client")
82
+	}
83
+
84
+	return &statsStatsd{
85
+		client: client,
86
+	}, nil
87
+}

+ 117
- 0
newwrappers/interfaces.go View File

1
+package newwrappers
2
+
3
+import (
4
+	"io"
5
+	"net"
6
+	"time"
7
+
8
+	"go.uber.org/zap"
9
+)
10
+
11
+type Packet []byte
12
+
13
+// Wrap is a base interface for all wrappers in this package.
14
+type Wrap interface {
15
+	Conn() net.Conn
16
+	Logger() *zap.SugaredLogger
17
+	LocalAddr() *net.TCPAddr
18
+	RemoteAddr() *net.TCPAddr
19
+}
20
+
21
+type BaseReaderWithTimeout interface {
22
+	ReadTimeout([]byte, time.Duration) (int, error)
23
+}
24
+
25
+type BaseWriterWithTimeout interface {
26
+	WriteTimeout([]byte, time.Duration) (int, error)
27
+}
28
+
29
+type BasePacketReader interface {
30
+	Read() (Packet, error)
31
+}
32
+
33
+type BasePacketWriter interface {
34
+	Write(Packet) error
35
+}
36
+
37
+type StreamReader interface {
38
+	Wrap
39
+	io.Reader
40
+	BaseReaderWithTimeout
41
+}
42
+
43
+type StreamWriter interface {
44
+	Wrap
45
+	io.Writer
46
+	BaseWriterWithTimeout
47
+}
48
+
49
+type StreamCloser interface {
50
+	Wrap
51
+	io.Closer
52
+}
53
+
54
+type StreamReadCloser interface {
55
+	Wrap
56
+	io.ReadCloser
57
+	BaseReaderWithTimeout
58
+}
59
+
60
+type StreamWriteCloser interface {
61
+	Wrap
62
+	io.WriteCloser
63
+	BaseWriterWithTimeout
64
+}
65
+
66
+type StreamReadWriter interface {
67
+	Wrap
68
+	io.ReadWriter
69
+	BaseReaderWithTimeout
70
+}
71
+
72
+type StreamReadWriteCloser interface {
73
+	Wrap
74
+	io.ReadWriteCloser
75
+	BaseReaderWithTimeout
76
+	BaseWriterWithTimeout
77
+}
78
+
79
+type PacketReader interface {
80
+	Wrap
81
+	BasePacketReader
82
+}
83
+
84
+type PacketWriter interface {
85
+	Wrap
86
+	BasePacketWriter
87
+}
88
+
89
+type PacketCloser interface {
90
+	Wrap
91
+	io.Closer
92
+}
93
+
94
+type PacketReadCloser interface {
95
+	Wrap
96
+	BasePacketReader
97
+	io.Closer
98
+}
99
+
100
+type PacketWriteCloser interface {
101
+	Wrap
102
+	BasePacketWriter
103
+	io.Closer
104
+}
105
+
106
+type PacketReadWriter interface {
107
+	Wrap
108
+	BasePacketWriter
109
+	BasePacketReader
110
+}
111
+
112
+type PacketReadWriteCloser interface {
113
+	Wrap
114
+	BasePacketWriter
115
+	BasePacketReader
116
+	io.Closer
117
+}

+ 178
- 0
newwrappers/wrapper_conn.go View File

1
+package newwrappers
2
+
3
+import (
4
+	"context"
5
+	"crypto/rand"
6
+	"encoding/hex"
7
+	"net"
8
+	"time"
9
+
10
+	"github.com/juju/errors"
11
+	"go.uber.org/zap"
12
+
13
+	"github.com/9seconds/mtg/newconfig"
14
+)
15
+
16
+const ConnIDLength = 8
17
+
18
+type ConnID [ConnIDLength]byte
19
+
20
+func (c ConnID) String() string {
21
+	return hex.EncodeToString(c[:])
22
+}
23
+
24
+type connPurpose uint8
25
+
26
+const (
27
+	connPurposeClient connPurpose = 1 << iota
28
+	connPurposeTelegram
29
+)
30
+
31
+const (
32
+	connTimeoutRead  = 2 * time.Minute
33
+	connTimeoutWrite = 2 * time.Minute
34
+)
35
+
36
+type wrapperConn struct {
37
+	parent     net.Conn
38
+	ctx        context.Context
39
+	cancel     context.CancelFunc
40
+	connID     ConnID
41
+	logger     *zap.SugaredLogger
42
+	localAddr  *net.TCPAddr
43
+	remoteAddr *net.TCPAddr
44
+}
45
+
46
+func (w *wrapperConn) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
47
+	select {
48
+	case <-w.ctx.Done():
49
+		w.Close()
50
+		return 0, errors.Annotate(w.ctx.Err(), "Cannot write because context was closed")
51
+
52
+	default:
53
+		if err := w.parent.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
54
+			w.Close() // nolint: gosec
55
+			return 0, errors.Annotate(err, "Cannot set write deadline to the socket")
56
+		}
57
+
58
+		n, err := w.parent.Write(p)
59
+		w.logger.Debugw("Write to stream", "bytes", n, "error", err)
60
+		if err != nil {
61
+			w.Close() // nolint: gosec
62
+		}
63
+
64
+		return n, err
65
+	}
66
+}
67
+
68
+func (w *wrapperConn) Write(p []byte) (int, error) {
69
+	return w.WriteTimeout(p, connTimeoutWrite)
70
+}
71
+
72
+func (w *wrapperConn) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
73
+	select {
74
+	case <-w.ctx.Done():
75
+		w.Close()
76
+		return 0, errors.Annotate(w.ctx.Err(), "Cannot read because context was closed")
77
+
78
+	default:
79
+		if err := w.parent.SetReadDeadline(time.Now().Add(timeout)); err != nil {
80
+			w.Close()
81
+			return 0, errors.Annotate(err, "Cannot set read deadline to the socket")
82
+		}
83
+
84
+		n, err := w.parent.Read(p)
85
+		w.logger.Debugw("Read from stream", "bytes", n, "error", err)
86
+		if err != nil {
87
+			w.Close()
88
+		}
89
+
90
+		return n, err
91
+	}
92
+}
93
+
94
+func (w *wrapperConn) Read(p []byte) (int, error) {
95
+	return w.ReadTimeout(p, connTimeoutRead)
96
+}
97
+
98
+func (w *wrapperConn) Close() error {
99
+	w.logger.Debugw("Close connection")
100
+	w.cancel()
101
+
102
+	return w.parent.Close()
103
+}
104
+
105
+func (w *wrapperConn) Conn() net.Conn {
106
+	return w.parent
107
+}
108
+
109
+func (w *wrapperConn) Logger() *zap.SugaredLogger {
110
+	return w.logger
111
+}
112
+
113
+func (w *wrapperConn) LocalAddr() *net.TCPAddr {
114
+	return w.localAddr
115
+}
116
+
117
+func (w *wrapperConn) RemoteAddr() *net.TCPAddr {
118
+	return w.remoteAddr
119
+}
120
+
121
+func newConn(ctx context.Context,
122
+	cancel context.CancelFunc,
123
+	parent net.Conn,
124
+	connID ConnID,
125
+	purpose connPurpose) StreamReadWriteCloser {
126
+	localAddr := *parent.LocalAddr().(*net.TCPAddr)
127
+
128
+	if parent.RemoteAddr().(*net.TCPAddr).IP.To4() != nil {
129
+		if newconfig.C.PublicIPv4Addr.IP != nil {
130
+			localAddr.IP = newconfig.C.PublicIPv4Addr.IP
131
+		}
132
+	} else if newconfig.C.PublicIPv6Addr.IP != nil {
133
+		localAddr.IP = newconfig.C.PublicIPv6Addr.IP
134
+	}
135
+
136
+	logger := zap.S().With(
137
+		"local_address", localAddr,
138
+		"remote_address", parent.RemoteAddr(),
139
+	).Named("conn")
140
+
141
+	if purpose == connPurposeClient {
142
+		logger = logger.With("connection_id", connID.String())
143
+	}
144
+
145
+	return &wrapperConn{
146
+		parent:     parent,
147
+		ctx:        ctx,
148
+		cancel:     cancel,
149
+		connID:     connID,
150
+		logger:     logger,
151
+		remoteAddr: parent.RemoteAddr().(*net.TCPAddr),
152
+		localAddr:  &localAddr,
153
+	}
154
+}
155
+
156
+func NewClientConn(ctx context.Context,
157
+	cancel context.CancelFunc,
158
+	parent net.Conn,
159
+	connID ConnID) StreamReadWriteCloser {
160
+	return newConn(ctx, cancel, parent, connID, connPurposeClient)
161
+}
162
+
163
+func NewTelegramConn(ctx context.Context,
164
+	cancel context.CancelFunc,
165
+	parent net.Conn,
166
+	connID ConnID) StreamReadWriteCloser {
167
+	return newConn(ctx, cancel, parent, connID, connPurposeTelegram)
168
+}
169
+
170
+func NewConnID() ConnID {
171
+	var id ConnID
172
+
173
+	if _, err := rand.Read(id[:]); err != nil {
174
+		panic(err)
175
+	}
176
+
177
+	return id
178
+}

+ 80
- 0
newwrappers/wrapper_obfuscated2.go View File

1
+package newwrappers
2
+
3
+import (
4
+	"crypto/cipher"
5
+	"net"
6
+	"time"
7
+
8
+	"github.com/juju/errors"
9
+	"go.uber.org/zap"
10
+)
11
+
12
+type wrapperObfuscated2 struct {
13
+	encryptor cipher.Stream
14
+	decryptor cipher.Stream
15
+	parent    StreamReadWriteCloser
16
+}
17
+
18
+func (w *wrapperObfuscated2) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
19
+	n, err := w.parent.ReadTimeout(p, timeout)
20
+	if err != nil {
21
+		return 0, errors.Annotate(err, "Cannot read stream ciphered data")
22
+	}
23
+	w.decryptor.XORKeyStream(p, p[:n])
24
+
25
+	return n, nil
26
+}
27
+
28
+func (w *wrapperObfuscated2) Read(p []byte) (int, error) {
29
+	n, err := w.parent.Read(p)
30
+	if err != nil {
31
+		return 0, errors.Annotate(err, "Cannot read stream ciphered data")
32
+	}
33
+	w.decryptor.XORKeyStream(p, p[:n])
34
+
35
+	return n, nil
36
+}
37
+
38
+func (w *wrapperObfuscated2) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
39
+	buf := make([]byte, len(p))
40
+	copy(buf, p)
41
+	w.encryptor.XORKeyStream(buf, buf)
42
+
43
+	return w.parent.WriteTimeout(buf, timeout)
44
+}
45
+
46
+func (w *wrapperObfuscated2) Write(p []byte) (int, error) {
47
+	buf := make([]byte, len(p))
48
+	copy(buf, p)
49
+	w.encryptor.XORKeyStream(buf, buf)
50
+
51
+	return w.parent.Write(buf)
52
+}
53
+
54
+func (w *wrapperObfuscated2) Conn() net.Conn {
55
+	return w.parent.Conn()
56
+}
57
+
58
+func (w *wrapperObfuscated2) Logger() *zap.SugaredLogger {
59
+	return w.parent.Logger().Named("obfuscated2")
60
+}
61
+
62
+func (w *wrapperObfuscated2) LocalAddr() *net.TCPAddr {
63
+	return w.parent.LocalAddr()
64
+}
65
+
66
+func (w *wrapperObfuscated2) RemoteAddr() *net.TCPAddr {
67
+	return w.parent.RemoteAddr()
68
+}
69
+
70
+func (w *wrapperObfuscated2) Close() error {
71
+	return w.parent.Close()
72
+}
73
+
74
+func NewObfuscated2(socket StreamReadWriteCloser, encryptor, decryptor cipher.Stream) StreamReadWriteCloser {
75
+	return &wrapperObfuscated2{
76
+		parent:    socket,
77
+		encryptor: encryptor,
78
+		decryptor: decryptor,
79
+	}
80
+}

+ 66
- 0
newwrappers/wrapper_stats.go View File

1
+package newwrappers
2
+
3
+import (
4
+	"net"
5
+	"time"
6
+
7
+	"go.uber.org/zap"
8
+
9
+	"github.com/9seconds/mtg/newstats"
10
+)
11
+
12
+type wrapperStats struct {
13
+	parent StreamReadWriteCloser
14
+}
15
+
16
+func (w *wrapperStats) Write(p []byte) (int, error) {
17
+	n, err := w.parent.Write(p)
18
+	newstats.S.EgressTraffic(n)
19
+
20
+	return n, err
21
+}
22
+
23
+func (w *wrapperStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
24
+	n, err := w.parent.WriteTimeout(p, timeout)
25
+	newstats.S.EgressTraffic(n)
26
+
27
+	return n, err
28
+}
29
+
30
+func (w *wrapperStats) Read(p []byte) (int, error) {
31
+	n, err := w.parent.Read(p)
32
+	newstats.S.IngressTraffic(n)
33
+
34
+	return n, err
35
+}
36
+
37
+func (w *wrapperStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
38
+	n, err := w.parent.ReadTimeout(p, timeout)
39
+	newstats.S.IngressTraffic(n)
40
+
41
+	return n, err
42
+}
43
+
44
+func (w *wrapperStats) Conn() net.Conn {
45
+	return w.parent.Conn()
46
+}
47
+
48
+func (w *wrapperStats) Logger() *zap.SugaredLogger {
49
+	return w.parent.Logger().Named("traffic")
50
+}
51
+
52
+func (w *wrapperStats) LocalAddr() *net.TCPAddr {
53
+	return w.parent.LocalAddr()
54
+}
55
+
56
+func (w *wrapperStats) RemoteAddr() *net.TCPAddr {
57
+	return w.parent.RemoteAddr()
58
+}
59
+
60
+func (w *wrapperStats) Close() error {
61
+	return w.parent.Close()
62
+}
63
+
64
+func NewTraffic(parent StreamReadWriteCloser) StreamReadWriteCloser {
65
+	return &wrapperStats{parent}
66
+}

+ 1
- 1
ntp/ntp.go View File

11
 
11
 
12
 const autoUpdatePeriod = time.Minute
12
 const autoUpdatePeriod = time.Minute
13
 
13
 
14
-var ntpEndpoints = []string{
14
+var ntpEndpoints = [...]string{
15
 	"0.pool.ntp.org",
15
 	"0.pool.ntp.org",
16
 	"1.pool.ntp.org",
16
 	"1.pool.ntp.org",
17
 	"2.pool.ntp.org",
17
 	"2.pool.ntp.org",

Loading…
Cancel
Save