9seconds 6 лет назад
Родитель
Сommit
09c7ce45d2
3 измененных файлов: 440 добавлений и 0 удалений
  1. 275
    0
      config2/config.go
  2. 79
    0
      config2/global_ips.go
  3. 86
    0
      config2/urls.go

+ 275
- 0
config2/config.go Просмотреть файл

@@ -0,0 +1,275 @@
1
+package config2
2
+
3
+import (
4
+	"bytes"
5
+	"context"
6
+	"encoding/json"
7
+	"net"
8
+	"strconv"
9
+	"sync"
10
+	"time"
11
+
12
+	"github.com/juju/errors"
13
+	statsd "gopkg.in/alexcesaro/statsd.v2"
14
+)
15
+
16
+type SecretType byte
17
+
18
+func (s SecretType) String() string {
19
+	switch s {
20
+	case SecretTypeMain:
21
+		return "main"
22
+	case SecretTypeSecured:
23
+		return "secured"
24
+	default:
25
+		return "tls"
26
+	}
27
+}
28
+
29
+const (
30
+	SecretTypeMain = 1 << iota
31
+	SecretTypeSecured
32
+	SecretTypeTLS
33
+)
34
+
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"
56
+
57
+	FlagWriteBufferSize = "write-buffer"
58
+	FlagReadBufferSize  = "read-buffer"
59
+
60
+	FlagSecureOnly = "secure-only"
61
+
62
+	FlagAntiReplayMaxSize      = "anti-replay-max-size"
63
+	FlagAntiReplayEvictionTime = "anti-replay-eviction-time"
64
+
65
+	FlagSecret = "secret"
66
+	FlagAdtag  = "adtag"
67
+)
68
+
69
+type BufferSize struct {
70
+	Read  int `json:"read"`
71
+	Write int `json:"write"`
72
+}
73
+
74
+type AntiReplay struct {
75
+	MaxSize      int           `json:"max_size"`
76
+	EvictionTime time.Duration `json:"duration"`
77
+}
78
+
79
+type Stats struct {
80
+	Prefix  string `json:"prefix"`
81
+	Enabled bool   `json:"enabled"`
82
+}
83
+
84
+type StatsdStats struct {
85
+	Stats
86
+
87
+	Addr       Addr              `json:"addr"`
88
+	Tags       map[string]string `json:"tags"`
89
+	TagsFormat statsd.TagFormat  `json:"format"`
90
+}
91
+
92
+type PrometheusStats struct {
93
+	Stats
94
+}
95
+
96
+type Addr struct {
97
+	IP   net.IP `json:"ip"`
98
+	Port int    `json:"port"`
99
+	net  string
100
+}
101
+
102
+func (a Addr) Network() string {
103
+	if a.net == "" {
104
+		return "tcp"
105
+	}
106
+	return a.net
107
+}
108
+
109
+func (a Addr) String() string {
110
+	return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
111
+}
112
+
113
+func (a Addr) MarshalJSON() ([]byte, error) {
114
+	data := map[string]string{
115
+		"network": a.Network(),
116
+		"addr":    a.String(),
117
+	}
118
+	return json.Marshal(data)
119
+}
120
+
121
+type Config struct {
122
+	BufferSize BufferSize `json:"buffer_size"`
123
+	AntiReplay AntiReplay `json:"anti_replay"`
124
+
125
+	ListenAddr     Addr `json:"listen_addr"`
126
+	PublicIPv4Addr Addr `json:"public_ipv4_addr"`
127
+	PublicIPv6Addr Addr `json:"public_ipv6_addr"`
128
+	StatsAddr      Addr `json:"stats_addr"`
129
+
130
+	StatsdStats     StatsdStats     `json:"stats_statsd"`
131
+	PrometheusStats PrometheusStats `json:"stats_prometheus"`
132
+
133
+	Debug      bool       `json:"debug"`
134
+	Verbose    bool       `json:"verbose"`
135
+	SecureOnly bool       `json:"secure_only"`
136
+	SecretType SecretType `json:"secret_type"`
137
+	Secret     []byte     `json:"secret"`
138
+	AdTag      []byte     `json:"adtag"`
139
+}
140
+
141
+func (c Config) String() string {
142
+	data, _ := json.Marshal(c)
143
+	return string(data)
144
+}
145
+
146
+type ConfigOpt struct {
147
+	Name  string
148
+	Value interface{}
149
+}
150
+
151
+var C = Config{}
152
+
153
+func Init(options ...ConfigOpt) error { // nolint: gocyclo
154
+	for _, opt := range options {
155
+		switch opt.Name {
156
+		case FlagDebug:
157
+			C.Debug = opt.Value.(bool)
158
+		case FlagVerbose:
159
+			C.Verbose = opt.Value.(bool)
160
+		case FlagBindIP:
161
+			C.ListenAddr.IP = opt.Value.(net.IP)
162
+		case FlagBindPort:
163
+			C.ListenAddr.Port = opt.Value.(int)
164
+		case FlagPublicIPv4:
165
+			C.PublicIPv4Addr.IP = opt.Value.(net.IP)
166
+		case FlagPublicIPv4Port:
167
+			C.PublicIPv4Addr.Port = opt.Value.(int)
168
+		case FlagPublicIPv6:
169
+			C.PublicIPv6Addr.IP = opt.Value.(net.IP)
170
+		case FlagPublicIPv6Port:
171
+			C.PublicIPv6Addr.Port = opt.Value.(int)
172
+		case FlagStatsIP:
173
+			C.StatsAddr.IP = opt.Value.(net.IP)
174
+		case FlagStatsPort:
175
+			C.StatsAddr.Port = opt.Value.(int)
176
+		case FlagStatsdIP:
177
+			C.StatsdStats.Addr.IP = opt.Value.(net.IP)
178
+		case FlagStatsdPort:
179
+			C.StatsdStats.Addr.Port = opt.Value.(int)
180
+		case FlagStatsdNetwork:
181
+			C.StatsdStats.Addr.net = opt.Value.(string)
182
+		case FlagStatsdPrefix:
183
+			C.StatsdStats.Prefix = opt.Value.(string)
184
+		case FlagStatsdTagsFormat:
185
+			value := opt.Value.(string)
186
+			switch value {
187
+			case "datadog":
188
+				C.StatsdStats.TagsFormat = statsd.Datadog
189
+			case "influxdb":
190
+				C.StatsdStats.TagsFormat = statsd.InfluxDB
191
+			default:
192
+				return errors.Errorf("Incorrect statsd tag %s", value)
193
+			}
194
+		case FlagStatsdTags:
195
+			C.StatsdStats.Tags = opt.Value.(map[string]string)
196
+		case FlagPrometheusPrefix:
197
+			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:
203
+			C.AntiReplay.MaxSize = opt.Value.(int)
204
+		case FlagAntiReplayEvictionTime:
205
+			C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
206
+		case FlagSecureOnly:
207
+			C.SecureOnly = opt.Value.(bool)
208
+		case FlagSecret:
209
+			C.Secret = opt.Value.([]byte)
210
+		case FlagAdtag:
211
+			C.AdTag = opt.Value.([]byte)
212
+		}
213
+	}
214
+
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 {
224
+	case len(C.Secret) == 17 && bytes.HasPrefix(C.Secret, []byte{0xdd}):
225
+		C.SecretType = SecretTypeSecured
226
+		C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
227
+	case len(C.Secret) == 16:
228
+		C.SecretType = SecretTypeMain
229
+	default:
230
+		return errors.New("Incorrect secret")
231
+	}
232
+
233
+	return nil
234
+}
235
+
236
+func InitPublicAddress() error {
237
+	if C.PublicIPv4Addr.Port == 0 {
238
+		C.PublicIPv4Addr.Port = C.ListenAddr.Port
239
+	}
240
+	if C.PublicIPv6Addr.Port == 0 {
241
+		C.PublicIPv6Addr.Port = C.ListenAddr.Port
242
+	}
243
+
244
+	ctx, cancel := context.WithCancel(context.Background())
245
+	defer cancel()
246
+	wg := &sync.WaitGroup{}
247
+	done := make(chan struct{})
248
+
249
+	if C.PublicIPv4Addr.IP == nil {
250
+		wg.Add(1)
251
+		go func() {
252
+			getGlobalIPv4(ctx, cancel)
253
+			wg.Done()
254
+		}()
255
+	}
256
+	if C.PublicIPv6Addr.IP == nil {
257
+		wg.Add(1)
258
+		go func() {
259
+			getGlobalIPv6(ctx, cancel)
260
+			wg.Done()
261
+
262
+		}()
263
+	}
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()
274
+	}
275
+}

