Add new config

This commit is contained in:
9seconds
2019-08-26 18:23:36 +03:00
parent 199eed0f18
commit 09c7ce45d2
3 changed files with 440 additions and 0 deletions
+275
View File
@@ -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()
}
}
+79
View File
@@ -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
}
+86
View File
@@ -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()
}