mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 14:04:02 +03:00
Reworked base
This commit is contained in:
@@ -15,6 +15,8 @@ require (
|
|||||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
|
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
|
||||||
github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect
|
github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
github.com/pkg/errors v0.8.1 // indirect
|
github.com/pkg/errors v0.8.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.1.0
|
github.com/prometheus/client_golang v1.1.0
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
|
||||||
|
|||||||
@@ -1,23 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
"go.uber.org/zap"
|
|
||||||
"go.uber.org/zap/zapcore"
|
|
||||||
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/config"
|
"github.com/9seconds/mtg/newcli"
|
||||||
"github.com/9seconds/mtg/ntp"
|
"github.com/9seconds/mtg/newconfig"
|
||||||
"github.com/9seconds/mtg/proxy"
|
|
||||||
"github.com/9seconds/mtg/stats"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "dev" // this has to be set by build ld flags
|
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
|
|||||||
var (
|
var (
|
||||||
app = kingpin.New("mtg", "Simple MTPROTO proxy.")
|
app = kingpin.New("mtg", "Simple MTPROTO proxy.")
|
||||||
|
|
||||||
debug = app.Flag("debug",
|
generateSecretCommand = app.Command("generate-secret",
|
||||||
|
"Generate new secret")
|
||||||
|
generateSecretType = generateSecretCommand.Arg("type",
|
||||||
|
"A type of secret to generate. Valid options are 'simple', 'secured' and 'tls'").
|
||||||
|
Required().
|
||||||
|
Enum("simple", "secured", "tls")
|
||||||
|
|
||||||
|
proxyCommand = app.Command("proxy",
|
||||||
|
"Run new proxy instance")
|
||||||
|
proxyDebug = proxyCommand.Flag("debug",
|
||||||
"Run in debug mode.").
|
"Run in debug mode.").
|
||||||
Short('d').
|
Short('d').
|
||||||
Envar("MTG_DEBUG").
|
Envar("MTG_DEBUG").
|
||||||
Bool()
|
Bool()
|
||||||
verbose = app.Flag("verbose",
|
proxyVerbose = proxyCommand.Flag("verbose",
|
||||||
"Run in verbose mode.").
|
"Run in verbose mode.").
|
||||||
Short('v').
|
Short('v').
|
||||||
Envar("MTG_VERBOSE").
|
Envar("MTG_VERBOSE").
|
||||||
Bool()
|
Bool()
|
||||||
|
proxyBindIP = proxyCommand.Flag("bind-ip",
|
||||||
bindIP = app.Flag("bind-ip",
|
|
||||||
"Which IP to bind to.").
|
"Which IP to bind to.").
|
||||||
Short('b').
|
Short('b').
|
||||||
Envar("MTG_IP").
|
Envar("MTG_IP").
|
||||||
Default("127.0.0.1").
|
Default("127.0.0.1").
|
||||||
IP()
|
IP()
|
||||||
bindPort = app.Flag("bind-port",
|
proxyBindPort = proxyCommand.Flag("bind-port",
|
||||||
"Which port to bind to.").
|
"Which port to bind to.").
|
||||||
Short('p').
|
Short('p').
|
||||||
Envar("MTG_PORT").
|
Envar("MTG_PORT").
|
||||||
Default("3128").
|
Default("3128").
|
||||||
Uint16()
|
Uint16()
|
||||||
|
proxyPublicIPv4 = proxyCommand.Flag("public-ipv4",
|
||||||
publicIPv4 = app.Flag("public-ipv4",
|
|
||||||
"Which IPv4 address is public.").
|
"Which IPv4 address is public.").
|
||||||
Short('4').
|
Short('4').
|
||||||
Envar("MTG_IPV4").
|
Envar("MTG_IPV4").
|
||||||
IP()
|
IP()
|
||||||
publicIPv4Port = app.Flag("public-ipv4-port",
|
proxyPublicIPv4Port = proxyCommand.Flag("public-ipv4-port",
|
||||||
"Which IPv4 port is public. Default is 'bind-port' value.").
|
"Which IPv4 port is public. Default is 'bind-port' value.").
|
||||||
Envar("MTG_IPV4_PORT").
|
Envar("MTG_IPV4_PORT").
|
||||||
Uint16()
|
Uint16()
|
||||||
|
proxyPublicIPv6 = proxyCommand.Flag("public-ipv6",
|
||||||
publicIPv6 = app.Flag("public-ipv6",
|
|
||||||
"Which IPv6 address is public.").
|
"Which IPv6 address is public.").
|
||||||
Short('6').
|
Short('6').
|
||||||
Envar("MTG_IPV6").
|
Envar("MTG_IPV6").
|
||||||
IP()
|
IP()
|
||||||
publicIPv6Port = app.Flag("public-ipv6-port",
|
proxyPublicIPv6Port = proxyCommand.Flag("public-ipv6-port",
|
||||||
"Which IPv6 port is public. Default is 'bind-port' value.").
|
"Which IPv6 port is public. Default is 'bind-port' value.").
|
||||||
Envar("MTG_IPV6_PORT").
|
Envar("MTG_IPV6_PORT").
|
||||||
Uint16()
|
Uint16()
|
||||||
|
proxyStatsIP = proxyCommand.Flag("stats-ip",
|
||||||
statsIP = app.Flag("stats-ip",
|
|
||||||
"Which IP bind stats server to.").
|
"Which IP bind stats server to.").
|
||||||
Short('t').
|
Short('t').
|
||||||
Envar("MTG_STATS_IP").
|
Envar("MTG_STATS_IP").
|
||||||
Default("127.0.0.1").
|
Default("127.0.0.1").
|
||||||
IP()
|
IP()
|
||||||
statsPort = app.Flag("stats-port",
|
proxyStatsPort = proxyCommand.Flag("stats-port",
|
||||||
"Which port bind stats to.").
|
"Which port bind stats to.").
|
||||||
Short('q').
|
Short('q').
|
||||||
Envar("MTG_STATS_PORT").
|
Envar("MTG_STATS_PORT").
|
||||||
Default("3129").
|
Default("3129").
|
||||||
Uint16()
|
Uint16()
|
||||||
|
proxyStatsdIP = proxyCommand.Flag("statsd-ip",
|
||||||
statsdIP = app.Flag("statsd-ip",
|
|
||||||
"Which IP should we use for working with statsd.").
|
"Which IP should we use for working with statsd.").
|
||||||
Envar("MTG_STATSD_IP").
|
Envar("MTG_STATSD_IP").
|
||||||
String()
|
IP()
|
||||||
statsdPort = app.Flag("statsd-port",
|
proxyStatsdPort = proxyCommand.Flag("statsd-port",
|
||||||
"Which port should we use for working with statsd.").
|
"Which port should we use for working with statsd.").
|
||||||
Envar("MTG_STATSD_PORT").
|
Envar("MTG_STATSD_PORT").
|
||||||
Default("8125").
|
Default("8125").
|
||||||
Uint16()
|
Uint16()
|
||||||
statsdNetwork = app.Flag("statsd-network",
|
proxyStatsdNetwork = proxyCommand.Flag("statsd-network",
|
||||||
"Which network is used to work with statsd. Only 'tcp' and 'udp' are supported.").
|
"Which network is used to work with statsd. Only 'tcp' and 'udp' are supported.").
|
||||||
Envar("MTG_STATSD_NETWORK").
|
Envar("MTG_STATSD_NETWORK").
|
||||||
Default("udp").
|
Default("udp").
|
||||||
String()
|
Enum("udp", "tcp")
|
||||||
statsdPrefix = app.Flag("statsd-prefix",
|
proxyStatsdPrefix = proxyCommand.Flag("statsd-prefix",
|
||||||
"Which bucket prefix should we use for sending stats to statsd.").
|
"Which bucket prefix should we use for sending stats to statsd.").
|
||||||
Envar("MTG_STATSD_PREFIX").
|
Envar("MTG_STATSD_PREFIX").
|
||||||
Default("mtg").
|
Default("mtg").
|
||||||
String()
|
String()
|
||||||
statsdTagsFormat = app.Flag("statsd-tags-format",
|
proxyStatsdTagsFormat = proxyCommand.Flag("statsd-tags-format",
|
||||||
"Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
|
"Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
|
||||||
Envar("MTG_STATSD_TAGS_FORMAT").
|
Envar("MTG_STATSD_TAGS_FORMAT").
|
||||||
String()
|
Default("influxdb").
|
||||||
statsdTags = app.Flag("statsd-tags",
|
Enum("datadog", "influxdb")
|
||||||
|
proxyStatsdTags = proxyCommand.Flag("statsd-tags",
|
||||||
"Tags to use for working with statsd (specified as 'key=value').").
|
"Tags to use for working with statsd (specified as 'key=value').").
|
||||||
Envar("MTG_STATSD_TAGS").
|
Envar("MTG_STATSD_TAGS").
|
||||||
StringMap()
|
StringMap()
|
||||||
|
proxyPrometheusPrefix = proxyCommand.Flag("prometheus-prefix",
|
||||||
prometheusPrefix = app.Flag("prometheus-prefix",
|
|
||||||
"Which namespace to use to send stats to Prometheus.").
|
"Which namespace to use to send stats to Prometheus.").
|
||||||
Envar("MTG_PROMETHEUS_PREFIX").
|
Envar("MTG_PROMETHEUS_PREFIX").
|
||||||
Default("mtg").
|
Default("mtg").
|
||||||
String()
|
String()
|
||||||
|
proxyWriteBufferSize = proxyCommand.Flag("write-buffer",
|
||||||
writeBufferSize = app.Flag("write-buffer",
|
|
||||||
"Write buffer size in bytes. You can think about it as a buffer from client to Telegram.").
|
"Write buffer size in bytes. You can think about it as a buffer from client to Telegram.").
|
||||||
Short('w').
|
Short('w').
|
||||||
Envar("MTG_BUFFER_WRITE").
|
Envar("MTG_BUFFER_WRITE").
|
||||||
Default("65536").
|
Default("65536").
|
||||||
Uint32()
|
Uint32()
|
||||||
readBufferSize = app.Flag("read-buffer",
|
proxyReadBufferSize = proxyCommand.Flag("read-buffer",
|
||||||
"Read buffer size in bytes. You can think about it as a buffer from Telegram to client.").
|
"Read buffer size in bytes. You can think about it as a buffer from Telegram to client.").
|
||||||
Short('r').
|
Short('r').
|
||||||
Envar("MTG_BUFFER_READ").
|
Envar("MTG_BUFFER_READ").
|
||||||
Default("131072").
|
Default("131072").
|
||||||
Uint32()
|
Uint32()
|
||||||
secureOnly = app.Flag("secure-only",
|
proxyAntiReplayMaxSize = proxyCommand.Flag("anti-replay-max-size",
|
||||||
"Support clients with dd-secrets only.").
|
|
||||||
Short('s').
|
|
||||||
Envar("MTG_SECURE_ONLY").
|
|
||||||
Bool()
|
|
||||||
|
|
||||||
antiReplayMaxSize = app.Flag("anti-replay-max-size",
|
|
||||||
"Max size of antireplay cache in megabytes.").
|
"Max size of antireplay cache in megabytes.").
|
||||||
Envar("MTG_ANTIREPLAY_MAXSIZE").
|
Envar("MTG_ANTIREPLAY_MAXSIZE").
|
||||||
Default("128").
|
Default("128").
|
||||||
Int()
|
Int()
|
||||||
antiReplayEvictionTime = app.Flag("anti-replay-eviction-time",
|
proxyAntiReplayEvictionTime = proxyCommand.Flag("anti-replay-eviction-time",
|
||||||
"Eviction time period for obfuscated2 handshakes").
|
"Eviction time period for obfuscated2 handshakes").
|
||||||
Envar("MTG_ANTIREPLAY_EVICTIONTIME").
|
Envar("MTG_ANTIREPLAY_EVICTIONTIME").
|
||||||
Default("168h").
|
Default("168h").
|
||||||
Duration()
|
Duration()
|
||||||
|
proxySecret = proxyCommand.Arg("secret", "Secret of this proxy.").Required().HexBytes()
|
||||||
secret = app.Arg("secret", "Secret of this proxy.").Required().HexBytes()
|
proxyAdtag = proxyCommand.Arg("adtag", "ADTag of the proxy.").HexBytes()
|
||||||
adtag = app.Arg("adtag", "ADTag of the proxy.").HexBytes()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() { // nolint: gocyclo
|
func main() {
|
||||||
rand.Seed(time.Now().UTC().UnixNano())
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
app.Version(version)
|
app.Version(version)
|
||||||
app.HelpFlag.Short('h')
|
app.HelpFlag.Short('h')
|
||||||
|
|
||||||
kingpin.MustParse(app.Parse(os.Args[1:]))
|
if err := setRLimit(); err != nil {
|
||||||
|
newcli.Fatal(err.Error())
|
||||||
err := setRLimit()
|
|
||||||
if err != nil {
|
|
||||||
usage(err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conf, err := config.NewConfig(*debug, *verbose,
|
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
|
||||||
*writeBufferSize, *readBufferSize,
|
case generateSecretCommand.FullCommand():
|
||||||
*bindIP, *publicIPv4, *publicIPv6, *statsIP,
|
newcli.Generate(*generateSecretType)
|
||||||
*bindPort, *publicIPv4Port, *publicIPv6Port, *statsPort, *statsdPort,
|
|
||||||
*statsdIP, *statsdNetwork, *statsdPrefix, *statsdTagsFormat,
|
|
||||||
*statsdTags, *prometheusPrefix, *secureOnly,
|
|
||||||
*antiReplayMaxSize, *antiReplayEvictionTime,
|
|
||||||
*secret, *adtag,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
usage(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
atom := zap.NewAtomicLevel()
|
case proxyCommand.FullCommand():
|
||||||
switch {
|
err := newconfig.Init(
|
||||||
case conf.Debug:
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeDebug, Value: *proxyDebug},
|
||||||
atom.SetLevel(zapcore.DebugLevel)
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeVerbose, Value: *proxyVerbose},
|
||||||
case conf.Verbose:
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeBindIP, Value: *proxyBindIP},
|
||||||
atom.SetLevel(zapcore.InfoLevel)
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeBindPort, Value: *proxyBindPort},
|
||||||
default:
|
newconfig.ConfigOpt{Option: newconfig.OptionTypePublicIPv4, Value: *proxyPublicIPv4},
|
||||||
atom.SetLevel(zapcore.ErrorLevel)
|
newconfig.ConfigOpt{Option: newconfig.OptionTypePublicIPv4Port, Value: *proxyPublicIPv4Port},
|
||||||
}
|
newconfig.ConfigOpt{Option: newconfig.OptionTypePublicIPv6, Value: *proxyPublicIPv6},
|
||||||
encoderCfg := zap.NewProductionEncoderConfig()
|
newconfig.ConfigOpt{Option: newconfig.OptionTypePublicIPv6Port, Value: *proxyPublicIPv6Port},
|
||||||
logger := zap.New(zapcore.NewCore(
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsIP, Value: *proxyStatsIP},
|
||||||
zapcore.NewJSONEncoder(encoderCfg),
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsPort, Value: *proxyStatsPort},
|
||||||
zapcore.Lock(os.Stderr),
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdIP, Value: *proxyStatsdIP},
|
||||||
atom,
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdPort, Value: *proxyStatsdPort},
|
||||||
))
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdNetwork, Value: *proxyStatsdNetwork},
|
||||||
zap.ReplaceGlobals(logger)
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdPrefix, Value: *proxyStatsdPrefix},
|
||||||
defer logger.Sync() // nolint: errcheck
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdTagsFormat, Value: *proxyStatsdTagsFormat},
|
||||||
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeStatsdTags, Value: *proxyStatsdTags},
|
||||||
printURLs(conf.GetURLs())
|
newconfig.ConfigOpt{Option: newconfig.OptionTypePrometheusPrefix, Value: *proxyPrometheusPrefix},
|
||||||
zap.S().Debugw("Configuration", "config", conf)
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeWriteBufferSize, Value: *proxyWriteBufferSize},
|
||||||
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeReadBufferSize, Value: *proxyReadBufferSize},
|
||||||
if conf.UseMiddleProxy() {
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeAntiReplayMaxSize, Value: *proxyAntiReplayMaxSize},
|
||||||
zap.S().Infow("Use middle proxy connection to Telegram")
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeAntiReplayEvictionTime, Value: *proxyAntiReplayEvictionTime},
|
||||||
if diff, err := ntp.Fetch(); err != nil {
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeSecret, Value: *proxySecret},
|
||||||
zap.S().Warnw("Could not fetch time data from NTP")
|
newconfig.ConfigOpt{Option: newconfig.OptionTypeAdtag, Value: *proxyAdtag},
|
||||||
} else {
|
)
|
||||||
if diff >= time.Second {
|
if err != nil {
|
||||||
usage(fmt.Sprintf("You choose to use middle proxy but your clock drift (%s) "+
|
newcli.Fatal(err.Error())
|
||||||
"is bigger than 1 second. Please, sync your time", diff))
|
|
||||||
}
|
|
||||||
go ntp.AutoUpdate()
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
zap.S().Infow("Use direct connection to Telegram")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := stats.Init(conf); err != nil {
|
if err := newcli.Proxy(); err != nil {
|
||||||
panic(err)
|
newcli.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
server, err := proxy.NewProxy(conf)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := server.Serve(); err != nil {
|
|
||||||
zap.S().Fatalw("Server stopped", "error", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,19 +203,3 @@ func setRLimit() (err error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func printURLs(data interface{}) {
|
|
||||||
encoder := json.NewEncoder(os.Stdout)
|
|
||||||
encoder.SetEscapeHTML(false)
|
|
||||||
encoder.SetIndent("", " ")
|
|
||||||
|
|
||||||
err := encoder.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage(msg string) {
|
|
||||||
io.WriteString(os.Stderr, msg+"\n") // nolint: errcheck, gosec
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package newantireplay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/allegro/bigcache"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cache *bigcache.BigCache
|
||||||
|
|
||||||
|
func Add(data []byte) {
|
||||||
|
cache.Set(string(data), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Has(data []byte) bool {
|
||||||
|
_, err := cache.Get(string(data))
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
c, err := bigcache.NewBigCache(bigcache.Config{
|
||||||
|
Shards: 1024,
|
||||||
|
LifeWindow: newconfig.C.AntiReplay.EvictionTime,
|
||||||
|
Hasher: hasher{},
|
||||||
|
HardMaxCacheSize: newconfig.C.AntiReplay.MaxSize,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cache = c
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package newantireplay
|
||||||
|
|
||||||
|
import "github.com/cespare/xxhash"
|
||||||
|
|
||||||
|
type hasher struct{}
|
||||||
|
|
||||||
|
func (h hasher) Sum64(value string) uint64 {
|
||||||
|
return xxhash.Sum64String(value)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package newcli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Generate(secretType string) {
|
||||||
|
data := make([]byte, newconfig.SimpleSecretLength)
|
||||||
|
if _, err := rand.Read(data); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
secret := hex.EncodeToString(data)
|
||||||
|
|
||||||
|
switch secretType {
|
||||||
|
case "simple":
|
||||||
|
PrintStdout(secret)
|
||||||
|
case "secured":
|
||||||
|
PrintStdout("dd" + secret)
|
||||||
|
default:
|
||||||
|
Fatal("Unknown secret type " + secret)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package newcli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newconfig"
|
||||||
|
"github.com/9seconds/mtg/newstats"
|
||||||
|
"github.com/9seconds/mtg/ntp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Proxy() error {
|
||||||
|
atom := zap.NewAtomicLevel()
|
||||||
|
switch {
|
||||||
|
case newconfig.C.Debug:
|
||||||
|
atom.SetLevel(zapcore.DebugLevel)
|
||||||
|
case newconfig.C.Verbose:
|
||||||
|
atom.SetLevel(zapcore.InfoLevel)
|
||||||
|
default:
|
||||||
|
atom.SetLevel(zapcore.ErrorLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
encoderCfg := zap.NewProductionEncoderConfig()
|
||||||
|
logger := zap.New(zapcore.NewCore(
|
||||||
|
zapcore.NewJSONEncoder(encoderCfg),
|
||||||
|
zapcore.Lock(os.Stderr),
|
||||||
|
atom,
|
||||||
|
))
|
||||||
|
zap.ReplaceGlobals(logger)
|
||||||
|
defer logger.Sync() // nolint: errcheck
|
||||||
|
|
||||||
|
if err := newconfig.InitPublicAddress(); err != nil {
|
||||||
|
Fatal(err.Error())
|
||||||
|
}
|
||||||
|
zap.S().Debugw("Configuration", "config", newconfig.C)
|
||||||
|
|
||||||
|
if len(newconfig.C.AdTag) > 0 {
|
||||||
|
zap.S().Infow("Use middle proxy connection to Telegram")
|
||||||
|
diff, err := ntp.Fetch()
|
||||||
|
if err != nil {
|
||||||
|
Fatal("Cannot fetch time data from NTP")
|
||||||
|
}
|
||||||
|
if diff > time.Second {
|
||||||
|
Fatal("Your local time is skewed and drift is bigger than a second. Please sync your time.")
|
||||||
|
}
|
||||||
|
go ntp.AutoUpdate()
|
||||||
|
} else {
|
||||||
|
zap.S().Infow("Use direct connection to Telegram")
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintJSONStdout(newconfig.GetURLs())
|
||||||
|
|
||||||
|
if err := newstats.Init(); err != nil {
|
||||||
|
Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package newcli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Fatal(args ...interface{}) {
|
||||||
|
PrintStderr(args...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintStderr(args ...interface{}) {
|
||||||
|
fmt.Fprintln(os.Stderr, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintStdout(args ...interface{}) {
|
||||||
|
fmt.Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintJSONStderr(data interface{}) {
|
||||||
|
printJSON(os.Stderr, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintJSONStdout(data interface{}) {
|
||||||
|
printJSON(os.Stdout, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printJSON(writer io.Writer, data interface{}) {
|
||||||
|
encoder := json.NewEncoder(writer)
|
||||||
|
encoder.SetEscapeHTML(false)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
|
||||||
|
if err := encoder.Encode(data); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,69 +1,68 @@
|
|||||||
package config2
|
package newconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
statsd "gopkg.in/alexcesaro/statsd.v2"
|
statsd "gopkg.in/alexcesaro/statsd.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SecretType byte
|
type SecretMode uint8
|
||||||
|
|
||||||
func (s SecretType) String() string {
|
func (s SecretMode) String() string {
|
||||||
switch s {
|
switch s {
|
||||||
case SecretTypeMain:
|
case SecretModeSimple:
|
||||||
return "main"
|
return "simple"
|
||||||
case SecretTypeSecured:
|
case SecretModeSecured:
|
||||||
return "secured"
|
return "secured"
|
||||||
default:
|
|
||||||
return "tls"
|
|
||||||
}
|
}
|
||||||
|
return "tls"
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SecretTypeMain = 1 << iota
|
SecretModeSimple SecretMode = iota
|
||||||
SecretTypeSecured
|
SecretModeSecured
|
||||||
SecretTypeTLS
|
SecretModeTLS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const SimpleSecretLength = 16
|
||||||
|
|
||||||
|
type OptionType uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FlagDebug = "debug"
|
OptionTypeDebug OptionType = iota
|
||||||
FlagVerbose = "verbose"
|
OptionTypeVerbose
|
||||||
|
|
||||||
FlagBindIP = "bind-ip"
|
OptionTypeBindIP
|
||||||
FlagBindPort = "bind-port"
|
OptionTypeBindPort
|
||||||
FlagPublicIPv4 = "public-ipv4"
|
OptionTypePublicIPv4
|
||||||
FlagPublicIPv4Port = "public-ipv4-port"
|
OptionTypePublicIPv4Port
|
||||||
FlagPublicIPv6 = "public-ipv6"
|
OptionTypePublicIPv6
|
||||||
FlagPublicIPv6Port = "public-ipv6-port"
|
OptionTypePublicIPv6Port
|
||||||
FlagStatsIP = "stats-ip"
|
OptionTypeStatsIP
|
||||||
FlagStatsPort = "stats-port"
|
OptionTypeStatsPort
|
||||||
|
|
||||||
FlagStatsdIP = "statsd-ip"
|
OptionTypeStatsdIP
|
||||||
FlagStatsdPort = "statsd-port"
|
OptionTypeStatsdPort
|
||||||
FlagStatsdNetwork = "statsd-network"
|
OptionTypeStatsdNetwork
|
||||||
FlagStatsdPrefix = "statsd-prefix"
|
OptionTypeStatsdPrefix
|
||||||
FlagStatsdTagsFormat = "statsd-tags-format"
|
OptionTypeStatsdTagsFormat
|
||||||
FlagStatsdTags = "statsd-tags"
|
OptionTypeStatsdTags
|
||||||
|
OptionTypePrometheusPrefix
|
||||||
|
|
||||||
FlagPrometheusPrefix = "prometheus-prefix"
|
OptionTypeWriteBufferSize
|
||||||
|
OptionTypeReadBufferSize
|
||||||
|
|
||||||
FlagWriteBufferSize = "write-buffer"
|
OptionTypeAntiReplayMaxSize
|
||||||
FlagReadBufferSize = "read-buffer"
|
OptionTypeAntiReplayEvictionTime
|
||||||
|
|
||||||
FlagSecureOnly = "secure-only"
|
OptionTypeSecret
|
||||||
|
OptionTypeAdtag
|
||||||
FlagAntiReplayMaxSize = "anti-replay-max-size"
|
|
||||||
FlagAntiReplayEvictionTime = "anti-replay-eviction-time"
|
|
||||||
|
|
||||||
FlagSecret = "secret"
|
|
||||||
FlagAdtag = "adtag"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BufferSize struct {
|
type BufferSize struct {
|
||||||
@@ -77,8 +76,7 @@ type AntiReplay struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
Prefix string `json:"prefix"`
|
Prefix string `json:"prefix"`
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatsdStats struct {
|
type StatsdStats struct {
|
||||||
@@ -132,8 +130,7 @@ type Config struct {
|
|||||||
|
|
||||||
Debug bool `json:"debug"`
|
Debug bool `json:"debug"`
|
||||||
Verbose bool `json:"verbose"`
|
Verbose bool `json:"verbose"`
|
||||||
SecureOnly bool `json:"secure_only"`
|
SecretMode SecretMode `json:"secret_mode"`
|
||||||
SecretType SecretType `json:"secret_type"`
|
|
||||||
Secret []byte `json:"secret"`
|
Secret []byte `json:"secret"`
|
||||||
AdTag []byte `json:"adtag"`
|
AdTag []byte `json:"adtag"`
|
||||||
}
|
}
|
||||||
@@ -144,44 +141,44 @@ func (c Config) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ConfigOpt struct {
|
type ConfigOpt struct {
|
||||||
Name string
|
Option OptionType
|
||||||
Value interface{}
|
Value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var C = Config{}
|
var C = Config{}
|
||||||
|
|
||||||
func Init(options ...ConfigOpt) error { // nolint: gocyclo
|
func Init(options ...ConfigOpt) error { // nolint: gocyclo
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
switch opt.Name {
|
switch opt.Option {
|
||||||
case FlagDebug:
|
case OptionTypeDebug:
|
||||||
C.Debug = opt.Value.(bool)
|
C.Debug = opt.Value.(bool)
|
||||||
case FlagVerbose:
|
case OptionTypeVerbose:
|
||||||
C.Verbose = opt.Value.(bool)
|
C.Verbose = opt.Value.(bool)
|
||||||
case FlagBindIP:
|
case OptionTypeBindIP:
|
||||||
C.ListenAddr.IP = opt.Value.(net.IP)
|
C.ListenAddr.IP = opt.Value.(net.IP)
|
||||||
case FlagBindPort:
|
case OptionTypeBindPort:
|
||||||
C.ListenAddr.Port = opt.Value.(int)
|
C.ListenAddr.Port = int(opt.Value.(uint16))
|
||||||
case FlagPublicIPv4:
|
case OptionTypePublicIPv4:
|
||||||
C.PublicIPv4Addr.IP = opt.Value.(net.IP)
|
C.PublicIPv4Addr.IP = opt.Value.(net.IP)
|
||||||
case FlagPublicIPv4Port:
|
case OptionTypePublicIPv4Port:
|
||||||
C.PublicIPv4Addr.Port = opt.Value.(int)
|
C.PublicIPv4Addr.Port = int(opt.Value.(uint16))
|
||||||
case FlagPublicIPv6:
|
case OptionTypePublicIPv6:
|
||||||
C.PublicIPv6Addr.IP = opt.Value.(net.IP)
|
C.PublicIPv6Addr.IP = opt.Value.(net.IP)
|
||||||
case FlagPublicIPv6Port:
|
case OptionTypePublicIPv6Port:
|
||||||
C.PublicIPv6Addr.Port = opt.Value.(int)
|
C.PublicIPv6Addr.Port = int(opt.Value.(uint16))
|
||||||
case FlagStatsIP:
|
case OptionTypeStatsIP:
|
||||||
C.StatsAddr.IP = opt.Value.(net.IP)
|
C.StatsAddr.IP = opt.Value.(net.IP)
|
||||||
case FlagStatsPort:
|
case OptionTypeStatsPort:
|
||||||
C.StatsAddr.Port = opt.Value.(int)
|
C.StatsAddr.Port = int(opt.Value.(uint16))
|
||||||
case FlagStatsdIP:
|
case OptionTypeStatsdIP:
|
||||||
C.StatsdStats.Addr.IP = opt.Value.(net.IP)
|
C.StatsdStats.Addr.IP = opt.Value.(net.IP)
|
||||||
case FlagStatsdPort:
|
case OptionTypeStatsdPort:
|
||||||
C.StatsdStats.Addr.Port = opt.Value.(int)
|
C.StatsdStats.Addr.Port = int(opt.Value.(uint16))
|
||||||
case FlagStatsdNetwork:
|
case OptionTypeStatsdNetwork:
|
||||||
C.StatsdStats.Addr.net = opt.Value.(string)
|
C.StatsdStats.Addr.net = opt.Value.(string)
|
||||||
case FlagStatsdPrefix:
|
case OptionTypeStatsdPrefix:
|
||||||
C.StatsdStats.Prefix = opt.Value.(string)
|
C.StatsdStats.Prefix = opt.Value.(string)
|
||||||
case FlagStatsdTagsFormat:
|
case OptionTypeStatsdTagsFormat:
|
||||||
value := opt.Value.(string)
|
value := opt.Value.(string)
|
||||||
switch value {
|
switch value {
|
||||||
case "datadog":
|
case "datadog":
|
||||||
@@ -191,41 +188,33 @@ func Init(options ...ConfigOpt) error { // nolint: gocyclo
|
|||||||
default:
|
default:
|
||||||
return errors.Errorf("Incorrect statsd tag %s", value)
|
return errors.Errorf("Incorrect statsd tag %s", value)
|
||||||
}
|
}
|
||||||
case FlagStatsdTags:
|
case OptionTypeStatsdTags:
|
||||||
C.StatsdStats.Tags = opt.Value.(map[string]string)
|
C.StatsdStats.Tags = opt.Value.(map[string]string)
|
||||||
case FlagPrometheusPrefix:
|
case OptionTypePrometheusPrefix:
|
||||||
C.PrometheusStats.Prefix = opt.Value.(string)
|
C.PrometheusStats.Prefix = opt.Value.(string)
|
||||||
case FlagWriteBufferSize:
|
case OptionTypeWriteBufferSize:
|
||||||
C.BufferSize.Write = opt.Value.(int)
|
C.BufferSize.Write = int(opt.Value.(uint32))
|
||||||
case FlagReadBufferSize:
|
case OptionTypeReadBufferSize:
|
||||||
C.BufferSize.Read = opt.Value.(int)
|
C.BufferSize.Read = int(opt.Value.(uint32))
|
||||||
case FlagAntiReplayMaxSize:
|
case OptionTypeAntiReplayMaxSize:
|
||||||
C.AntiReplay.MaxSize = opt.Value.(int)
|
C.AntiReplay.MaxSize = opt.Value.(int)
|
||||||
case FlagAntiReplayEvictionTime:
|
case OptionTypeAntiReplayEvictionTime:
|
||||||
C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
|
C.AntiReplay.EvictionTime = opt.Value.(time.Duration)
|
||||||
case FlagSecureOnly:
|
case OptionTypeSecret:
|
||||||
C.SecureOnly = opt.Value.(bool)
|
|
||||||
case FlagSecret:
|
|
||||||
C.Secret = opt.Value.([]byte)
|
C.Secret = opt.Value.([]byte)
|
||||||
case FlagAdtag:
|
case OptionTypeAdtag:
|
||||||
C.AdTag = opt.Value.([]byte)
|
C.AdTag = opt.Value.([]byte)
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Unknown tag %v", opt.Option)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
switch {
|
||||||
case len(C.Secret) == 17 && bytes.HasPrefix(C.Secret, []byte{0xdd}):
|
case len(C.Secret) == 1+SimpleSecretLength && bytes.HasPrefix(C.Secret, []byte{0xdd}):
|
||||||
C.SecretType = SecretTypeSecured
|
C.SecretMode = SecretModeSecured
|
||||||
C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
|
C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
|
||||||
case len(C.Secret) == 16:
|
case len(C.Secret) == SimpleSecretLength:
|
||||||
C.SecretType = SecretTypeMain
|
C.SecretMode = SecretModeSimple
|
||||||
default:
|
default:
|
||||||
return errors.New("Incorrect secret")
|
return errors.New("Incorrect secret")
|
||||||
}
|
}
|
||||||
@@ -241,35 +230,29 @@ func InitPublicAddress() error {
|
|||||||
C.PublicIPv6Addr.Port = C.ListenAddr.Port
|
C.PublicIPv6Addr.Port = C.ListenAddr.Port
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
foundAddress := C.PublicIPv4Addr.IP != nil || C.PublicIPv6Addr.IP != nil
|
||||||
defer cancel()
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
done := make(chan struct{})
|
|
||||||
|
|
||||||
if C.PublicIPv4Addr.IP == nil {
|
if C.PublicIPv4Addr.IP == nil {
|
||||||
wg.Add(1)
|
ip, err := getGlobalIPv4()
|
||||||
go func() {
|
if err != nil {
|
||||||
getGlobalIPv4(ctx, cancel)
|
zap.S().Warnw("Cannot resolve public address", "error", err)
|
||||||
wg.Done()
|
} else {
|
||||||
}()
|
C.PublicIPv4Addr.IP = ip
|
||||||
|
foundAddress = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if C.PublicIPv6Addr.IP == nil {
|
if C.PublicIPv6Addr.IP == nil {
|
||||||
wg.Add(1)
|
ip, err := getGlobalIPv6()
|
||||||
go func() {
|
if err != nil {
|
||||||
getGlobalIPv6(ctx, cancel)
|
zap.S().Warnw("Cannot resolve public address", "error", err)
|
||||||
wg.Done()
|
} else {
|
||||||
|
C.PublicIPv6Addr.IP = ip
|
||||||
}()
|
foundAddress = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
if !foundAddress {
|
||||||
case <-done:
|
return errors.New("Cannot resolve any public address")
|
||||||
return nil
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package config2
|
package newconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -18,27 +17,23 @@ const (
|
|||||||
ifconfigTimeout = 10 * time.Second
|
ifconfigTimeout = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func getGlobalIPv4(ctx context.Context, cancel context.CancelFunc) {
|
func getGlobalIPv4() (net.IP, error) {
|
||||||
ip, err := fetchIP(ctx, "tcp4")
|
ip, err := fetchIP("tcp4")
|
||||||
if err != nil || ip.To4() == nil {
|
if err != nil || ip.To4() == nil {
|
||||||
cancel()
|
return nil, errors.Annotate(err, "Cannot find public ipv4 address")
|
||||||
zap.S().Errorw("Cannot find public ipv4 address", "error", err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
C.PublicIPv4Addr.IP = ip
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGlobalIPv6(ctx context.Context, cancel context.CancelFunc) {
|
func getGlobalIPv6() (net.IP, error) {
|
||||||
ip, err := fetchIP(ctx, "tcp6")
|
ip, err := fetchIP("tcp6")
|
||||||
if err != nil || ip.To4() != nil {
|
if err != nil || ip.To4() != nil {
|
||||||
cancel()
|
return nil, errors.Annotate(err, "Cannot find public ipv6 address")
|
||||||
zap.S().Errorw("Cannot find public ipv6 address", "error", err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
C.PublicIPv6Addr.IP = ip
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchIP(ctx context.Context, network string) (net.IP, error) {
|
func fetchIP(network string) (net.IP, error) {
|
||||||
dialer := &net.Dialer{FallbackDelay: -1}
|
dialer := &net.Dialer{FallbackDelay: -1}
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Jar: nil,
|
Jar: nil,
|
||||||
@@ -50,14 +45,9 @@ func fetchIP(ctx context.Context, network string) (net.IP, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", ifconfigAddress, nil)
|
resp, err := client.Get(ifconfigAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
if resp != nil {
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := client.Do(req.WithContext(ctx))
|
|
||||||
if err != nil {
|
|
||||||
if resp.Body != nil {
|
|
||||||
io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
|
io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
|
||||||
}
|
}
|
||||||
return nil, errors.Annotate(err, "Cannot perform a request")
|
return nil, errors.Annotate(err, "Cannot perform a request")
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package config2
|
package newconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
@@ -20,12 +20,11 @@ type IPURLs struct {
|
|||||||
|
|
||||||
func GetURLs() (urls IPURLs) {
|
func GetURLs() (urls IPURLs) {
|
||||||
secret := ""
|
secret := ""
|
||||||
switch C.SecretType {
|
switch C.SecretMode {
|
||||||
case SecretTypeMain, SecretTypeSecured:
|
case SecretModeSimple:
|
||||||
secret = hex.EncodeToString(C.Secret)
|
secret = hex.EncodeToString(C.Secret)
|
||||||
if C.SecureOnly {
|
case SecretModeSecured:
|
||||||
secret = "dd" + secret
|
secret = "dd" + hex.EncodeToString(C.Secret)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)
|
urls.IPv4 = makeURLs(&C.PublicIPv4Addr, secret)
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package newobfuscated2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newantireplay"
|
||||||
|
"github.com/9seconds/mtg/newconfig"
|
||||||
|
"github.com/9seconds/mtg/newprotocol"
|
||||||
|
"github.com/9seconds/mtg/newwrappers"
|
||||||
|
)
|
||||||
|
|
||||||
|
const clientProtocolHandshakeTimeout = 10 * time.Second
|
||||||
|
|
||||||
|
type ClientProtocol struct {
|
||||||
|
newprotocol.BaseProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientProtocol) Handshake(socket newwrappers.StreamReadWriteCloser) (newwrappers.StreamReadWriteCloser, error) {
|
||||||
|
fm, err := c.ReadFrame(socket)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotate(err, "Cannot make client handshake")
|
||||||
|
}
|
||||||
|
|
||||||
|
decHasher := sha256.New()
|
||||||
|
decHasher.Write(fm.key()) // nolint: errcheck
|
||||||
|
decHasher.Write(newconfig.C.Secret) // nolint: errcheck
|
||||||
|
decryptor := makeStreamCipher(decHasher.Sum(nil), fm.iv())
|
||||||
|
|
||||||
|
invertedFrame := fm.invert()
|
||||||
|
encHasher := sha256.New()
|
||||||
|
encHasher.Write(invertedFrame.key()) // nolint: errcheck
|
||||||
|
encHasher.Write(newconfig.C.Secret) // nolint: errcheck
|
||||||
|
encryptor := makeStreamCipher(encHasher.Sum(nil), invertedFrame.iv())
|
||||||
|
|
||||||
|
decryptedFrame := frame{}
|
||||||
|
decryptor.XORKeyStream(decryptedFrame.bytes(), fm.bytes())
|
||||||
|
|
||||||
|
magic := decryptedFrame.magic()
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(magic, newprotocol.ConnectionTagAbridged):
|
||||||
|
c.ConnectionType = newprotocol.ConnectionTypeAbridged
|
||||||
|
case bytes.Equal(magic, newprotocol.ConnectionTagIntermediate):
|
||||||
|
c.ConnectionType = newprotocol.ConnectionTypeIntermediate
|
||||||
|
case bytes.Equal(magic, newprotocol.ConnectionTagSecure):
|
||||||
|
c.ConnectionType = newprotocol.ConnectionTypeSecure
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Unknown connection type")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ConnectionProtocol = newprotocol.ConnectionProtocolIPv4
|
||||||
|
if socket.LocalAddr().IP.To4() == nil {
|
||||||
|
c.ConnectionProtocol = newprotocol.ConnectionProtocolIPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewReader(decryptedFrame.dc())
|
||||||
|
if err := binary.Read(buf, binary.LittleEndian, &c.DC); err != nil {
|
||||||
|
c.DC = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
antiReplayKey := decryptedFrame.unique()
|
||||||
|
if newantireplay.Has(antiReplayKey) {
|
||||||
|
return nil, errors.New("Replay attack is detected")
|
||||||
|
}
|
||||||
|
newantireplay.Add(antiReplayKey)
|
||||||
|
|
||||||
|
return newwrappers.NewObfuscated2(socket, encryptor, decryptor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientProtocol) ReadFrame(socket newwrappers.StreamReader) (fm frame, err error) {
|
||||||
|
if _, err := io.ReadFull(handshakeReader{socket}, fm.bytes()); err != nil {
|
||||||
|
err = errors.Annotate(err, "Cannot extract obfuscated2 frame")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type handshakeReader struct {
|
||||||
|
parent newwrappers.StreamReader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h handshakeReader) Read(p []byte) (int, error) {
|
||||||
|
return h.parent.ReadTimeout(p, clientProtocolHandshakeTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeStreamCipher(key, iv []byte) cipher.Stream {
|
||||||
|
block, _ := aes.NewCipher(key) // nolint: gosec
|
||||||
|
return cipher.NewCTR(block, iv)
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package newobfuscated2
|
||||||
|
|
||||||
|
const (
|
||||||
|
frameLenKey = 32
|
||||||
|
frameLenIV = 16
|
||||||
|
frameLenMagic = 4
|
||||||
|
frameLenDC = 2
|
||||||
|
|
||||||
|
frameOffsetFirst = 8
|
||||||
|
frameOffsetKey = frameOffsetFirst + frameLenKey
|
||||||
|
frameOffsetIV = frameOffsetKey + frameLenIV
|
||||||
|
frameOffsetMagic = frameOffsetIV + frameLenMagic
|
||||||
|
frameOffsetDC = frameOffsetMagic + frameLenDC
|
||||||
|
|
||||||
|
frameLen = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
|
||||||
|
type frame struct {
|
||||||
|
data [frameLen]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *frame) bytes() []byte {
|
||||||
|
return f.data[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *frame) key() []byte {
|
||||||
|
return f.data[frameOffsetFirst:frameOffsetKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *frame) iv() []byte {
|
||||||
|
return f.data[frameOffsetKey:frameOffsetIV]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *frame) magic() []byte {
|
||||||
|
return f.data[frameOffsetIV:frameOffsetMagic]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *frame) dc() []byte {
|
||||||
|
return f.data[frameOffsetMagic:frameOffsetDC]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *frame) unique() []byte {
|
||||||
|
return f.data[frameOffsetFirst:frameOffsetDC]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *frame) invert() (nf frame) {
|
||||||
|
nf = *f
|
||||||
|
for i := 0; i < frameLenKey+frameLenIV; i++ {
|
||||||
|
nf.data[frameOffsetFirst+i] = nf.data[frameOffsetIV-1-i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package newobfuscated2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newprotocol"
|
||||||
|
"github.com/9seconds/mtg/newwrappers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TelegramProtocol struct {
|
||||||
|
newprotocol.BaseProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TelegramProtocol) Handshake(socketRaw newwrappers.Wrap, client *ClientProtocol) (newwrappers.StreamReadWriteCloser, error) {
|
||||||
|
socket := socketRaw.(newwrappers.StreamReadWriteCloser)
|
||||||
|
fm := generateFrame(client)
|
||||||
|
data := fm.bytes()
|
||||||
|
|
||||||
|
encryptor := makeStreamCipher(fm.key(), fm.iv())
|
||||||
|
decryptedFrame := fm.invert()
|
||||||
|
decryptor := makeStreamCipher(decryptedFrame.key(), decryptedFrame.iv())
|
||||||
|
|
||||||
|
copyFrame := make([]byte, frameLen)
|
||||||
|
copy(copyFrame[:frameOffsetIV], data[:frameOffsetIV])
|
||||||
|
encryptor.XORKeyStream(data, data)
|
||||||
|
copy(data[:frameOffsetIV], copyFrame[:frameOffsetIV])
|
||||||
|
|
||||||
|
if _, err := socket.Write(data); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "Cannot write handshate frame to Telegram")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newwrappers.NewObfuscated2(socket, encryptor, decryptor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateFrame(client *ClientProtocol) (fm frame) {
|
||||||
|
for {
|
||||||
|
data := fm.bytes()
|
||||||
|
if _, err := rand.Read(data); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if data[0] == 0xef {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val := (uint32(data[3]) << 24) | (uint32(data[2]) << 16) | (uint32(data[1]) << 8) | uint32(data[0])
|
||||||
|
if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (uint32(data[7]) << 24) | (uint32(data[6]) << 16) | (uint32(data[5]) << 8) | uint32(data[4])
|
||||||
|
if val == 0x00000000 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(fm.magic(), client.ConnectionType.Tag())
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package newprotocol
|
||||||
|
|
||||||
|
type BaseProtocol struct {
|
||||||
|
ConnectionType ConnectionType
|
||||||
|
ConnectionProtocol ConnectionProtocol
|
||||||
|
DC int16
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package newprotocol
|
||||||
|
|
||||||
|
type ConnectionProtocol uint8
|
||||||
|
|
||||||
|
func (c ConnectionProtocol) String() string {
|
||||||
|
switch c {
|
||||||
|
case ConnectionProtocolAny:
|
||||||
|
return "any"
|
||||||
|
case ConnectionProtocolIPv4:
|
||||||
|
return "ipv4"
|
||||||
|
}
|
||||||
|
return "ipv6"
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnectionProtocolIPv4 ConnectionProtocol = 1
|
||||||
|
ConnectionProtocolIPv6 = ConnectionProtocolIPv4 << 1
|
||||||
|
ConnectionProtocolAny = ConnectionProtocolIPv4 | ConnectionProtocolIPv6
|
||||||
|
)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package newprotocol
|
||||||
|
|
||||||
|
type ConnectionType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnectionTypeUnknown ConnectionType = iota
|
||||||
|
ConnectionTypeAbridged
|
||||||
|
ConnectionTypeIntermediate
|
||||||
|
ConnectionTypeSecure
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ConnectionTagAbridged = []byte{0xef, 0xef, 0xef, 0xef}
|
||||||
|
ConnectionTagIntermediate = []byte{0xee, 0xee, 0xee, 0xee}
|
||||||
|
ConnectionTagSecure = []byte{0xdd, 0xdd, 0xdd, 0xdd}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t ConnectionType) Tag() []byte {
|
||||||
|
switch t {
|
||||||
|
case ConnectionTypeAbridged:
|
||||||
|
return ConnectionTagAbridged
|
||||||
|
case ConnectionTypeIntermediate:
|
||||||
|
return ConnectionTagIntermediate
|
||||||
|
default:
|
||||||
|
return ConnectionTagSecure
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package newproxy
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package newstats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newconfig"
|
||||||
|
"github.com/9seconds/mtg/newprotocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Stats interface {
|
||||||
|
IngressTraffic(int)
|
||||||
|
EgressTraffic(int)
|
||||||
|
ClientConnected(newprotocol.ConnectionType, *net.TCPAddr)
|
||||||
|
ClientDisconnected(newprotocol.ConnectionType, *net.TCPAddr)
|
||||||
|
Crash()
|
||||||
|
AntiReplayDetected()
|
||||||
|
}
|
||||||
|
|
||||||
|
type multiStats []Stats
|
||||||
|
|
||||||
|
func (m multiStats) IngressTraffic(traffic int) {
|
||||||
|
for i := range m {
|
||||||
|
go m[i].IngressTraffic(traffic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m multiStats) EgressTraffic(traffic int) {
|
||||||
|
for i := range m {
|
||||||
|
go m[i].EgressTraffic(traffic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m multiStats) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
|
||||||
|
for i := range m {
|
||||||
|
go m[i].ClientConnected(connectionType, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m multiStats) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
|
||||||
|
for i := range m {
|
||||||
|
go m[i].ClientDisconnected(connectionType, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m multiStats) Crash() {
|
||||||
|
for i := range m {
|
||||||
|
go m[i].Crash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m multiStats) AntiReplayDetected() {
|
||||||
|
for i := range m {
|
||||||
|
go m[i].AntiReplayDetected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var S Stats
|
||||||
|
|
||||||
|
func Init() error {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
instanceJSON := newStatsJSON(mux)
|
||||||
|
instancePrometheus, err := newStatsPrometheus(mux)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotate(err, "Cannot initialize Prometheus")
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := []Stats{instanceJSON, instancePrometheus}
|
||||||
|
if newconfig.C.StatsdStats.Addr.IP != nil {
|
||||||
|
instanceStatsd, err := newStatsStatsd()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotate(err, "Cannot initialize StatsD")
|
||||||
|
}
|
||||||
|
stats = append(stats, instanceStatsd)
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", newconfig.C.StatsAddr.String())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotate(err, "Cannot initialize stats server")
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := http.Server{
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
go srv.Serve(listener) // nolint: errcheck
|
||||||
|
|
||||||
|
S = multiStats(stats)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package newstats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newprotocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type statsJSON struct {
|
||||||
|
Connections statsJSONConnections `json:"connections"`
|
||||||
|
Traffic statsJSONTraffic `json:"traffic"`
|
||||||
|
Uptime statsJSONUptime `json:"uptime"`
|
||||||
|
Crashes uint32 `json:"crashes"`
|
||||||
|
AntiReplays uint32 `json:"anti_replay_detected"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type statsBaseJSONConnections struct {
|
||||||
|
All statsJSONConnectionType `json:"all"`
|
||||||
|
Abridged statsJSONConnectionType `json:"abridged"`
|
||||||
|
Intermediate statsJSONConnectionType `json:"intermediate"`
|
||||||
|
Secured statsJSONConnectionType `json:"secured"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type statsJSONConnections struct {
|
||||||
|
statsBaseJSONConnections
|
||||||
|
}
|
||||||
|
|
||||||
|
type statsJSONConnectionType struct {
|
||||||
|
IPv4 uint32 `json:"ipv4"`
|
||||||
|
IPv6 uint32 `json:"ipv6"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c statsJSONConnections) MarshalJSON() ([]byte, error) {
|
||||||
|
c.All.IPv4 = c.Abridged.IPv4 + c.Intermediate.IPv4 + c.Secured.IPv4
|
||||||
|
c.All.IPv6 = c.Abridged.IPv6 + c.Intermediate.IPv6 + c.Secured.IPv6
|
||||||
|
|
||||||
|
return json.Marshal(c.statsBaseJSONConnections)
|
||||||
|
}
|
||||||
|
|
||||||
|
type statsJSONTraffic struct {
|
||||||
|
Ingress uint64 `json:"ingress"`
|
||||||
|
Egress uint64 `json:"egress"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type statsJSONUptime time.Time
|
||||||
|
|
||||||
|
func (s statsJSONUptime) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(time.Since(time.Time(s)).Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsJSON) IngressTraffic(traffic int) {
|
||||||
|
atomic.AddUint64(&s.Traffic.Ingress, uint64(traffic))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsJSON) EgressTraffic(traffic int) {
|
||||||
|
atomic.AddUint64(&s.Traffic.Egress, uint64(traffic))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsJSON) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
|
||||||
|
s.changeConnections(connectionType, addr, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsJSON) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
|
||||||
|
s.changeConnections(connectionType, addr, ^uint32(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsJSON) changeConnections(connectionType newprotocol.ConnectionType, addr *net.TCPAddr, value uint32) {
|
||||||
|
var connections *statsJSONConnectionType
|
||||||
|
|
||||||
|
switch connectionType {
|
||||||
|
case newprotocol.ConnectionTypeAbridged:
|
||||||
|
connections = &s.Connections.Abridged
|
||||||
|
case newprotocol.ConnectionTypeSecure:
|
||||||
|
connections = &s.Connections.Secured
|
||||||
|
default:
|
||||||
|
connections = &s.Connections.Intermediate
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.IP.To4() == nil {
|
||||||
|
atomic.AddUint32(&connections.IPv4, value)
|
||||||
|
} else {
|
||||||
|
atomic.AddUint32(&connections.IPv6, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsJSON) Crash() {
|
||||||
|
atomic.AddUint32(&s.Crashes, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsJSON) AntiReplayDetected() {
|
||||||
|
atomic.AddUint32(&s.AntiReplays, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStatsJSON(mux *http.ServeMux) Stats {
|
||||||
|
instance := &statsJSON{}
|
||||||
|
logger := zap.S().Named("stats")
|
||||||
|
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
first, err := json.Marshal(instance)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorw("Cannot encode json", "error", err)
|
||||||
|
http.Error(w, "Internal server error", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
interim := map[string]interface{}{}
|
||||||
|
if err := json.Unmarshal(first, &interim); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
encoder.SetEscapeHTML(false)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
if err := encoder.Encode(interim); err != nil {
|
||||||
|
logger.Errorw("Cannot encode json", "error", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return instance
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package newstats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newconfig"
|
||||||
|
"github.com/9seconds/mtg/newprotocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type statsPrometheus struct {
|
||||||
|
connections *prometheus.GaugeVec
|
||||||
|
traffic *prometheus.GaugeVec
|
||||||
|
crashes prometheus.Gauge
|
||||||
|
antiReplays prometheus.Gauge
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsPrometheus) IngressTraffic(traffic int) {
|
||||||
|
s.traffic.WithLabelValues("ingress").Add(float64(traffic))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsPrometheus) EgressTraffic(traffic int) {
|
||||||
|
s.traffic.WithLabelValues("egress").Add(float64(traffic))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsPrometheus) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
|
||||||
|
s.changeConnections(connectionType, addr, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsPrometheus) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
|
||||||
|
s.changeConnections(connectionType, addr, -1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsPrometheus) changeConnections(connectionType newprotocol.ConnectionType,
|
||||||
|
addr *net.TCPAddr,
|
||||||
|
increment float64) {
|
||||||
|
var labels [2]string
|
||||||
|
|
||||||
|
switch connectionType {
|
||||||
|
case newprotocol.ConnectionTypeAbridged:
|
||||||
|
labels[0] = "abridged"
|
||||||
|
case newprotocol.ConnectionTypeSecure:
|
||||||
|
labels[0] = "secured"
|
||||||
|
default:
|
||||||
|
labels[0] = "intermediate"
|
||||||
|
}
|
||||||
|
|
||||||
|
labels[1] = "ipv4"
|
||||||
|
if addr.IP.To4() == nil {
|
||||||
|
labels[1] = "ipv6"
|
||||||
|
}
|
||||||
|
|
||||||
|
s.connections.WithLabelValues(labels[:]...).Add(increment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsPrometheus) Crash() {
|
||||||
|
s.crashes.Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsPrometheus) AntiReplayDetected() {
|
||||||
|
s.antiReplays.Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStatsPrometheus(mux *http.ServeMux) (Stats, error) {
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
instance := &statsPrometheus{
|
||||||
|
connections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
|
Namespace: newconfig.C.PrometheusStats.Prefix,
|
||||||
|
Name: "connections",
|
||||||
|
Help: "Current number of connections to the proxy.",
|
||||||
|
}, []string{"type", "protocol"}),
|
||||||
|
traffic: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
|
Namespace: newconfig.C.PrometheusStats.Prefix,
|
||||||
|
Name: "traffic",
|
||||||
|
Help: "Traffic passed through the proxy in bytes.",
|
||||||
|
}, []string{"direction"}),
|
||||||
|
crashes: prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Namespace: newconfig.C.PrometheusStats.Prefix,
|
||||||
|
Name: "crashes",
|
||||||
|
Help: "How many crashes happened.",
|
||||||
|
}),
|
||||||
|
antiReplays: prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Namespace: newconfig.C.PrometheusStats.Prefix,
|
||||||
|
Name: "anti_replays",
|
||||||
|
Help: "How many anti replay attacks were prevented.",
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := registry.Register(instance.connections); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "Cannot register metrics for connections")
|
||||||
|
}
|
||||||
|
if err := registry.Register(instance.traffic); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "Cannot register metrics for traffic")
|
||||||
|
}
|
||||||
|
if err := registry.Register(instance.crashes); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "Cannot register metrics for crashes")
|
||||||
|
}
|
||||||
|
if err := registry.Register(instance.antiReplays); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "Cannot register metrics for anti replays")
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
|
||||||
|
mux.Handle("/prometheus", handler)
|
||||||
|
|
||||||
|
return instance, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package newstats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/alexcesaro/statsd.v2"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newconfig"
|
||||||
|
"github.com/9seconds/mtg/newprotocol"
|
||||||
|
"github.com/juju/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type statsStatsd struct {
|
||||||
|
client *statsd.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsStatsd) IngressTraffic(traffic int) {
|
||||||
|
s.client.Count("traffic.ingress", traffic)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsStatsd) EgressTraffic(traffic int) {
|
||||||
|
s.client.Count("traffic.egress", traffic)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsStatsd) ClientConnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
|
||||||
|
s.changeConnections(connectionType, addr, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsStatsd) ClientDisconnected(connectionType newprotocol.ConnectionType, addr *net.TCPAddr) {
|
||||||
|
s.changeConnections(connectionType, addr, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsStatsd) changeConnections(connectionType newprotocol.ConnectionType, addr *net.TCPAddr, value int) {
|
||||||
|
var labels [3]string
|
||||||
|
|
||||||
|
labels[0] = "connections"
|
||||||
|
switch connectionType {
|
||||||
|
case newprotocol.ConnectionTypeAbridged:
|
||||||
|
labels[1] = "abridged"
|
||||||
|
case newprotocol.ConnectionTypeSecure:
|
||||||
|
labels[1] = "secured"
|
||||||
|
default:
|
||||||
|
labels[1] = "intermediate"
|
||||||
|
}
|
||||||
|
|
||||||
|
labels[2] = "ipv4"
|
||||||
|
if addr.IP.To4() == nil {
|
||||||
|
labels[2] = "ipv6"
|
||||||
|
}
|
||||||
|
|
||||||
|
s.client.Count(strings.Join(labels[:], "."), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsStatsd) Crash() {
|
||||||
|
s.client.Increment("crashes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsStatsd) AntiReplayDetected() {
|
||||||
|
s.client.Increment("anti_replays")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStatsStatsd() (Stats, error) {
|
||||||
|
options := []statsd.Option{
|
||||||
|
statsd.Prefix(newconfig.C.StatsdStats.Prefix),
|
||||||
|
statsd.Network(newconfig.C.StatsdStats.Addr.Network()),
|
||||||
|
statsd.Address(newconfig.C.StatsdStats.Addr.String()),
|
||||||
|
statsd.TagsFormat(newconfig.C.StatsdStats.TagsFormat),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(newconfig.C.StatsdStats.Tags) > 0 {
|
||||||
|
tags := make([]string, len(newconfig.C.StatsdStats.Tags)*2)
|
||||||
|
for k, v := range newconfig.C.StatsdStats.Tags {
|
||||||
|
tags = append(tags, k, v)
|
||||||
|
}
|
||||||
|
options = append(options, statsd.Tags(tags...))
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := statsd.New(options...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotate(err, "Cannot initialize a client")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &statsStatsd{
|
||||||
|
client: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package newwrappers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Packet []byte
|
||||||
|
|
||||||
|
// Wrap is a base interface for all wrappers in this package.
|
||||||
|
type Wrap interface {
|
||||||
|
Conn() net.Conn
|
||||||
|
Logger() *zap.SugaredLogger
|
||||||
|
LocalAddr() *net.TCPAddr
|
||||||
|
RemoteAddr() *net.TCPAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseReaderWithTimeout interface {
|
||||||
|
ReadTimeout([]byte, time.Duration) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseWriterWithTimeout interface {
|
||||||
|
WriteTimeout([]byte, time.Duration) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasePacketReader interface {
|
||||||
|
Read() (Packet, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasePacketWriter interface {
|
||||||
|
Write(Packet) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamReader interface {
|
||||||
|
Wrap
|
||||||
|
io.Reader
|
||||||
|
BaseReaderWithTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamWriter interface {
|
||||||
|
Wrap
|
||||||
|
io.Writer
|
||||||
|
BaseWriterWithTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamCloser interface {
|
||||||
|
Wrap
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamReadCloser interface {
|
||||||
|
Wrap
|
||||||
|
io.ReadCloser
|
||||||
|
BaseReaderWithTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamWriteCloser interface {
|
||||||
|
Wrap
|
||||||
|
io.WriteCloser
|
||||||
|
BaseWriterWithTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamReadWriter interface {
|
||||||
|
Wrap
|
||||||
|
io.ReadWriter
|
||||||
|
BaseReaderWithTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamReadWriteCloser interface {
|
||||||
|
Wrap
|
||||||
|
io.ReadWriteCloser
|
||||||
|
BaseReaderWithTimeout
|
||||||
|
BaseWriterWithTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketReader interface {
|
||||||
|
Wrap
|
||||||
|
BasePacketReader
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketWriter interface {
|
||||||
|
Wrap
|
||||||
|
BasePacketWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketCloser interface {
|
||||||
|
Wrap
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketReadCloser interface {
|
||||||
|
Wrap
|
||||||
|
BasePacketReader
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketWriteCloser interface {
|
||||||
|
Wrap
|
||||||
|
BasePacketWriter
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketReadWriter interface {
|
||||||
|
Wrap
|
||||||
|
BasePacketWriter
|
||||||
|
BasePacketReader
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketReadWriteCloser interface {
|
||||||
|
Wrap
|
||||||
|
BasePacketWriter
|
||||||
|
BasePacketReader
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
package newwrappers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ConnIDLength = 8
|
||||||
|
|
||||||
|
type ConnID [ConnIDLength]byte
|
||||||
|
|
||||||
|
func (c ConnID) String() string {
|
||||||
|
return hex.EncodeToString(c[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
type connPurpose uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
connPurposeClient connPurpose = 1 << iota
|
||||||
|
connPurposeTelegram
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
connTimeoutRead = 2 * time.Minute
|
||||||
|
connTimeoutWrite = 2 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrapperConn struct {
|
||||||
|
parent net.Conn
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
connID ConnID
|
||||||
|
logger *zap.SugaredLogger
|
||||||
|
localAddr *net.TCPAddr
|
||||||
|
remoteAddr *net.TCPAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||||
|
select {
|
||||||
|
case <-w.ctx.Done():
|
||||||
|
w.Close()
|
||||||
|
return 0, errors.Annotate(w.ctx.Err(), "Cannot write because context was closed")
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := w.parent.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
|
||||||
|
w.Close() // nolint: gosec
|
||||||
|
return 0, errors.Annotate(err, "Cannot set write deadline to the socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := w.parent.Write(p)
|
||||||
|
w.logger.Debugw("Write to stream", "bytes", n, "error", err)
|
||||||
|
if err != nil {
|
||||||
|
w.Close() // nolint: gosec
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) Write(p []byte) (int, error) {
|
||||||
|
return w.WriteTimeout(p, connTimeoutWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||||
|
select {
|
||||||
|
case <-w.ctx.Done():
|
||||||
|
w.Close()
|
||||||
|
return 0, errors.Annotate(w.ctx.Err(), "Cannot read because context was closed")
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := w.parent.SetReadDeadline(time.Now().Add(timeout)); err != nil {
|
||||||
|
w.Close()
|
||||||
|
return 0, errors.Annotate(err, "Cannot set read deadline to the socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := w.parent.Read(p)
|
||||||
|
w.logger.Debugw("Read from stream", "bytes", n, "error", err)
|
||||||
|
if err != nil {
|
||||||
|
w.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) Read(p []byte) (int, error) {
|
||||||
|
return w.ReadTimeout(p, connTimeoutRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) Close() error {
|
||||||
|
w.logger.Debugw("Close connection")
|
||||||
|
w.cancel()
|
||||||
|
|
||||||
|
return w.parent.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) Conn() net.Conn {
|
||||||
|
return w.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) Logger() *zap.SugaredLogger {
|
||||||
|
return w.logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) LocalAddr() *net.TCPAddr {
|
||||||
|
return w.localAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperConn) RemoteAddr() *net.TCPAddr {
|
||||||
|
return w.remoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConn(ctx context.Context,
|
||||||
|
cancel context.CancelFunc,
|
||||||
|
parent net.Conn,
|
||||||
|
connID ConnID,
|
||||||
|
purpose connPurpose) StreamReadWriteCloser {
|
||||||
|
localAddr := *parent.LocalAddr().(*net.TCPAddr)
|
||||||
|
|
||||||
|
if parent.RemoteAddr().(*net.TCPAddr).IP.To4() != nil {
|
||||||
|
if newconfig.C.PublicIPv4Addr.IP != nil {
|
||||||
|
localAddr.IP = newconfig.C.PublicIPv4Addr.IP
|
||||||
|
}
|
||||||
|
} else if newconfig.C.PublicIPv6Addr.IP != nil {
|
||||||
|
localAddr.IP = newconfig.C.PublicIPv6Addr.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := zap.S().With(
|
||||||
|
"local_address", localAddr,
|
||||||
|
"remote_address", parent.RemoteAddr(),
|
||||||
|
).Named("conn")
|
||||||
|
|
||||||
|
if purpose == connPurposeClient {
|
||||||
|
logger = logger.With("connection_id", connID.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &wrapperConn{
|
||||||
|
parent: parent,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
connID: connID,
|
||||||
|
logger: logger,
|
||||||
|
remoteAddr: parent.RemoteAddr().(*net.TCPAddr),
|
||||||
|
localAddr: &localAddr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientConn(ctx context.Context,
|
||||||
|
cancel context.CancelFunc,
|
||||||
|
parent net.Conn,
|
||||||
|
connID ConnID) StreamReadWriteCloser {
|
||||||
|
return newConn(ctx, cancel, parent, connID, connPurposeClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTelegramConn(ctx context.Context,
|
||||||
|
cancel context.CancelFunc,
|
||||||
|
parent net.Conn,
|
||||||
|
connID ConnID) StreamReadWriteCloser {
|
||||||
|
return newConn(ctx, cancel, parent, connID, connPurposeTelegram)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnID() ConnID {
|
||||||
|
var id ConnID
|
||||||
|
|
||||||
|
if _, err := rand.Read(id[:]); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package newwrappers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrapperObfuscated2 struct {
|
||||||
|
encryptor cipher.Stream
|
||||||
|
decryptor cipher.Stream
|
||||||
|
parent StreamReadWriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||||
|
n, err := w.parent.ReadTimeout(p, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Annotate(err, "Cannot read stream ciphered data")
|
||||||
|
}
|
||||||
|
w.decryptor.XORKeyStream(p, p[:n])
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) Read(p []byte) (int, error) {
|
||||||
|
n, err := w.parent.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Annotate(err, "Cannot read stream ciphered data")
|
||||||
|
}
|
||||||
|
w.decryptor.XORKeyStream(p, p[:n])
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||||
|
buf := make([]byte, len(p))
|
||||||
|
copy(buf, p)
|
||||||
|
w.encryptor.XORKeyStream(buf, buf)
|
||||||
|
|
||||||
|
return w.parent.WriteTimeout(buf, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) Write(p []byte) (int, error) {
|
||||||
|
buf := make([]byte, len(p))
|
||||||
|
copy(buf, p)
|
||||||
|
w.encryptor.XORKeyStream(buf, buf)
|
||||||
|
|
||||||
|
return w.parent.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) Conn() net.Conn {
|
||||||
|
return w.parent.Conn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) Logger() *zap.SugaredLogger {
|
||||||
|
return w.parent.Logger().Named("obfuscated2")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) LocalAddr() *net.TCPAddr {
|
||||||
|
return w.parent.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) RemoteAddr() *net.TCPAddr {
|
||||||
|
return w.parent.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperObfuscated2) Close() error {
|
||||||
|
return w.parent.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewObfuscated2(socket StreamReadWriteCloser, encryptor, decryptor cipher.Stream) StreamReadWriteCloser {
|
||||||
|
return &wrapperObfuscated2{
|
||||||
|
parent: socket,
|
||||||
|
encryptor: encryptor,
|
||||||
|
decryptor: decryptor,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package newwrappers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/newstats"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrapperStats struct {
|
||||||
|
parent StreamReadWriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) Write(p []byte) (int, error) {
|
||||||
|
n, err := w.parent.Write(p)
|
||||||
|
newstats.S.EgressTraffic(n)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||||
|
n, err := w.parent.WriteTimeout(p, timeout)
|
||||||
|
newstats.S.EgressTraffic(n)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) Read(p []byte) (int, error) {
|
||||||
|
n, err := w.parent.Read(p)
|
||||||
|
newstats.S.IngressTraffic(n)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||||
|
n, err := w.parent.ReadTimeout(p, timeout)
|
||||||
|
newstats.S.IngressTraffic(n)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) Conn() net.Conn {
|
||||||
|
return w.parent.Conn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) Logger() *zap.SugaredLogger {
|
||||||
|
return w.parent.Logger().Named("traffic")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) LocalAddr() *net.TCPAddr {
|
||||||
|
return w.parent.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) RemoteAddr() *net.TCPAddr {
|
||||||
|
return w.parent.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapperStats) Close() error {
|
||||||
|
return w.parent.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTraffic(parent StreamReadWriteCloser) StreamReadWriteCloser {
|
||||||
|
return &wrapperStats{parent}
|
||||||
|
}
|
||||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
const autoUpdatePeriod = time.Minute
|
const autoUpdatePeriod = time.Minute
|
||||||
|
|
||||||
var ntpEndpoints = []string{
|
var ntpEndpoints = [...]string{
|
||||||
"0.pool.ntp.org",
|
"0.pool.ntp.org",
|
||||||
"1.pool.ntp.org",
|
"1.pool.ntp.org",
|
||||||
"2.pool.ntp.org",
|
"2.pool.ntp.org",
|
||||||
|
|||||||
Reference in New Issue
Block a user