+ 79
- 0
config2/global_ips.go Просмотреть файл

@@ -0,0 +1,79 @@
1
+package config2
2
+
3
+import (
4
+	"context"
5
+	"io"
6
+	"io/ioutil"
7
+	"net"
8
+	"net/http"
9
+	"strings"
10
+	"time"
11
+
12
+	"github.com/juju/errors"
13
+	"go.uber.org/zap"
14
+)
15
+
16
+const (
17
+	ifconfigAddress = "https://ifconfig.co/ip"
18
+	ifconfigTimeout = 10 * time.Second
19
+)
20
+
21
+func getGlobalIPv4(ctx context.Context, cancel context.CancelFunc) {
22
+	ip, err := fetchIP(ctx, "tcp4")
23
+	if err != nil || ip.To4() == nil {
24
+		cancel()
25
+		zap.S().Errorw("Cannot find public ipv4 address", "error", err)
26
+		return
27
+	}
28
+	C.PublicIPv4Addr.IP = ip
29
+}
30
+
31
+func getGlobalIPv6(ctx context.Context, cancel context.CancelFunc) {
32
+	ip, err := fetchIP(ctx, "tcp6")
33
+	if err != nil || ip.To4() != nil {
34
+		cancel()
35
+		zap.S().Errorw("Cannot find public ipv6 address", "error", err)
36
+		return
37
+	}
38
+	C.PublicIPv6Addr.IP = ip
39
+}
40
+
41
+func fetchIP(ctx context.Context, network string) (net.IP, error) {
42
+	dialer := &net.Dialer{FallbackDelay: -1}
43
+	client := &http.Client{
44
+		Jar:     nil,
45
+		Timeout: ifconfigTimeout,
46
+		Transport: &http.Transport{
47
+			DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
48
+				return dialer.DialContext(ctx, network, addr)
49
+			},
50
+		},
51
+	}
52
+
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))
59
+	if err != nil {
60
+		if resp.Body != nil {
61
+			io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
62
+		}
63
+		return nil, errors.Annotate(err, "Cannot perform a request")
64
+	}
65
+	defer resp.Body.Close() // nolint: errcheck
66
+
67
+	respDataBytes, err := ioutil.ReadAll(resp.Body)
68
+	if err != nil {
69
+		return nil, errors.Annotate(err, "Cannot read response body")
70
+	}
71
+	respData := strings.TrimSpace(string(respDataBytes))
72
+
73
+	ip := net.ParseIP(respData)
74
+	if ip == nil {
75
+		return nil, errors.Errorf("ifconfig.co returns incorrect IP %s", respData)
76
+	}
77
+
78
+	return ip, nil
79
+}

