mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 23:24:01 +03:00
Add new config
This commit is contained in:
@@ -0,0 +1,275 @@
|
|||||||
|
package config2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
statsd "gopkg.in/alexcesaro/statsd.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretType byte
|
||||||
|
|
||||||
|
func (s SecretType) String() string {
|
||||||
|
switch s {
|
||||||
|
case SecretTypeMain:
|
||||||
|
return "main"
|
||||||
|
case SecretTypeSecured:
|
||||||
|
return "secured"
|
||||||
|
default:
|
||||||
|
return "tls"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SecretTypeMain = 1 << iota
|
||||||
|
SecretTypeSecured
|
||||||
|
SecretTypeTLS
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FlagDebug = "debug"
|
||||||
|
FlagVerbose = "verbose"
|
||||||
|
|
||||||
|
FlagBindIP = "bind-ip"
|
||||||
|
FlagBindPort = "bind-port"
|
||||||
|
FlagPublicIPv4 = "public-ipv4"
|
||||||
|
FlagPublicIPv4Port = "public-ipv4-port"
|
||||||
|
FlagPublicIPv6 = "public-ipv6"
|
||||||
|
FlagPublicIPv6Port = "public-ipv6-port"
|
||||||
|
FlagStatsIP = "stats-ip"
|
||||||
|
FlagStatsPort = "stats-port"
|
||||||
|
|
||||||
|
FlagStatsdIP = "statsd-ip"
|
||||||
|
FlagStatsdPort = "statsd-port"
|
||||||
|
FlagStatsdNetwork = "statsd-network"
|
||||||
|
FlagStatsdPrefix = "statsd-prefix"
|
||||||
|
FlagStatsdTagsFormat = "statsd-tags-format"
|
||||||
|
FlagStatsdTags = "statsd-tags"
|
||||||
|
|
||||||
|
FlagPrometheusPrefix = "prometheus-prefix"
|
||||||
|
|
||||||
|
FlagWriteBufferSize = "write-buffer"
|
||||||
|
FlagReadBufferSize = "read-buffer"
|
||||||
|
|
||||||
|
FlagSecureOnly = "secure-only"
|
||||||
|
|
||||||
|
FlagAntiReplayMaxSize = "anti-replay-max-size"
|
||||||
|
FlagAntiReplayEvictionTime = "anti-replay-eviction-time"
|
||||||
|
|
||||||
|
FlagSecret = "secret"
|
||||||
|
FlagAdtag = "adtag"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BufferSize struct {
|
||||||
|
Read int `json:"read"`
|
||||||
|
Write int `json:"write"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AntiReplay struct {
|
||||||
|
MaxSize int `json:"max_size"`
|
||||||
|
EvictionTime time.Duration `json:"duration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
Prefix string `json:"prefix"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatsdStats struct {
|
||||||
|
Stats
|
||||||
|
|
||||||
|
Addr Addr `json:"addr"`
|
||||||
|
Tags map[string]string `json:"tags"`
|
||||||
|
TagsFormat statsd.TagFormat `json:"format"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrometheusStats struct {
|
||||||
|
Stats
|
||||||
|
}
|
||||||
|
|
||||||
|
type Addr struct {
|
||||||
|
IP net.IP `json:"ip"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
net string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Addr) Network() string {
|
||||||
|
if a.net == "" {
|
||||||
|
return "tcp"
|
||||||
|
}
|
||||||
|
return a.net
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Addr) String() string {
|
||||||
|
return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Addr) MarshalJSON() ([]byte, error) {
|
||||||
|
data := map[string]string{
|
||||||
|
"network": a.Network(),
|
||||||
|
"addr": a.String(),
|
||||||
|
}
|
||||||
|
return json.Marshal(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
BufferSize BufferSize `json:"buffer_size"`
|
||||||
|
AntiReplay AntiReplay `json:"anti_replay"`
|
||||||
|
|
||||||
|
ListenAddr Addr `json:"listen_addr"`
|
||||||
|
PublicIPv4Addr Addr `json:"public_ipv4_addr"`
|
||||||
|
PublicIPv6Addr Addr `json:"public_ipv6_addr"`
|
||||||
|
StatsAddr Addr `json:"stats_addr"`
|
||||||
|
|
||||||
|
StatsdStats StatsdStats `json:"stats_statsd"`
|
||||||
|
PrometheusStats PrometheusStats `json:"stats_prometheus"`
|
||||||
|
|
||||||
|
Debug bool `json:"debug"`
|
||||||
|
Verbose bool `json:"verbose"`
|
||||||
|
SecureOnly bool `json:"secure_only"`
|
||||||
|
SecretType SecretType `json:"secret_type"`
|
||||||
|
Secret []byte `json:"secret"`
|
||||||
|
AdTag []byte `json:"adtag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) String() string {
|
||||||
|
data, _ := json.Marshal(c)
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigOpt struct {
|
||||||
|
Name string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var C = Config{}
|
||||||
|
|
||||||
|
func Init(options ...ConfigOpt) error { // nolint: gocyclo
|
||||||
|
for _, opt := range options {
|
||||||
|
switch opt.Name {
|
||||||
|
case FlagDebug:
|
||||||
|
C.Debug = opt.Value.(bool)
|
||||||
|
case FlagVerbose:
|
||||||
|
C.Verbose = opt.Value.(bool)
|
||||||
|
case FlagBindIP:
|
||||||
|
C.ListenAddr.IP = opt.Value.(net.IP)
|
||||||
|
case FlagBindPort:
|
||||||
|
C.ListenAddr.Port = opt.Value.(int)
|
||||||
|
case FlagPublicIPv4:
|
||||||
|
C.PublicIPv4Addr.IP = opt.Value.(net.IP)
|
||||||
|
case FlagPublicIPv4Port:
|
||||||
|
C.PublicIPv4Addr.Port = opt.Value.(int)
|
||||||
|
case FlagPublicIPv6:
|
||||||
|
C.PublicIPv6Addr.IP = opt.Value.(net.IP)
|
||||||
|
case FlagPublicIPv6Port:
|
||||||
|
C.PublicIPv6Addr.Port = opt.Value.(int)
|
||||||
|
case FlagStatsIP:
|
||||||
|
C.StatsAddr.IP = opt.Value.(net.IP)
|
||||||
|
case FlagStatsPort:
|
||||||
|
C.StatsAddr.Port = opt.Value.(int)
|
||||||
|
case FlagStatsdIP:
|
||||||
|
C.StatsdStats.Addr.IP = opt.Value.(net.IP)
|
||||||
|
case FlagStatsdPort:
|
||||||
|
C.StatsdStats.Addr.Port = opt.Value.(int)
|
||||||
|
case FlagStatsdNetwork:
|
||||||
|
C.StatsdStats.Addr.net = opt.Value.(string)
|
||||||
|
case FlagStatsdPrefix:
|
||||||
|
C.StatsdStats.Prefix = opt.Value.(string)
|
||||||
|
case FlagStatsdTagsFormat:
|
||||||
|
value := opt.Value.(string)
|
||||||
|
switch value {
|
||||||
|
case "datadog":
|
||||||
|
C.StatsdStats.TagsFormat = statsd.Datadog
|
||||||
|
case "influxdb":
|
||||||
|
C.StatsdStats.TagsFormat = statsd.InfluxDB
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Incorrect statsd tag %s", value)
|
||||||
|
}
|
||||||
|
case FlagStatsdTags:
|
||||||
|
C.StatsdStats.Tags = opt.Value.(map[string]string)
|
||||||
|
case FlagPrometheusPrefix:
|
||||||
|
C.PrometheusStats.Prefix = opt.Value.(string)
|
||||||
|
case FlagWriteBufferSize:
|
||||||
|
C.BufferSize.Write = opt.Value.(int)
|
||||||
|
case FlagReadBufferSize:
|
||||||
|
C.BufferSize.Read = opt.Value.(int)
|
||||||
|
case FlagAntiReplayMaxSize:
|
||||||
|
C.AntiReplay.MaxSize = opt.Value.(int)
|
||||||
|
case FlagAntiReplayEvictionTime:
|
||||||
|
C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
|
||||||
|
case FlagSecureOnly:
|
||||||
|
C.SecureOnly = opt.Value.(bool)
|
||||||
|
case FlagSecret:
|
||||||
|
C.Secret = opt.Value.([]byte)
|
||||||
|
case FlagAdtag:
|
||||||
|
C.AdTag = opt.Value.([]byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultStatsdTags statsd.TagFormat
|
||||||
|
if C.StatsdStats.TagsFormat == defaultStatsdTags {
|
||||||
|
C.StatsdStats.TagsFormat = statsd.Datadog
|
||||||
|
}
|
||||||
|
if C.StatsdStats.Addr.net == "" {
|
||||||
|
C.StatsdStats.Addr.net = "udp"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(C.Secret) == 17 && bytes.HasPrefix(C.Secret, []byte{0xdd}):
|
||||||
|
C.SecretType = SecretTypeSecured
|
||||||
|
C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
|
||||||
|
case len(C.Secret) == 16:
|
||||||
|
C.SecretType = SecretTypeMain
|
||||||
|
default:
|
||||||
|
return errors.New("Incorrect secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitPublicAddress() error {
|
||||||
|
if C.PublicIPv4Addr.Port == 0 {
|
||||||
|
C.PublicIPv4Addr.Port = C.ListenAddr.Port
|
||||||
|
}
|
||||||
|
if C.PublicIPv6Addr.Port == 0 {
|
||||||
|
C.PublicIPv6Addr.Port = C.ListenAddr.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
|
if C.PublicIPv4Addr.IP == nil {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
getGlobalIPv4(ctx, cancel)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if C.PublicIPv6Addr.IP == nil {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
getGlobalIPv6(ctx, cancel)
|
||||||
|
wg.Done()
|
||||||
|
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package config2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ifconfigAddress = "https://ifconfig.co/ip"
|
||||||
|
ifconfigTimeout = 10 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
func getGlobalIPv4(ctx context.Context, cancel context.CancelFunc) {
|
||||||
|
ip, err := fetchIP(ctx, "tcp4")
|
||||||
|
if err != nil || ip.To4() == nil {
|
||||||
|
cancel()
|
||||||
|
zap.S().Errorw("Cannot find public ipv4 address", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
C.PublicIPv4Addr.IP = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGlobalIPv6(ctx context.Context, cancel context.CancelFunc) {
|
||||||
|
ip, err := fetchIP(ctx, "tcp6")
|
||||||
|
if err != nil || ip.To4() != nil {
|
||||||
|
cancel()
|
||||||
|
zap.S().Errorw("Cannot find public ipv6 address", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
C.PublicIPv6Addr.IP = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchIP(ctx context.Context, network string) (net.IP, error) {
|
||||||
|
dialer := &net.Dialer{FallbackDelay: -1}
|
||||||
|
client := &http.Client{
|
||||||
|
Jar: nil,
|
||||||
|
Timeout: ifconfigTimeout,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
|
||||||
|
return dialer.DialContext(ctx, network, addr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", ifconfigAddress, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
if resp.Body != nil {
|
||||||
|
io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
|
||||||
|
}
|
||||||
|
return nil, errors.Annotate(err, "Cannot perform a request")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
respDataBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotate(err, "Cannot read response body")
|
||||||
|
}
|
||||||
|
respData := strings.TrimSpace(string(respDataBytes))
|
||||||
|
|
||||||
|
ip := net.ParseIP(respData)
|
||||||
|
if ip == nil {
|
||||||
|
return nil, errors.Errorf("ifconfig.co returns incorrect IP %s", respData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package config2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
type URLs struct {
|
||||||
|
TG string `json:"tg_url"`
|
||||||
|
TMe string `json:"tme_url"`
|
||||||
|
TGQRCode string `json:"tg_qrcode"`
|
||||||
|
TMeQRCode string `json:"tme_qrcode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPURLs struct {
|
||||||
|
IPv4 URLs `json:"ipv4"`
|
||||||
|
IPv6 URLs `json:"ipv6"`
|
||||||
|
BotSecret string `json:"secret_for_mtproxybot"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetURLs() (urls IPURLs) {
|
||||||
|
secret := ""
|
||||||
|
switch C.SecretType {
|
||||||
|
case SecretTypeMain, SecretTypeSecured:
|
||||||
|
secret = hex.EncodeToString(C.Secret)
|
||||||
|
if C.SecureOnly {
|
||||||
|
secret = "dd" + secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)
|
||||||
|
urls.IPv6 = makeURLs(&C.PublicIPv6Addr, secret)
|
||||||
|
urls.BotSecret = secret
|
||||||
|
|
||||||
|
return urls
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeURLs(addr *Addr, secret string) (urls URLs) {
|
||||||
|
values := url.Values{}
|
||||||
|
values.Set("address", addr.String())
|
||||||
|
values.Set("secret", secret)
|
||||||
|
|
||||||
|
urls.TG = makeTGURL(values)
|
||||||
|
urls.TMe = makeTMeURL(values)
|
||||||
|
urls.TGQRCode = makeQRCodeURL(urls.TG)
|
||||||
|
urls.TMeQRCode = makeQRCodeURL(urls.TG)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTGURL(values url.Values) string {
|
||||||
|
tgURL := url.URL{
|
||||||
|
Scheme: "tg",
|
||||||
|
Host: "proxy",
|
||||||
|
RawQuery: values.Encode(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return tgURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTMeURL(values url.Values) string {
|
||||||
|
tMeURL := url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: "t.me",
|
||||||
|
Path: "proxy",
|
||||||
|
RawQuery: values.Encode(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return tMeURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeQRCodeURL(data string) string {
|
||||||
|
qr := url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: "api.qrserver.com",
|
||||||
|
Path: "v1/create-qr-code",
|
||||||
|
}
|
||||||
|
|
||||||
|
values := url.Values{}
|
||||||
|
values.Set("qzone", "4")
|
||||||
|
values.Set("format", "svg")
|
||||||
|
values.Set("data", data)
|
||||||
|
qr.RawQuery = values.Encode()
|
||||||
|
|
||||||
|
return qr.String()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user