Bläddra i källkod

Reworked base

tags/1.0^2
9seconds 6 år sedan
förälder
incheckning
07985cf418

+ 2
- 0
go.mod Visa fil

@@ -15,6 +15,8 @@ require (
15 15
 	github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
16 16
 	github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect
17 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 20
 	github.com/pkg/errors v0.8.1 // indirect
19 21
 	github.com/prometheus/client_golang v1.1.0
20 22
 	github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect

+ 76
- 128
main.go Visa fil

@@ -1,23 +1,16 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"io"
7 4
 	"math/rand"
8 5
 	"os"
9 6
 	"syscall"
10 7
 	"time"
11 8
 
12 9
 	"github.com/juju/errors"
13
-	"go.uber.org/zap"
14
-	"go.uber.org/zap/zapcore"
15 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 16
 var version = "dev" // this has to be set by build ld flags
@@ -25,201 +18,172 @@ var version = "dev" // this has to be set by build ld flags
25 18
 var (
26 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 31
 		"Run in debug mode.").
30 32
 		Short('d').
31 33
 		Envar("MTG_DEBUG").
32 34
 		Bool()
33
-	verbose = app.Flag("verbose",
35
+	proxyVerbose = proxyCommand.Flag("verbose",
34 36
 		"Run in verbose mode.").
35 37
 		Short('v').
36 38
 		Envar("MTG_VERBOSE").
37 39
 		Bool()
38
-
39
-	bindIP = app.Flag("bind-ip",
40
+	proxyBindIP = proxyCommand.Flag("bind-ip",
40 41
 		"Which IP to bind to.").
41 42
 		Short('b').
42 43
 		Envar("MTG_IP").
43 44
 		Default("127.0.0.1").
44 45
 		IP()
45
-	bindPort = app.Flag("bind-port",
46
+	proxyBindPort = proxyCommand.Flag("bind-port",
46 47
 		"Which port to bind to.").
47 48
 		Short('p').
48 49
 		Envar("MTG_PORT").
49 50
 		Default("3128").
50 51
 		Uint16()
51
-
52
-	publicIPv4 = app.Flag("public-ipv4",
52
+	proxyPublicIPv4 = proxyCommand.Flag("public-ipv4",
53 53
 		"Which IPv4 address is public.").
54 54
 		Short('4').
55 55
 		Envar("MTG_IPV4").
56 56
 		IP()
57
-	publicIPv4Port = app.Flag("public-ipv4-port",
57
+	proxyPublicIPv4Port = proxyCommand.Flag("public-ipv4-port",
58 58
 		"Which IPv4 port is public. Default is 'bind-port' value.").
59 59
 		Envar("MTG_IPV4_PORT").
60 60
 		Uint16()
61
-
62
-	publicIPv6 = app.Flag("public-ipv6",
61
+	proxyPublicIPv6 = proxyCommand.Flag("public-ipv6",
63 62
 		"Which IPv6 address is public.").
64 63
 		Short('6').
65 64
 		Envar("MTG_IPV6").
66 65
 		IP()
67
-	publicIPv6Port = app.Flag("public-ipv6-port",
66
+	proxyPublicIPv6Port = proxyCommand.Flag("public-ipv6-port",
68 67
 		"Which IPv6 port is public. Default is 'bind-port' value.").
69 68
 		Envar("MTG_IPV6_PORT").
70 69
 		Uint16()
71
-
72
-	statsIP = app.Flag("stats-ip",
70
+	proxyStatsIP = proxyCommand.Flag("stats-ip",
73 71
 		"Which IP bind stats server to.").
74 72
 		Short('t').
75 73
 		Envar("MTG_STATS_IP").
76 74
 		Default("127.0.0.1").
77 75
 		IP()
78
-	statsPort = app.Flag("stats-port",
76
+	proxyStatsPort = proxyCommand.Flag("stats-port",
79 77
 		"Which port bind stats to.").
80 78
 		Short('q').
81 79
 		Envar("MTG_STATS_PORT").
82 80
 		Default("3129").
83 81
 		Uint16()
84
-
85
-	statsdIP = app.Flag("statsd-ip",
82
+	proxyStatsdIP = proxyCommand.Flag("statsd-ip",
86 83
 		"Which IP should we use for working with statsd.").
87 84
 		Envar("MTG_STATSD_IP").
88
-		String()
89
-	statsdPort = app.Flag("statsd-port",
85
+		IP()
86
+	proxyStatsdPort = proxyCommand.Flag("statsd-port",
90 87
 		"Which port should we use for working with statsd.").
91 88
 		Envar("MTG_STATSD_PORT").
92 89
 		Default("8125").
93 90
 		Uint16()
94
-	statsdNetwork = app.Flag("statsd-network",
91
+	proxyStatsdNetwork = proxyCommand.Flag("statsd-network",
95 92
 		"Which network is used to work with statsd. Only 'tcp' and 'udp' are supported.").
96 93
 		Envar("MTG_STATSD_NETWORK").
97 94
 		Default("udp").
98
-		String()
99
-	statsdPrefix = app.Flag("statsd-prefix",
95
+		Enum("udp", "tcp")
96
+	proxyStatsdPrefix = proxyCommand.Flag("statsd-prefix",
100 97
 		"Which bucket prefix should we use for sending stats to statsd.").
101 98
 		Envar("MTG_STATSD_PREFIX").
102 99
 		Default("mtg").
103 100
 		String()
104
-	statsdTagsFormat = app.Flag("statsd-tags-format",
101
+	proxyStatsdTagsFormat = proxyCommand.Flag("statsd-tags-format",
105 102
 		"Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
106 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 107
 		"Tags to use for working with statsd (specified as 'key=value').").
110 108
 		Envar("MTG_STATSD_TAGS").
111 109
 		StringMap()
112
-
113
-	prometheusPrefix = app.Flag("prometheus-prefix",
110
+	proxyPrometheusPrefix = proxyCommand.Flag("prometheus-prefix",
114 111
 		"Which namespace to use to send stats to Prometheus.").
115 112
 		Envar("MTG_PROMETHEUS_PREFIX").
116 113
 		Default("mtg").
117 114
 		String()
118
-
119
-	writeBufferSize = app.Flag("write-buffer",
115
+	proxyWriteBufferSize = proxyCommand.Flag("write-buffer",
120 116
 		"Write buffer size in bytes. You can think about it as a buffer from client to Telegram.").
121 117
 		Short('w').
122 118
 		Envar("MTG_BUFFER_WRITE").
123 119
 		Default("65536").
124 120
 		Uint32()
125
-	readBufferSize = app.Flag("read-buffer",
121
+	proxyReadBufferSize = proxyCommand.Flag("read-buffer",
126 122
 		"Read buffer size in bytes. You can think about it as a buffer from Telegram to client.").
127 123
 		Short('r').
128 124
 		Envar("MTG_BUFFER_READ").
129 125
 		Default("131072").
130 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 128
 		"Max size of antireplay cache in megabytes.").
139 129
 		Envar("MTG_ANTIREPLAY_MAXSIZE").
140 130
 		Default("128").
141 131
 		Int()
142
-	antiReplayEvictionTime = app.Flag("anti-replay-eviction-time",
132
+	proxyAntiReplayEvictionTime = proxyCommand.Flag("anti-replay-eviction-time",
143 133
 		"Eviction time period for obfuscated2 handshakes").
144 134
 		Envar("MTG_ANTIREPLAY_EVICTIONTIME").
145 135
 		Default("168h").
146 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 142
 	rand.Seed(time.Now().UTC().UnixNano())
154 143
 	app.Version(version)
155 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,19 +203,3 @@ func setRLimit() (err error) {
239 203
 
240 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 Visa fil

@@ -0,0 +1,32 @@
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 Visa fil

@@ -0,0 +1,9 @@
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 Visa fil

@@ -0,0 +1,25 @@
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 Visa fil

@@ -0,0 +1,61 @@
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 Visa fil

@@ -0,0 +1,39 @@
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 Visa fil

@@ -1,69 +1,68 @@
1
-package config2
1
+package newconfig
2 2
 
3 3
 import (
4 4
 	"bytes"
5
-	"context"
6 5
 	"encoding/json"
7 6
 	"net"
8 7
 	"strconv"
9
-	"sync"
10 8
 	"time"
11 9
 
12 10
 	"github.com/juju/errors"
11
+	"go.uber.org/zap"
13 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 18
 	switch s {
20
-	case SecretTypeMain:
21
-		return "main"
22
-	case SecretTypeSecured:
19
+	case SecretModeSimple:
20
+		return "simple"
21
+	case SecretModeSecured:
23 22
 		return "secured"
24
-	default:
25
-		return "tls"
26 23
 	}
24
+	return "tls"
27 25
 }
28 26
 
29 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 68
 type BufferSize struct {
@@ -77,8 +76,7 @@ type AntiReplay struct {
77 76
 }
78 77
 
79 78
 type Stats struct {
80
-	Prefix  string `json:"prefix"`
81
-	Enabled bool   `json:"enabled"`
79
+	Prefix string `json:"prefix"`
82 80
 }
83 81
 
84 82
 type StatsdStats struct {
@@ -132,8 +130,7 @@ type Config struct {
132 130
 
133 131
 	Debug      bool       `json:"debug"`
134 132
 	Verbose    bool       `json:"verbose"`
135
-	SecureOnly bool       `json:"secure_only"`
136
-	SecretType SecretType `json:"secret_type"`
133
+	SecretMode SecretMode `json:"secret_mode"`
137 134
 	Secret     []byte     `json:"secret"`
138 135
 	AdTag      []byte     `json:"adtag"`
139 136
 }
@@ -144,44 +141,44 @@ func (c Config) String() string {
144 141
 }
145 142
 
146 143
 type ConfigOpt struct {
147
-	Name  string
148
-	Value interface{}
144
+	Option OptionType
145
+	Value  interface{}
149 146
 }
150 147
 
151 148
 var C = Config{}
152 149
 
153 150
 func Init(options ...ConfigOpt) error { // nolint: gocyclo
154 151
 	for _, opt := range options {
155
-		switch opt.Name {
156
-		case FlagDebug:
152
+		switch opt.Option {
153
+		case OptionTypeDebug:
157 154
 			C.Debug = opt.Value.(bool)
158
-		case FlagVerbose:
155
+		case OptionTypeVerbose:
159 156
 			C.Verbose = opt.Value.(bool)
160
-		case FlagBindIP:
157
+		case OptionTypeBindIP:
161 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 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 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 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 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 178
 			C.StatsdStats.Addr.net = opt.Value.(string)
182
-		case FlagStatsdPrefix:
179
+		case OptionTypeStatsdPrefix:
183 180
 			C.StatsdStats.Prefix = opt.Value.(string)
184
-		case FlagStatsdTagsFormat:
181
+		case OptionTypeStatsdTagsFormat:
185 182
 			value := opt.Value.(string)
186 183
 			switch value {
187 184
 			case "datadog":
@@ -191,41 +188,33 @@ func Init(options ...ConfigOpt) error { // nolint: gocyclo
191 188
 			default:
192 189
 				return errors.Errorf("Incorrect statsd tag %s", value)
193 190
 			}
194
-		case FlagStatsdTags:
191
+		case OptionTypeStatsdTags:
195 192
 			C.StatsdStats.Tags = opt.Value.(map[string]string)
196
-		case FlagPrometheusPrefix:
193
+		case OptionTypePrometheusPrefix:
197 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 200
 			C.AntiReplay.MaxSize = opt.Value.(int)
204
-		case FlagAntiReplayEvictionTime:
201
+		case OptionTypeAntiReplayEvictionTime:
205 202
 			C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
206
-		case FlagSecureOnly:
207
-			C.SecureOnly = opt.Value.(bool)
208
-		case FlagSecret:
203
+		case OptionTypeSecret:
209 204
 			C.Secret = opt.Value.([]byte)
210
-		case FlagAdtag:
205
+		case OptionTypeAdtag:
211 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 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 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 218
 	default:
230 219
 		return errors.New("Incorrect secret")
231 220
 	}
@@ -241,35 +230,29 @@ func InitPublicAddress() error {
241 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 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 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 Visa fil

@@ -1,4 +1,4 @@
1
-package config2
1
+package newconfig
2 2
 
3 3
 import (
4 4
 	"context"
@@ -10,7 +10,6 @@ import (
10 10
 	"time"
11 11
 
12 12
 	"github.com/juju/errors"
13
-	"go.uber.org/zap"
14 13
 )
15 14
 
16 15
 const (
@@ -18,27 +17,23 @@ const (
18 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 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 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 37
 	dialer := &net.Dialer{FallbackDelay: -1}
43 38
 	client := &http.Client{
44 39
 		Jar:     nil,
@@ -50,14 +45,9 @@ func fetchIP(ctx context.Context, network string) (net.IP, error) {
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 49
 	if err != nil {
60
-		if resp.Body != nil {
50
+		if resp != nil {
61 51
 			io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
62 52
 		}
63 53
 		return nil, errors.Annotate(err, "Cannot perform a request")

config2/urls.go → newconfig/urls.go Visa fil

@@ -1,4 +1,4 @@
1
-package config2
1
+package newconfig
2 2
 
3 3
 import (
4 4
 	"encoding/hex"
@@ -20,12 +20,11 @@ type IPURLs struct {
20 20
 
21 21
 func GetURLs() (urls IPURLs) {
22 22
 	secret := ""
23
-	switch C.SecretType {
24
-	case SecretTypeMain, SecretTypeSecured:
23
+	switch C.SecretMode {
24
+	case SecretModeSimple:
25 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 30
 	urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)

+ 95
- 0
newobfuscated2/client_protocol.go Visa fil

@@ -0,0 +1,95 @@
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 Visa fil

@@ -0,0 +1,54 @@
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 Visa fil

@@ -0,0 +1,61 @@
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 Visa fil

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

+ 19
- 0
newprotocol/connection_protocol.go Visa fil

@@ -0,0 +1,19 @@
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 Visa fil

@@ -0,0 +1,27 @@
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 Visa fil

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

+ 93
- 0
newstats/stats.go Visa fil

@@ -0,0 +1,93 @@
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 Visa fil

@@ -0,0 +1,127 @@
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 Visa fil

@@ -0,0 +1,110 @@
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 Visa fil

@@ -0,0 +1,87 @@
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 Visa fil

@@ -0,0 +1,117 @@
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 Visa fil

@@ -0,0 +1,178 @@
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 Visa fil

@@ -0,0 +1,80 @@
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 Visa fil

@@ -0,0 +1,66 @@
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 Visa fil

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

Laddar…
Avbryt
Spara