+ 86
- 0
config2/urls.go Просмотреть файл

@@ -0,0 +1,86 @@
1
+package config2
2
+
3
+import (
4
+	"encoding/hex"
5
+	"net/url"
6
+)
7
+
8
+type URLs struct {
9
+	TG        string `json:"tg_url"`
10
+	TMe       string `json:"tme_url"`
11
+	TGQRCode  string `json:"tg_qrcode"`
12
+	TMeQRCode string `json:"tme_qrcode"`
13
+}
14
+
15
+type IPURLs struct {
16
+	IPv4      URLs   `json:"ipv4"`
17
+	IPv6      URLs   `json:"ipv6"`
18
+	BotSecret string `json:"secret_for_mtproxybot"`
19
+}
20
+
21
+func GetURLs() (urls IPURLs) {
22
+	secret := ""
23
+	switch C.SecretType {
24
+	case SecretTypeMain, SecretTypeSecured:
25
+		secret = hex.EncodeToString(C.Secret)
26
+		if C.SecureOnly {
27
+			secret = "dd" + secret
28
+		}
29
+	}
30
+
31
+	urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)
32
+	urls.IPv6 = makeURLs(&C.PublicIPv6Addr, secret)
33
+	urls.BotSecret = secret
34
+
35
+	return urls
36
+}
37
+
38
+func makeURLs(addr *Addr, secret string) (urls URLs) {
39
+	values := url.Values{}
40
+	values.Set("address", addr.String())
41
+	values.Set("secret", secret)
42
+
43
+	urls.TG = makeTGURL(values)
44
+	urls.TMe = makeTMeURL(values)
45
+	urls.TGQRCode = makeQRCodeURL(urls.TG)
46
+	urls.TMeQRCode = makeQRCodeURL(urls.TG)
47
+
48
+	return
49
+}
50
+
51
+func makeTGURL(values url.Values) string {
52
+	tgURL := url.URL{
53
+		Scheme:   "tg",
54
+		Host:     "proxy",
55
+		RawQuery: values.Encode(),
56
+	}
57
+
58
+	return tgURL.String()
59
+}
60
+
61
+func makeTMeURL(values url.Values) string {
62
+	tMeURL := url.URL{
63
+		Scheme:   "https",
64
+		Host:     "t.me",
65
+		Path:     "proxy",
66
+		RawQuery: values.Encode(),
67
+	}
68
+
69
+	return tMeURL.String()
70
+}
71
+
72
+func makeQRCodeURL(data string) string {
73
+	qr := url.URL{
74
+		Scheme: "https",
75
+		Host:   "api.qrserver.com",
76
+		Path:   "v1/create-qr-code",
77
+	}
78
+
79
+	values := url.Values{}
80
+	values.Set("qzone", "4")
81
+	values.Set("format", "svg")
82
+	values.Set("data", data)
83
+	qr.RawQuery = values.Encode()
84
+
85
+	return qr.String()
86
+}

Загрузка…
Отмена
Сохранить