mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 12:04:03 +03:00
Reset a project
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
package antireplay
|
||||
|
||||
import "github.com/VictoriaMetrics/fastcache"
|
||||
|
||||
var (
|
||||
prefixObfuscated2 = []byte{0x00}
|
||||
prefixTLS = []byte{0x01}
|
||||
)
|
||||
|
||||
type cache struct {
|
||||
data *fastcache.Cache
|
||||
}
|
||||
|
||||
func (c cache) AddObfuscated2(data []byte) {
|
||||
c.data.Set(keyObfuscated2(data), nil)
|
||||
}
|
||||
|
||||
func (c cache) AddTLS(data []byte) {
|
||||
c.data.Set(keyTLS(data), nil)
|
||||
}
|
||||
|
||||
func (c cache) HasObfuscated2(data []byte) bool {
|
||||
return c.data.Has(keyObfuscated2(data))
|
||||
}
|
||||
|
||||
func (c cache) HasTLS(data []byte) bool {
|
||||
return c.data.Has(keyTLS(data))
|
||||
}
|
||||
|
||||
func keyObfuscated2(data []byte) []byte {
|
||||
return append(prefixObfuscated2, data...)
|
||||
}
|
||||
|
||||
func keyTLS(data []byte) []byte {
|
||||
return append(prefixTLS, data...)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package antireplay
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/VictoriaMetrics/fastcache"
|
||||
)
|
||||
|
||||
type CacheInterface interface {
|
||||
AddObfuscated2([]byte)
|
||||
AddTLS([]byte)
|
||||
HasObfuscated2([]byte) bool
|
||||
HasTLS([]byte) bool
|
||||
}
|
||||
|
||||
var (
|
||||
Cache CacheInterface
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
func Init() {
|
||||
initOnce.Do(func() {
|
||||
if config.C.AntiReplayMaxSize == 0 {
|
||||
Cache = nilCache{}
|
||||
} else {
|
||||
Cache = cache{fastcache.New(config.C.AntiReplayMaxSize)}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package antireplay
|
||||
|
||||
type nilCache struct{}
|
||||
|
||||
func (n nilCache) AddObfuscated2(_ []byte) {}
|
||||
func (n nilCache) AddTLS(_ []byte) {}
|
||||
func (n nilCache) HasObfuscated2(_ []byte) bool { return false }
|
||||
func (n nilCache) HasTLS(_ []byte) bool { return false }
|
||||
@@ -1,26 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
)
|
||||
|
||||
func Generate(secretType, hostname string) {
|
||||
data := make([]byte, config.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:
|
||||
PrintStdout("ee" + secret + hex.EncodeToString([]byte(hostname)))
|
||||
}
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/antireplay"
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/faketls"
|
||||
"github.com/9seconds/mtg/hub"
|
||||
"github.com/9seconds/mtg/ntp"
|
||||
"github.com/9seconds/mtg/obfuscated2"
|
||||
"github.com/9seconds/mtg/proxy"
|
||||
"github.com/9seconds/mtg/stats"
|
||||
"github.com/9seconds/mtg/telegram"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func Proxy() error { // nolint: funlen,cyclop
|
||||
ctx := utils.GetSignalContext()
|
||||
|
||||
atom := zap.NewAtomicLevel()
|
||||
|
||||
switch {
|
||||
case config.C.Debug:
|
||||
atom.SetLevel(zapcore.DebugLevel)
|
||||
case config.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 := config.InitPublicAddress(ctx); err != nil {
|
||||
Fatal(err)
|
||||
}
|
||||
|
||||
zap.S().Debugw("Configuration", "config", config.Printable())
|
||||
|
||||
if config.C.MiddleProxyMode() {
|
||||
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(config.GetURLs())
|
||||
|
||||
if err := stats.Init(ctx); err != nil {
|
||||
Fatal(err)
|
||||
}
|
||||
|
||||
antireplay.Init()
|
||||
telegram.Init()
|
||||
hub.Init(ctx)
|
||||
|
||||
proxyListener, err := net.Listen("tcp", config.C.Bind.String())
|
||||
if err != nil {
|
||||
Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
proxyListener.Close()
|
||||
}()
|
||||
|
||||
app := &proxy.Proxy{
|
||||
Logger: zap.S().Named("proxy"),
|
||||
Context: ctx,
|
||||
ClientProtocolMaker: obfuscated2.MakeClientProtocol,
|
||||
}
|
||||
if config.C.SecretMode == config.SecretModeTLS {
|
||||
app.ClientProtocolMaker = faketls.MakeClientProtocol
|
||||
}
|
||||
|
||||
app.Serve(proxyListener)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Fatal(arg interface{}) {
|
||||
if value, ok := arg.(error); ok {
|
||||
arg = fmt.Errorf("fatal error: %+v", value) // nolint: errorlint
|
||||
}
|
||||
|
||||
PrintStderr(arg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func PrintStderr(args ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, args...)
|
||||
}
|
||||
|
||||
func PrintStdout(args ...interface{}) {
|
||||
fmt.Println(args...) // nolint: forbidigo
|
||||
}
|
||||
|
||||
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,312 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
|
||||
"github.com/alecthomas/units"
|
||||
statsd "github.com/smira/go-statsd"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SecretMode uint8
|
||||
|
||||
func (s SecretMode) String() string {
|
||||
switch s {
|
||||
case SecretModeSimple:
|
||||
return "simple"
|
||||
case SecretModeSecured:
|
||||
return "secured"
|
||||
case SecretModeTLS:
|
||||
return "tls"
|
||||
}
|
||||
|
||||
return "tls"
|
||||
}
|
||||
|
||||
const (
|
||||
SecretModeSimple SecretMode = iota
|
||||
SecretModeSecured
|
||||
SecretModeTLS
|
||||
)
|
||||
|
||||
type PreferIP uint8
|
||||
|
||||
const (
|
||||
PreferIPv4 PreferIP = iota
|
||||
PreferIPv6
|
||||
)
|
||||
|
||||
const SimpleSecretLength = 16
|
||||
|
||||
type OptionType uint8
|
||||
|
||||
const (
|
||||
OptionTypeDebug OptionType = iota
|
||||
OptionTypeVerbose
|
||||
|
||||
OptionTypePreferIP
|
||||
|
||||
OptionTypeBind
|
||||
OptionTypePublicIPv4
|
||||
OptionTypePublicIPv6
|
||||
|
||||
OptionTypeStatsBind
|
||||
OptionTypeStatsNamespace
|
||||
OptionTypeStatsdAddress
|
||||
OptionTypeStatsdTagsFormat
|
||||
OptionTypeStatsdTags
|
||||
|
||||
OptionTypeWriteBufferSize
|
||||
OptionTypeReadBufferSize
|
||||
|
||||
OptionTypeCloakPort
|
||||
|
||||
OptionTypeAntiReplayMaxSize
|
||||
|
||||
OptionTypeMultiplexPerConnection
|
||||
|
||||
OptionTypeNTPServers
|
||||
|
||||
OptionTypeSecret
|
||||
OptionTypeAdtag
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Bind *net.TCPAddr `json:"bind"`
|
||||
PublicIPv4 *net.TCPAddr `json:"public_ipv4"`
|
||||
PublicIPv6 *net.TCPAddr `json:"public_ipv6"`
|
||||
StatsBind *net.TCPAddr `json:"stats_bind"`
|
||||
StatsdAddr *net.TCPAddr `json:"stats_addr"`
|
||||
StatsdTagsFormat *statsd.TagFormat `json:"statsd_tags_format"`
|
||||
|
||||
StatsNamespace string `json:"stats_namespace"`
|
||||
CloakHost string `json:"cloak_host"`
|
||||
StatsdTags map[string]string `json:"statsd_tags"`
|
||||
|
||||
WriteBuffer int `json:"write_buffer"`
|
||||
ReadBuffer int `json:"read_buffer"`
|
||||
CloakPort int `json:"cloak_port"`
|
||||
|
||||
AntiReplayMaxSize int `json:"anti_replay_max_size"`
|
||||
|
||||
MultiplexPerConnection int `json:"multiplex_per_connection"`
|
||||
|
||||
Debug bool `json:"debug"`
|
||||
Verbose bool `json:"verbose"`
|
||||
SecretMode SecretMode `json:"secret_mode"`
|
||||
PreferIP PreferIP `json:"prefer_ip"`
|
||||
NTPServers []string `json:"ntp_servers"`
|
||||
|
||||
Secret []byte `json:"secret"`
|
||||
AdTag []byte `json:"adtag"`
|
||||
}
|
||||
|
||||
func (c *Config) ClientReadBuffer() int {
|
||||
return c.ReadBuffer
|
||||
}
|
||||
|
||||
func (c *Config) ClientWriteBuffer() int {
|
||||
return c.WriteBuffer
|
||||
}
|
||||
|
||||
func (c *Config) MiddleProxyMode() bool {
|
||||
return len(c.AdTag) > 0
|
||||
}
|
||||
|
||||
func (c *Config) ProxyReadBuffer() int {
|
||||
value := c.ReadBuffer
|
||||
|
||||
if c.MiddleProxyMode() {
|
||||
value = c.adjustProxyValue(value)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func (c *Config) ProxyWriteBuffer() int {
|
||||
value := c.WriteBuffer
|
||||
|
||||
if c.MiddleProxyMode() {
|
||||
value = c.adjustProxyValue(value)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func (c *Config) adjustProxyValue(value int) int {
|
||||
if c.MultiplexPerConnection == 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
fvalue := float64(value)
|
||||
|
||||
newValue := fvalue * 2 * math.Log(float64(c.MultiplexPerConnection))
|
||||
newValue = math.Ceil(newValue)
|
||||
newValue = math.Max(fvalue, newValue)
|
||||
|
||||
return int(newValue)
|
||||
}
|
||||
|
||||
type Opt struct {
|
||||
Option OptionType
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
var C = Config{}
|
||||
|
||||
func Init(options ...Opt) error { // nolint: gocyclo, funlen, cyclop
|
||||
for _, opt := range options {
|
||||
switch opt.Option {
|
||||
case OptionTypeDebug:
|
||||
C.Debug = opt.Value.(bool)
|
||||
case OptionTypeVerbose:
|
||||
C.Verbose = opt.Value.(bool)
|
||||
case OptionTypePreferIP:
|
||||
value := opt.Value.(string)
|
||||
switch value {
|
||||
case "ipv4":
|
||||
C.PreferIP = PreferIPv4
|
||||
case "ipv6":
|
||||
C.PreferIP = PreferIPv6
|
||||
default:
|
||||
return fmt.Errorf("incorrect direct IP mode %s", value)
|
||||
}
|
||||
case OptionTypeBind:
|
||||
C.Bind = opt.Value.(*net.TCPAddr)
|
||||
case OptionTypePublicIPv4:
|
||||
C.PublicIPv4 = opt.Value.(*net.TCPAddr)
|
||||
if C.PublicIPv4 == nil {
|
||||
C.PublicIPv4 = &net.TCPAddr{}
|
||||
}
|
||||
case OptionTypePublicIPv6:
|
||||
C.PublicIPv6 = opt.Value.(*net.TCPAddr)
|
||||
if C.PublicIPv6 == nil {
|
||||
C.PublicIPv6 = &net.TCPAddr{}
|
||||
}
|
||||
case OptionTypeStatsBind:
|
||||
C.StatsBind = opt.Value.(*net.TCPAddr)
|
||||
case OptionTypeStatsNamespace:
|
||||
C.StatsNamespace = opt.Value.(string)
|
||||
case OptionTypeStatsdAddress:
|
||||
C.StatsdAddr = opt.Value.(*net.TCPAddr)
|
||||
case OptionTypeStatsdTagsFormat:
|
||||
value := opt.Value.(string)
|
||||
switch value {
|
||||
case "datadog":
|
||||
C.StatsdTagsFormat = statsd.TagFormatDatadog
|
||||
case "influxdb":
|
||||
C.StatsdTagsFormat = statsd.TagFormatInfluxDB
|
||||
default:
|
||||
return fmt.Errorf("incorrect statsd tag %s", value)
|
||||
}
|
||||
case OptionTypeStatsdTags:
|
||||
C.StatsdTags = opt.Value.(map[string]string)
|
||||
case OptionTypeWriteBufferSize:
|
||||
C.WriteBuffer = int(opt.Value.(units.Base2Bytes))
|
||||
case OptionTypeReadBufferSize:
|
||||
C.ReadBuffer = int(opt.Value.(units.Base2Bytes))
|
||||
case OptionTypeCloakPort:
|
||||
C.CloakPort = int(opt.Value.(uint16))
|
||||
case OptionTypeAntiReplayMaxSize:
|
||||
C.AntiReplayMaxSize = int(opt.Value.(units.Base2Bytes))
|
||||
case OptionTypeMultiplexPerConnection:
|
||||
C.MultiplexPerConnection = int(opt.Value.(uint))
|
||||
case OptionTypeNTPServers:
|
||||
C.NTPServers = opt.Value.([]string)
|
||||
if len(C.NTPServers) == 0 {
|
||||
return errors.New("ntp server list is empty")
|
||||
}
|
||||
case OptionTypeSecret:
|
||||
C.Secret = opt.Value.([]byte)
|
||||
case OptionTypeAdtag:
|
||||
C.AdTag = opt.Value.([]byte)
|
||||
default:
|
||||
return fmt.Errorf("unknown tag %v", opt.Option)
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(C.Secret) == 1+SimpleSecretLength && bytes.HasPrefix(C.Secret, []byte{0xdd}):
|
||||
C.SecretMode = SecretModeSecured
|
||||
C.Secret = bytes.TrimPrefix(C.Secret, []byte{0xdd})
|
||||
case len(C.Secret) > SimpleSecretLength && bytes.HasPrefix(C.Secret, []byte{0xee}):
|
||||
C.SecretMode = SecretModeTLS
|
||||
secret := bytes.TrimPrefix(C.Secret, []byte{0xee})
|
||||
C.Secret = secret[:SimpleSecretLength]
|
||||
C.CloakHost = string(secret[SimpleSecretLength:])
|
||||
case len(C.Secret) == SimpleSecretLength:
|
||||
C.SecretMode = SecretModeSimple
|
||||
default:
|
||||
return errors.New("incorrect secret")
|
||||
}
|
||||
|
||||
if C.MultiplexPerConnection == 0 {
|
||||
return errors.New("cannot use 0 clients per connection for multiplexing")
|
||||
}
|
||||
|
||||
if C.CloakHost != "" {
|
||||
if _, err := net.LookupHost(C.CloakHost); err != nil {
|
||||
zap.S().Warnw("Cannot resolve address of host", "hostname", C.CloakHost, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitPublicAddress(ctx context.Context) error {
|
||||
if C.PublicIPv4.Port == 0 {
|
||||
C.PublicIPv4.Port = C.Bind.Port
|
||||
}
|
||||
|
||||
if C.PublicIPv6.Port == 0 {
|
||||
C.PublicIPv6.Port = C.Bind.Port
|
||||
}
|
||||
|
||||
foundAddress := C.PublicIPv4.IP != nil || C.PublicIPv6.IP != nil
|
||||
|
||||
if C.PublicIPv4.IP == nil {
|
||||
ip, err := getGlobalIPv4(ctx)
|
||||
if err != nil {
|
||||
zap.S().Warnw("Cannot resolve public address", "error", err)
|
||||
} else {
|
||||
C.PublicIPv4.IP = ip
|
||||
foundAddress = true
|
||||
}
|
||||
}
|
||||
|
||||
if C.PublicIPv6.IP == nil {
|
||||
ip, err := getGlobalIPv6(ctx)
|
||||
if err != nil {
|
||||
zap.S().Warnw("Cannot resolve public address", "error", err)
|
||||
} else {
|
||||
C.PublicIPv6.IP = ip
|
||||
foundAddress = true
|
||||
}
|
||||
}
|
||||
|
||||
if !foundAddress {
|
||||
return errors.New("cannot resolve any public address")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Printable() interface{} {
|
||||
data, err := json.Marshal(C)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rv := map[string]interface{}{}
|
||||
if err := json.Unmarshal(data, &rv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return rv
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ifconfigAddress = "https://ifconfig.co/ip"
|
||||
ifconfigTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
func getGlobalIPv4(ctx context.Context) (net.IP, error) {
|
||||
ip, err := fetchIP(ctx, "tcp4")
|
||||
if err != nil || ip.To4() == nil {
|
||||
return nil, fmt.Errorf("cannot find public ipv4 address: %w", err)
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func getGlobalIPv6(ctx context.Context) (net.IP, error) {
|
||||
ip, err := fetchIP(ctx, "tcp6")
|
||||
if err != nil || ip.To4() != nil {
|
||||
return nil, fmt.Errorf("cannot find public ipv6 address: %w", err)
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, fmt.Errorf("cannot create a request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
if resp != nil {
|
||||
io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("cannot perform a request: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
respDataBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read response body: %w", err)
|
||||
}
|
||||
|
||||
respData := strings.TrimSpace(string(respDataBytes))
|
||||
|
||||
ip := net.ParseIP(respData)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("ifconfig.co returns incorrect IP %s", respData)
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
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,omitempty"`
|
||||
IPv6 *URLs `json:"ipv6,omitempty"`
|
||||
BotSecret string `json:"secret_for_mtproxybot"`
|
||||
}
|
||||
|
||||
func GetURLs() (urls IPURLs) {
|
||||
secret := ""
|
||||
|
||||
switch C.SecretMode {
|
||||
case SecretModeSimple:
|
||||
secret = hex.EncodeToString(C.Secret)
|
||||
case SecretModeSecured:
|
||||
secret = "dd" + hex.EncodeToString(C.Secret)
|
||||
case SecretModeTLS:
|
||||
secret = "ee" + hex.EncodeToString(C.Secret) + hex.EncodeToString([]byte(C.CloakHost))
|
||||
}
|
||||
|
||||
if C.PublicIPv4.IP != nil {
|
||||
urls.IPv4 = makeURLs(C.PublicIPv4, secret)
|
||||
}
|
||||
|
||||
if C.PublicIPv6.IP != nil {
|
||||
urls.IPv6 = makeURLs(C.PublicIPv6, secret)
|
||||
}
|
||||
|
||||
urls.BotSecret = hex.EncodeToString(C.Secret)
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
func makeURLs(addr *net.TCPAddr, secret string) *URLs {
|
||||
urls := &URLs{}
|
||||
|
||||
values := url.Values{}
|
||||
values.Set("server", addr.IP.String())
|
||||
values.Set("port", strconv.Itoa(addr.Port))
|
||||
values.Set("secret", secret)
|
||||
|
||||
return &URLs{
|
||||
TG: makeTGURL(values),
|
||||
TMe: makeTMeURL(values),
|
||||
TGQRCode: makeQRCodeURL(urls.TG),
|
||||
TMeQRCode: makeQRCodeURL(urls.TMe),
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
type ConnectionAcks struct {
|
||||
Simple bool
|
||||
Quick bool
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
type DC int16
|
||||
|
||||
const DCDefaultIdx DC = 1
|
||||
@@ -1,24 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
const ConnIDLength = 8
|
||||
|
||||
type ConnID [ConnIDLength]byte
|
||||
|
||||
func (c ConnID) String() string {
|
||||
return hex.EncodeToString(c[:])
|
||||
}
|
||||
|
||||
func NewConnID() ConnID {
|
||||
var id ConnID
|
||||
|
||||
if _, err := rand.Read(id[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
type Packet []byte
|
||||
@@ -1,22 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
type ConnectionProtocol uint8
|
||||
|
||||
func (c ConnectionProtocol) String() string {
|
||||
switch c {
|
||||
case ConnectionProtocolAny:
|
||||
return "any"
|
||||
case ConnectionProtocolIPv4:
|
||||
return "ipv4"
|
||||
case ConnectionProtocolIPv6:
|
||||
return "ipv6"
|
||||
}
|
||||
|
||||
return "ipv6"
|
||||
}
|
||||
|
||||
const (
|
||||
ConnectionProtocolIPv4 ConnectionProtocol = 1
|
||||
ConnectionProtocolIPv6 = ConnectionProtocolIPv4 << 1
|
||||
ConnectionProtocolAny = ConnectionProtocolIPv4 | ConnectionProtocolIPv6
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
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
|
||||
case ConnectionTypeSecure, ConnectionTypeUnknown:
|
||||
return ConnectionTagSecure
|
||||
}
|
||||
|
||||
return ConnectionTagSecure
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Wrap interface {
|
||||
Conn() net.Conn
|
||||
Logger() *zap.SugaredLogger
|
||||
LocalAddr() *net.TCPAddr
|
||||
RemoteAddr() *net.TCPAddr
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
import "io"
|
||||
|
||||
type PacketAckReader interface {
|
||||
Read(*ConnectionAcks) (Packet, error)
|
||||
}
|
||||
|
||||
type PacketAckWriter interface {
|
||||
Write(Packet, *ConnectionAcks) error
|
||||
}
|
||||
|
||||
type PacketAckCloser interface {
|
||||
io.Closer
|
||||
}
|
||||
|
||||
type PacketAckReadCloser interface {
|
||||
PacketAckReader
|
||||
PacketAckCloser
|
||||
}
|
||||
|
||||
type PacketAckWriteCloser interface {
|
||||
PacketAckWriter
|
||||
PacketAckCloser
|
||||
}
|
||||
|
||||
type PacketAckReadWriter interface {
|
||||
PacketAckReader
|
||||
PacketAckWriter
|
||||
}
|
||||
|
||||
type PacketAckReadWriteCloser interface {
|
||||
PacketAckReader
|
||||
PacketAckWriter
|
||||
PacketAckCloser
|
||||
}
|
||||
|
||||
type PacketAckFullReadWriteCloser interface {
|
||||
Wrap
|
||||
PacketAckReadWriteCloser
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
import "io"
|
||||
|
||||
type BasePacketReader interface {
|
||||
Read() (Packet, error)
|
||||
}
|
||||
|
||||
type BasePacketWriter interface {
|
||||
Write(Packet) error
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package conntypes
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BaseStreamReaderWithTimeout interface {
|
||||
ReadTimeout([]byte, time.Duration) (int, error)
|
||||
}
|
||||
|
||||
type BaseStreamWriterWithTimeout interface {
|
||||
WriteTimeout([]byte, time.Duration) (int, error)
|
||||
}
|
||||
|
||||
type StreamReader interface {
|
||||
Wrap
|
||||
io.Reader
|
||||
BaseStreamReaderWithTimeout
|
||||
}
|
||||
|
||||
type StreamWriter interface {
|
||||
Wrap
|
||||
io.Writer
|
||||
BaseStreamWriterWithTimeout
|
||||
}
|
||||
|
||||
type StreamCloser interface {
|
||||
Wrap
|
||||
io.Closer
|
||||
}
|
||||
|
||||
type StreamReadCloser interface {
|
||||
Wrap
|
||||
io.ReadCloser
|
||||
BaseStreamReaderWithTimeout
|
||||
}
|
||||
|
||||
type StreamWriteCloser interface {
|
||||
Wrap
|
||||
io.WriteCloser
|
||||
BaseStreamWriterWithTimeout
|
||||
}
|
||||
|
||||
type StreamReadWriter interface {
|
||||
Wrap
|
||||
io.ReadWriter
|
||||
BaseStreamReaderWithTimeout
|
||||
}
|
||||
|
||||
type StreamReadWriteCloser interface {
|
||||
Wrap
|
||||
io.ReadWriteCloser
|
||||
BaseStreamReaderWithTimeout
|
||||
BaseStreamWriterWithTimeout
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package faketls
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/antireplay"
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/obfuscated2"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"github.com/9seconds/mtg/stats"
|
||||
"github.com/9seconds/mtg/tlstypes"
|
||||
"github.com/9seconds/mtg/wrappers/stream"
|
||||
)
|
||||
|
||||
type ClientProtocol struct {
|
||||
obfuscated2.ClientProtocol
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conntypes.StreamReadWriteCloser, error) {
|
||||
rewinded := stream.NewRewind(socket)
|
||||
bufferedReader := bufio.NewReader(rewinded)
|
||||
|
||||
for _, expected := range faketlsStartBytes {
|
||||
if actual, err := bufferedReader.ReadByte(); err != nil || actual != expected {
|
||||
rewinded.Rewind()
|
||||
c.cloakHost(rewinded)
|
||||
|
||||
return nil, errors.New("failed first bytes of tls handshake")
|
||||
}
|
||||
}
|
||||
|
||||
rewinded.Rewind()
|
||||
rewinded = stream.NewRewind(rewinded)
|
||||
|
||||
if err := c.tlsHandshake(rewinded); err != nil {
|
||||
rewinded.Rewind()
|
||||
c.cloakHost(rewinded)
|
||||
|
||||
return nil, fmt.Errorf("failed tls handshake: %w", err)
|
||||
}
|
||||
|
||||
conn := stream.NewFakeTLS(socket)
|
||||
|
||||
conn, err := c.ClientProtocol.Handshake(conn)
|
||||
if err != nil {
|
||||
return nil, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
return conn, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) tlsHandshake(conn io.ReadWriter) error {
|
||||
helloRecord, err := tlstypes.ReadRecord(conn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read initial record: %w", err)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
helloRecord.Data.WriteBytes(buf)
|
||||
|
||||
clientHello, err := tlstypes.ParseClientHello(buf.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse client hello: %w", err)
|
||||
}
|
||||
|
||||
digest := clientHello.Digest()
|
||||
for i := 0; i < len(digest)-4; i++ {
|
||||
if digest[i] != 0 {
|
||||
return errBadDigest
|
||||
}
|
||||
}
|
||||
|
||||
timestamp := int64(binary.LittleEndian.Uint32(digest[len(digest)-4:]))
|
||||
createdAt := time.Unix(timestamp, 0)
|
||||
timeDiff := time.Since(createdAt)
|
||||
|
||||
if (timeDiff > TimeSkew || timeDiff < -TimeSkew) && timestamp > TimeFromBoot {
|
||||
return errBadTime
|
||||
}
|
||||
|
||||
if antireplay.Cache.HasTLS(clientHello.Random[:]) {
|
||||
stats.Stats.ReplayDetected()
|
||||
|
||||
return errors.New("replay attack is detected")
|
||||
}
|
||||
|
||||
antireplay.Cache.AddTLS(clientHello.Random[:])
|
||||
serverHello := tlstypes.NewServerHello(clientHello)
|
||||
serverHelloPacket := serverHello.WelcomePacket()
|
||||
|
||||
if _, err := conn.Write(serverHelloPacket); err != nil {
|
||||
return fmt.Errorf("cannot send welcome packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) cloakHost(clientConn io.ReadWriteCloser) {
|
||||
stats.Stats.CloakedRequest()
|
||||
|
||||
addr := net.JoinHostPort(config.C.CloakHost, strconv.Itoa(config.C.CloakPort))
|
||||
|
||||
hostConn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cloak(clientConn, hostConn)
|
||||
}
|
||||
|
||||
func MakeClientProtocol() protocol.ClientProtocol {
|
||||
return &ClientProtocol{}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package faketls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/wrappers/rwc"
|
||||
)
|
||||
|
||||
const (
|
||||
cloakLastActivityTimeout = 5 * time.Second
|
||||
cloakMaxTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
func cloak(one, another io.ReadWriteCloser) {
|
||||
defer func() {
|
||||
one.Close()
|
||||
another.Close()
|
||||
}()
|
||||
|
||||
channelPing := make(chan struct{}, 1)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
one = rwc.NewPing(ctx, one, channelPing)
|
||||
another = rwc.NewPing(ctx, another, channelPing)
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
go cloakPipe(one, another, wg)
|
||||
|
||||
go cloakPipe(another, one, wg)
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
cancel()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
lastActivityTimer := time.NewTimer(cloakLastActivityTimeout)
|
||||
defer lastActivityTimer.Stop()
|
||||
|
||||
maxTimer := time.NewTimer(cloakMaxTimeout)
|
||||
defer maxTimer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-channelPing:
|
||||
lastActivityTimer.Stop()
|
||||
lastActivityTimer = time.NewTimer(cloakLastActivityTimeout)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-lastActivityTimer.C:
|
||||
cancel()
|
||||
|
||||
return
|
||||
case <-maxTimer.C:
|
||||
cancel()
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func cloakPipe(one io.Writer, another io.Reader, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
io.Copy(one, another) // nolint: errcheck
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package faketls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TimeSkew = 5 * time.Second
|
||||
TimeFromBoot = 24 * 60 * 60
|
||||
)
|
||||
|
||||
var (
|
||||
errBadDigest = errors.New("bad digest")
|
||||
errBadTime = errors.New("bad time")
|
||||
|
||||
faketlsStartBytes = [...]byte{
|
||||
0x16,
|
||||
0x03,
|
||||
0x01,
|
||||
0x02,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x01,
|
||||
0xfc,
|
||||
0x03,
|
||||
0x03,
|
||||
}
|
||||
)
|
||||
@@ -1,25 +1,3 @@
|
||||
module github.com/9seconds/mtg
|
||||
module github.com/9seconds/mtg/v2
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7
|
||||
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15
|
||||
github.com/beevik/ntp v0.3.0
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/prometheus/client_golang v1.9.0
|
||||
github.com/prometheus/common v0.18.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/smira/go-statsd v1.3.2
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
|
||||
golang.org/x/mod v0.4.1 // indirect
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 // indirect
|
||||
)
|
||||
go 1.16
|
||||
|
||||
@@ -1,472 +0,0 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4=
|
||||
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw=
|
||||
github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU=
|
||||
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y=
|
||||
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smira/go-statsd v1.3.2 h1:1EeuzxNZ/TD9apbTOFSM9nulqfcsQFmT4u1A2DREabI=
|
||||
github.com/smira/go-statsd v1.3.2/go.mod h1:1srXJ9/pbnN04G8f4F1jUzsGOnwkPKXciyqpewGlkC4=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/mtproto"
|
||||
"github.com/9seconds/mtg/mtproto/rpc"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const connectionTTL = time.Hour
|
||||
|
||||
type connection struct {
|
||||
conn conntypes.PacketReadWriteCloser
|
||||
proxyConns map[string]*ProxyConn
|
||||
closeOnce sync.Once
|
||||
proxyConnsMutex sync.RWMutex
|
||||
id int
|
||||
logger *zap.SugaredLogger
|
||||
|
||||
channelDone chan struct{}
|
||||
channelWrite chan conntypes.Packet
|
||||
channelRead chan *rpc.ProxyResponse
|
||||
channelConnAttach chan *ProxyConn
|
||||
channelConnDetach chan conntypes.ConnID
|
||||
}
|
||||
|
||||
func (c *connection) run() { // nolint: cyclop
|
||||
defer c.Close()
|
||||
|
||||
ttl := time.NewTimer(connectionTTL)
|
||||
defer ttl.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.channelDone:
|
||||
for _, v := range c.proxyConns {
|
||||
v.Close()
|
||||
}
|
||||
|
||||
return
|
||||
case <-ttl.C:
|
||||
c.logger.Debugw("Closing connection by TTL")
|
||||
c.Close()
|
||||
case resp := <-c.channelRead:
|
||||
if channel, ok := c.proxyConns[string(resp.ConnID[:])]; ok {
|
||||
if resp.Type == rpc.ProxyResponseTypeCloseExt {
|
||||
channel.Close()
|
||||
} else {
|
||||
channel.put(resp)
|
||||
}
|
||||
}
|
||||
case packet := <-c.channelWrite:
|
||||
if err := c.conn.Write(packet); err != nil {
|
||||
c.logger.Debugw("Cannot write packet", "error", err)
|
||||
c.Close()
|
||||
}
|
||||
case conn := <-c.channelConnAttach:
|
||||
c.proxyConnsMutex.Lock()
|
||||
c.proxyConns[string(conn.req.ConnID[:])] = conn
|
||||
c.proxyConnsMutex.Unlock()
|
||||
conn.channelWrite = c.channelWrite
|
||||
case connID := <-c.channelConnDetach:
|
||||
if conn, ok := c.proxyConns[string(connID[:])]; ok {
|
||||
c.proxyConnsMutex.Lock()
|
||||
delete(c.proxyConns, string(connID[:]))
|
||||
c.proxyConnsMutex.Unlock()
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) readLoop() {
|
||||
for {
|
||||
packet, err := c.conn.Read()
|
||||
if err != nil {
|
||||
c.logger.Debugw("Cannot read packet", "error", err)
|
||||
c.Close()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
response, err := rpc.ParseProxyResponse(packet)
|
||||
if err != nil {
|
||||
c.logger.Debugw("Failed response", "error", err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case <-c.channelDone:
|
||||
return
|
||||
case c.channelRead <- response:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) Close() {
|
||||
c.closeOnce.Do(func() {
|
||||
c.logger.Debugw("Closing connection")
|
||||
|
||||
close(c.channelDone)
|
||||
c.conn.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func (c *connection) Done() bool {
|
||||
select {
|
||||
case <-c.channelDone:
|
||||
return true
|
||||
default:
|
||||
return c.Len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) Len() int {
|
||||
c.proxyConnsMutex.RLock()
|
||||
defer c.proxyConnsMutex.RUnlock()
|
||||
|
||||
return len(c.proxyConns)
|
||||
}
|
||||
|
||||
func (c *connection) Attach(conn *ProxyConn) error {
|
||||
select {
|
||||
case <-c.channelDone:
|
||||
return ErrClosed
|
||||
case c.channelConnAttach <- conn:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) Detach(connID conntypes.ConnID) {
|
||||
select {
|
||||
case <-c.channelDone:
|
||||
case c.channelConnDetach <- connID:
|
||||
}
|
||||
}
|
||||
|
||||
func newConnection(req *protocol.TelegramRequest) (*connection, error) {
|
||||
conn, err := mtproto.TelegramProtocol(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create a new connection: %w", err)
|
||||
}
|
||||
|
||||
id := rand.Int() // nolint: gosec
|
||||
rv := &connection{
|
||||
conn: conn,
|
||||
id: id,
|
||||
logger: zap.S().Named("hub-connection").With("id", id,
|
||||
"dc", req.ClientProtocol.DC(),
|
||||
"protocol", req.ClientProtocol.ConnectionProtocol()),
|
||||
proxyConns: make(map[string]*ProxyConn),
|
||||
|
||||
channelRead: make(chan *rpc.ProxyResponse, 1),
|
||||
channelDone: make(chan struct{}),
|
||||
channelWrite: make(chan conntypes.Packet),
|
||||
channelConnAttach: make(chan *ProxyConn),
|
||||
channelConnDetach: make(chan conntypes.ConnID),
|
||||
}
|
||||
|
||||
go rv.readLoop()
|
||||
|
||||
go rv.run()
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
)
|
||||
|
||||
type connectionList struct {
|
||||
connections []*connection
|
||||
}
|
||||
|
||||
func (c *connectionList) get(conn *ProxyConn) (*connection, error) {
|
||||
if len(c.connections) > 0 && c.connections[0].Len() < config.C.MultiplexPerConnection {
|
||||
if err := c.connections[0].Attach(conn); err == nil {
|
||||
return c.connections[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
newConn, err := newConnection(conn.req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot allocate a new connection: %w", err)
|
||||
}
|
||||
|
||||
if err = newConn.Attach(conn); err != nil {
|
||||
newConn.Close()
|
||||
|
||||
return nil, fmt.Errorf("cannot attach to the newly created connection: %w", err)
|
||||
}
|
||||
|
||||
c.connections = append(c.connections, newConn)
|
||||
lastIndex := len(c.connections) - 1
|
||||
c.connections[0], c.connections[lastIndex] = c.connections[lastIndex], c.connections[0]
|
||||
|
||||
return newConn, nil
|
||||
}
|
||||
|
||||
func (c *connectionList) gc() {
|
||||
prevLen := len(c.connections)
|
||||
if prevLen == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for i := len(c.connections) - 1; i >= 0; i-- {
|
||||
lastIndex := len(c.connections) - 1
|
||||
|
||||
if c.connections[i].Done() {
|
||||
c.connections[i].Close()
|
||||
|
||||
if len(c.connections)-1 == i {
|
||||
c.connections = c.connections[:lastIndex]
|
||||
} else {
|
||||
c.connections[i], c.connections[lastIndex] = c.connections[lastIndex], c.connections[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if prevLen != len(c.connections) {
|
||||
c.sort()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connectionList) sort() {
|
||||
if len(c.connections) > 1 {
|
||||
sort.Slice(c.connections, func(i, j int) bool {
|
||||
return c.connections[i].Len() < c.connections[j].Len()
|
||||
})
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
)
|
||||
|
||||
type hub struct {
|
||||
muxes map[int32]*mux
|
||||
mutex sync.RWMutex
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (h *hub) Register(req *protocol.TelegramRequest) (*ProxyConn, error) {
|
||||
return h.getMux(req).Get(req)
|
||||
}
|
||||
|
||||
func (h *hub) getMux(req *protocol.TelegramRequest) *mux {
|
||||
var key int32 = 32767 + int32(req.ClientProtocol.DC()) + 100000*int32(req.ClientProtocol.ConnectionProtocol())
|
||||
|
||||
h.mutex.RLock()
|
||||
m, ok := h.muxes[key]
|
||||
h.mutex.RUnlock()
|
||||
|
||||
if !ok {
|
||||
h.mutex.Lock()
|
||||
m, ok = h.muxes[key]
|
||||
|
||||
if !ok {
|
||||
m = newMux(h.ctx)
|
||||
h.muxes[key] = m
|
||||
}
|
||||
|
||||
h.mutex.Unlock()
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTimeout = errors.New("timeout")
|
||||
ErrClosed = errors.New("context is closed")
|
||||
|
||||
Hub Interface
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
func Init(ctx context.Context) {
|
||||
initOnce.Do(func() {
|
||||
Hub = &hub{
|
||||
muxes: make(map[int32]*mux),
|
||||
ctx: ctx,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package hub
|
||||
|
||||
import "github.com/9seconds/mtg/protocol"
|
||||
|
||||
type Interface interface {
|
||||
Register(*protocol.TelegramRequest) (*ProxyConn, error)
|
||||
}
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
)
|
||||
|
||||
const muxGCEvery = time.Minute
|
||||
|
||||
type muxNewRequest struct {
|
||||
req *protocol.TelegramRequest
|
||||
resp chan<- muxNewResponse
|
||||
}
|
||||
|
||||
type muxNewResponse struct {
|
||||
conn *ProxyConn
|
||||
err error
|
||||
}
|
||||
|
||||
type mux struct {
|
||||
connections connectionList
|
||||
clients map[string]*connection
|
||||
ctx context.Context
|
||||
channelClosed chan conntypes.ConnID
|
||||
channelNew chan muxNewRequest
|
||||
}
|
||||
|
||||
func (m *mux) run() {
|
||||
gcTicker := time.NewTicker(muxGCEvery)
|
||||
defer gcTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-m.ctx.Done():
|
||||
for _, v := range m.clients {
|
||||
v.Close()
|
||||
}
|
||||
|
||||
return
|
||||
case <-gcTicker.C:
|
||||
m.connections.gc()
|
||||
case req := <-m.channelNew:
|
||||
m.connections.gc()
|
||||
proxyConn := newProxyConn(req.req, m.channelClosed)
|
||||
conn, err := m.connections.get(proxyConn)
|
||||
|
||||
if err == nil {
|
||||
m.clients[string(req.req.ConnID[:])] = conn
|
||||
}
|
||||
|
||||
req.resp <- muxNewResponse{
|
||||
conn: proxyConn,
|
||||
err: err,
|
||||
}
|
||||
close(req.resp)
|
||||
case connID := <-m.channelClosed:
|
||||
if conn, ok := m.clients[string(connID[:])]; ok {
|
||||
conn.Detach(connID)
|
||||
delete(m.clients, string(connID[:]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mux) Get(req *protocol.TelegramRequest) (*ProxyConn, error) {
|
||||
resp := make(chan muxNewResponse)
|
||||
m.channelNew <- muxNewRequest{
|
||||
req: req,
|
||||
resp: resp,
|
||||
}
|
||||
|
||||
rv := <-resp
|
||||
|
||||
return rv.conn, rv.err
|
||||
}
|
||||
|
||||
func newMux(ctx context.Context) *mux {
|
||||
m := &mux{
|
||||
ctx: ctx,
|
||||
clients: make(map[string]*connection),
|
||||
channelClosed: make(chan conntypes.ConnID, 1),
|
||||
channelNew: make(chan muxNewRequest),
|
||||
}
|
||||
go m.run()
|
||||
|
||||
return m
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/mtproto/rpc"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
proxyConnWriteTimeout = 2 * time.Minute
|
||||
proxyConnReadTimeout = 2 * time.Minute
|
||||
|
||||
proxyConnBackpressureAfter = 10
|
||||
)
|
||||
|
||||
type ProxyConn struct {
|
||||
closeOnce sync.Once
|
||||
req *protocol.TelegramRequest
|
||||
channelResponse chan *rpc.ProxyResponse
|
||||
channelClosed chan<- conntypes.ConnID
|
||||
channelWrite chan<- conntypes.Packet
|
||||
channelDone chan struct{}
|
||||
}
|
||||
|
||||
func (p *ProxyConn) Read() (*rpc.ProxyResponse, error) {
|
||||
timer := time.NewTimer(proxyConnReadTimeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-timer.C:
|
||||
return nil, ErrTimeout
|
||||
case <-p.channelDone:
|
||||
return nil, ErrClosed
|
||||
case packet := <-p.channelResponse:
|
||||
return packet, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProxyConn) Write(packet conntypes.Packet) error {
|
||||
timer := time.NewTimer(proxyConnWriteTimeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-timer.C:
|
||||
return ErrTimeout
|
||||
case <-p.channelDone:
|
||||
return ErrClosed
|
||||
case p.channelWrite <- packet:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProxyConn) put(response *rpc.ProxyResponse) {
|
||||
select {
|
||||
case <-p.channelDone:
|
||||
case p.channelResponse <- response:
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProxyConn) Close() {
|
||||
p.closeOnce.Do(func() {
|
||||
close(p.channelDone)
|
||||
go func() {
|
||||
p.channelClosed <- p.req.ConnID
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func newProxyConn(req *protocol.TelegramRequest, channelClosed chan<- conntypes.ConnID) *ProxyConn {
|
||||
return &ProxyConn{
|
||||
channelResponse: make(chan *rpc.ProxyResponse, proxyConnBackpressureAfter),
|
||||
channelDone: make(chan struct{}),
|
||||
channelClosed: channelClosed,
|
||||
req: req,
|
||||
}
|
||||
}
|
||||
@@ -2,189 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/cli"
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
var version = "dev" // has to be set by ldflags
|
||||
|
||||
var (
|
||||
app = kingpin.New("mtg", "Simple MTPROTO proxy.")
|
||||
|
||||
generateSecretCommand = app.Command("generate-secret",
|
||||
"Generate new secret")
|
||||
generateCloakHost = generateSecretCommand.Flag("cloak-host",
|
||||
"A host to use for TLS cloaking.").
|
||||
Short('c').
|
||||
Default("storage.googleapis.com").
|
||||
String()
|
||||
generateSecretType = generateSecretCommand.Arg("type",
|
||||
"A type of secret to generate. Valid options are 'simple', 'secured' and 'tls'").
|
||||
Required().
|
||||
Enum("simple", "secured", "tls")
|
||||
|
||||
runCommand = app.Command("run",
|
||||
"Run new proxy instance")
|
||||
runDebug = runCommand.Flag("debug",
|
||||
"Run in debug mode.").
|
||||
Short('d').
|
||||
Envar("MTG_DEBUG").
|
||||
Bool()
|
||||
runVerbose = runCommand.Flag("verbose",
|
||||
"Run in verbose mode.").
|
||||
Short('v').
|
||||
Envar("MTG_VERBOSE").
|
||||
Bool()
|
||||
runPreferIP = runCommand.Flag("prefer-ip",
|
||||
"Prefer this IP protocol if possible. Valid options are 'ipv4' and 'ipv6'").
|
||||
Envar("MTG_PREFER_DIRECT_IP").
|
||||
Default("ipv6").
|
||||
Enum("ipv4", "ipv6")
|
||||
runBind = runCommand.Flag("bind",
|
||||
"Host:Port to bind proxy to.").
|
||||
Short('b').
|
||||
Envar("MTG_BIND").
|
||||
Default("0.0.0.0:3128").
|
||||
TCP()
|
||||
runPublicIPv4 = runCommand.Flag("public-ipv4",
|
||||
"Which IPv4 host:port to use.").
|
||||
Short('4').
|
||||
Envar("MTG_IPV4").
|
||||
TCP()
|
||||
runPublicIPv6 = runCommand.Flag("public-ipv6",
|
||||
"Which IPv6 host:port to use.").
|
||||
Short('6').
|
||||
Envar("MTG_IPV6").
|
||||
TCP()
|
||||
runStatsBind = runCommand.Flag("stats-bind",
|
||||
"Which Host:Port to bind stats server to.").
|
||||
Short('t').
|
||||
Envar("MTG_STATS_BIND").
|
||||
Default("127.0.0.1:3129").
|
||||
TCP()
|
||||
runStatsNamespace = runCommand.Flag("stats-namespace",
|
||||
"Which namespace to use for Prometheus.").
|
||||
Envar("MTG_STATS_NAMESPACE").
|
||||
Default("mtg").
|
||||
String()
|
||||
runStatsdAddress = runCommand.Flag("statsd-addr",
|
||||
"Host:port of statsd server").
|
||||
Envar("MTG_STATSD_ADDR").
|
||||
TCP()
|
||||
runStatsdTagsFormat = runCommand.Flag("statsd-tags-format",
|
||||
"Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
|
||||
Envar("MTG_STATSD_TAGS_FORMAT").
|
||||
Default("influxdb").
|
||||
Enum("datadog", "influxdb")
|
||||
runStatsdTags = runCommand.Flag("statsd-tags",
|
||||
"Tags to use for working with statsd (specified as 'key=value').").
|
||||
Envar("MTG_STATSD_TAGS").
|
||||
StringMap()
|
||||
runWriteBufferSize = runCommand.Flag("write-buffer",
|
||||
"Write buffer size. You can think about it as a buffer from client to Telegram.").
|
||||
Short('w').
|
||||
Envar("MTG_BUFFER_WRITE").
|
||||
Default("32KB").
|
||||
Bytes()
|
||||
runReadBufferSize = runCommand.Flag("read-buffer",
|
||||
"Read buffer size. You can think about it as a buffer from Telegram to client.").
|
||||
Short('r').
|
||||
Envar("MTG_BUFFER_READ").
|
||||
Default("32KB").
|
||||
Bytes()
|
||||
runTLSCloakPort = runCommand.Flag("cloak-port",
|
||||
"Port which should be used for host cloaking.").
|
||||
Envar("MTG_CLOAK_PORT").
|
||||
Default("443").
|
||||
Uint16()
|
||||
runAntiReplayMaxSize = runCommand.Flag("anti-replay-max-size",
|
||||
"Max size of antireplay cache.").
|
||||
Envar("MTG_ANTIREPLAY_MAXSIZE").
|
||||
Default("128MB").
|
||||
Bytes()
|
||||
runMultiplexPerConnection = runCommand.Flag("multiplex-per-connection",
|
||||
"How many clients can share a single connection to Telegram.").
|
||||
Envar("MTG_MULTIPLEX_PERCONNECTION").
|
||||
Default("50").
|
||||
Uint()
|
||||
runNTPServers = runCommand.Flag("ntp-server",
|
||||
"A list of NTP servers to use.").
|
||||
Envar("MTG_NTP_SERVERS").
|
||||
Default("0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org").
|
||||
Strings()
|
||||
runSecret = runCommand.Arg("secret", "Secret of this proxy.").Required().HexBytes()
|
||||
runAdtag = runCommand.Arg("adtag", "ADTag of the proxy.").HexBytes()
|
||||
)
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
app.Version(getVersion())
|
||||
app.HelpFlag.Short('h')
|
||||
|
||||
if err := utils.SetLimits(); err != nil {
|
||||
cli.Fatal(err)
|
||||
}
|
||||
|
||||
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
|
||||
case generateSecretCommand.FullCommand():
|
||||
cli.Generate(*generateSecretType, *generateCloakHost)
|
||||
case runCommand.FullCommand():
|
||||
err := config.Init(
|
||||
config.Opt{Option: config.OptionTypeDebug, Value: *runDebug},
|
||||
config.Opt{Option: config.OptionTypeVerbose, Value: *runVerbose},
|
||||
config.Opt{Option: config.OptionTypePreferIP, Value: *runPreferIP},
|
||||
config.Opt{Option: config.OptionTypeBind, Value: *runBind},
|
||||
config.Opt{Option: config.OptionTypePublicIPv4, Value: *runPublicIPv4},
|
||||
config.Opt{Option: config.OptionTypePublicIPv6, Value: *runPublicIPv6},
|
||||
config.Opt{Option: config.OptionTypeStatsBind, Value: *runStatsBind},
|
||||
config.Opt{Option: config.OptionTypeStatsNamespace, Value: *runStatsNamespace},
|
||||
config.Opt{Option: config.OptionTypeStatsdAddress, Value: *runStatsdAddress},
|
||||
config.Opt{Option: config.OptionTypeStatsdTagsFormat, Value: *runStatsdTagsFormat},
|
||||
config.Opt{Option: config.OptionTypeStatsdTags, Value: *runStatsdTags},
|
||||
config.Opt{Option: config.OptionTypeWriteBufferSize, Value: *runWriteBufferSize},
|
||||
config.Opt{Option: config.OptionTypeReadBufferSize, Value: *runReadBufferSize},
|
||||
config.Opt{Option: config.OptionTypeCloakPort, Value: *runTLSCloakPort},
|
||||
config.Opt{Option: config.OptionTypeAntiReplayMaxSize, Value: *runAntiReplayMaxSize},
|
||||
config.Opt{Option: config.OptionTypeMultiplexPerConnection, Value: *runMultiplexPerConnection},
|
||||
config.Opt{Option: config.OptionTypeNTPServers, Value: *runNTPServers},
|
||||
config.Opt{Option: config.OptionTypeSecret, Value: *runSecret},
|
||||
config.Opt{Option: config.OptionTypeAdtag, Value: *runAdtag},
|
||||
)
|
||||
if err != nil {
|
||||
cli.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cli.Proxy(); err != nil {
|
||||
cli.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getVersion() string {
|
||||
if version != "dev" {
|
||||
return version
|
||||
}
|
||||
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return version
|
||||
}
|
||||
|
||||
builder := strings.Builder{}
|
||||
builder.WriteString(info.Main.Version)
|
||||
|
||||
if info.Main.Sum != "" {
|
||||
builder.WriteString(" (checksum: ")
|
||||
builder.WriteString(info.Main.Sum)
|
||||
builder.WriteRune(')')
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
package mtproto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/mtproto/rpc"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"github.com/9seconds/mtg/telegram"
|
||||
"github.com/9seconds/mtg/wrappers/packet"
|
||||
"github.com/9seconds/mtg/wrappers/stream"
|
||||
)
|
||||
|
||||
func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.PacketReadWriteCloser, error) {
|
||||
conn, err := telegram.Middle.Dial(req.ClientProtocol.DC(),
|
||||
req.ClientProtocol.ConnectionProtocol())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot connect to telegram: %w", err)
|
||||
}
|
||||
|
||||
rpcNonceConn := packet.NewMtprotoFrame(conn, rpc.SeqNoNonce)
|
||||
|
||||
rpcNonceReq, err := doRPCNonceRequest(rpcNonceConn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot do nonce request: %w", err)
|
||||
}
|
||||
|
||||
rpcNonceResp, err := getRPCNonceResponse(rpcNonceConn, rpcNonceReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get nonce response: %w", err)
|
||||
}
|
||||
|
||||
secureConn := stream.NewMiddleProxyCipher(conn, rpcNonceReq, rpcNonceResp, telegram.Middle.Secret())
|
||||
frameConn := packet.NewMtprotoFrame(secureConn, rpc.SeqNoHandshake)
|
||||
|
||||
if err := doRPCHandshakeRequest(frameConn); err != nil {
|
||||
return nil, fmt.Errorf("cannot do handshake request: %w", err)
|
||||
}
|
||||
|
||||
if err := getRPCHandshakeResponse(frameConn); err != nil {
|
||||
return nil, fmt.Errorf("cannot get handshake response: %w", err)
|
||||
}
|
||||
|
||||
return frameConn, nil
|
||||
}
|
||||
|
||||
func doRPCNonceRequest(conn conntypes.BasePacketWriter) (*rpc.NonceRequest, error) {
|
||||
rpcNonceReq, err := rpc.NewNonceRequest(telegram.Middle.Secret())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := conn.Write(rpcNonceReq.Bytes()); err != nil {
|
||||
return nil, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
return rpcNonceReq, nil
|
||||
}
|
||||
|
||||
func getRPCNonceResponse(conn conntypes.BasePacketReader, req *rpc.NonceRequest) (*rpc.NonceResponse, error) {
|
||||
packet, err := conn.Read()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read from connection: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpc.NewNonceResponse(packet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot build rpc nonce response: %w", err)
|
||||
}
|
||||
|
||||
if err = resp.Valid(req); err != nil {
|
||||
return nil, fmt.Errorf("invalid nonce response: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func doRPCHandshakeRequest(conn conntypes.BasePacketWriter) error {
|
||||
if err := conn.Write(rpc.HandshakeRequest); err != nil {
|
||||
return fmt.Errorf("cannot make a request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRPCHandshakeResponse(conn conntypes.BasePacketReader) error {
|
||||
packet, err := conn.Read()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read a response: %w", err)
|
||||
}
|
||||
|
||||
resp, err := rpc.NewHandshakeResponse(packet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot build a handshake response: %w", err)
|
||||
}
|
||||
|
||||
if err := resp.Valid(); err != nil {
|
||||
return fmt.Errorf("invalid handshake response: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package rpc
|
||||
|
||||
// SeqNo* is the number of the sequence which have special meaning for
|
||||
// the Telegram.
|
||||
const (
|
||||
SeqNoNonce = -2
|
||||
SeqNoHandshake = -1
|
||||
)
|
||||
|
||||
// Different constants for RPC protocol.
|
||||
var (
|
||||
TagCloseExt = []byte{0xa2, 0x34, 0xb6, 0x5e}
|
||||
TagProxyAns = []byte{0x0d, 0xda, 0x03, 0x44}
|
||||
TagSimpleAck = []byte{0x9b, 0x40, 0xac, 0x3b}
|
||||
TagHandshake = []byte{0xf5, 0xee, 0x82, 0x76}
|
||||
TagNonce = []byte{0xaa, 0x87, 0xcb, 0x7a}
|
||||
TagProxyRequest = []byte{0xee, 0xf1, 0xce, 0x36}
|
||||
|
||||
NonceCryptoAES = []byte{0x01, 0x00, 0x00, 0x00}
|
||||
|
||||
HandshakeFlags = []byte{0x00, 0x00, 0x00, 0x00}
|
||||
|
||||
ProxyRequestExtraSize = []byte{0x18, 0x00, 0x00, 0x00}
|
||||
ProxyRequestProxyTag = []byte{0xae, 0x26, 0x1e, 0xdb}
|
||||
|
||||
HandshakeSenderPID = []byte("IPIPPRPDTIME")
|
||||
HandshakePeerPID = []byte("IPIPPRPDTIME")
|
||||
)
|
||||
@@ -1,5 +0,0 @@
|
||||
package rpc
|
||||
|
||||
var HandshakeRequest = append(TagHandshake,
|
||||
append(HandshakeFlags,
|
||||
append(HandshakeSenderPID, HandshakePeerPID...)...)...)
|
||||
@@ -1,54 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type HandshakeResponse struct {
|
||||
Type []byte
|
||||
Flags []byte
|
||||
SenderPID []byte
|
||||
PeerPID []byte
|
||||
}
|
||||
|
||||
// Bytes returns a serialized handshake response.
|
||||
func (r *HandshakeResponse) Bytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.Write(r.Type)
|
||||
buf.Write(r.Flags)
|
||||
buf.Write(r.SenderPID)
|
||||
buf.Write(r.PeerPID)
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Valid checks that handshake response compliments request.
|
||||
func (r *HandshakeResponse) Valid() error {
|
||||
if !bytes.Equal(r.Type, TagHandshake) {
|
||||
return errors.New("unexpected handshake tag")
|
||||
}
|
||||
|
||||
if !bytes.Equal(r.PeerPID, HandshakeSenderPID) {
|
||||
return errors.New("incorrect sender PID")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewHandshakeResponse constructs new handshake response from the given
|
||||
// data.
|
||||
func NewHandshakeResponse(data []byte) (*HandshakeResponse, error) {
|
||||
if len(data) != 32 {
|
||||
return nil, fmt.Errorf("incorrect handshake response length %d", len(data))
|
||||
}
|
||||
|
||||
return &HandshakeResponse{
|
||||
Type: data[:4],
|
||||
Flags: data[4:8],
|
||||
SenderPID: data[8:20],
|
||||
PeerPID: data[20:],
|
||||
}, nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NonceRequest struct {
|
||||
KeySelector []byte
|
||||
CryptoTS []byte
|
||||
Nonce []byte
|
||||
}
|
||||
|
||||
// Bytes returns serialized nonce request.
|
||||
func (r *NonceRequest) Bytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.Write(TagNonce)
|
||||
buf.Write(r.KeySelector)
|
||||
buf.Write(NonceCryptoAES)
|
||||
buf.Write(r.CryptoTS)
|
||||
buf.Write(r.Nonce)
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// NewNonceRequest builds new none request based on proxy secret.
|
||||
func NewNonceRequest(proxySecret []byte) (*NonceRequest, error) {
|
||||
nonce := make([]byte, 16)
|
||||
keySelector := make([]byte, 4)
|
||||
cryptoTS := make([]byte, 4)
|
||||
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, fmt.Errorf("cannot generate nonce: %w", err)
|
||||
}
|
||||
|
||||
copy(keySelector, proxySecret)
|
||||
|
||||
timestamp := time.Now().Truncate(time.Second).Unix() % 4294967296 // 256 ^ 4 - do not know how to name
|
||||
binary.LittleEndian.PutUint32(cryptoTS, uint32(timestamp))
|
||||
|
||||
return &NonceRequest{
|
||||
KeySelector: keySelector,
|
||||
CryptoTS: cryptoTS,
|
||||
Nonce: nonce,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type NonceResponse struct {
|
||||
NonceRequest
|
||||
|
||||
Type []byte
|
||||
Crypto []byte
|
||||
}
|
||||
|
||||
// Bytes returns serialized form of the nonce response.
|
||||
func (r *NonceResponse) Bytes() []byte {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
buf.Write(r.Type)
|
||||
buf.Write(r.KeySelector)
|
||||
buf.Write(r.Crypto)
|
||||
buf.Write(r.CryptoTS)
|
||||
buf.Write(r.Nonce)
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (r *NonceResponse) Valid(req *NonceRequest) error {
|
||||
if !bytes.Equal(r.Type, TagNonce) {
|
||||
return errors.New("unexpected RPC type")
|
||||
}
|
||||
|
||||
if !bytes.Equal(r.Crypto, NonceCryptoAES) {
|
||||
return errors.New("unexpected crypto type")
|
||||
}
|
||||
|
||||
if !bytes.Equal(r.KeySelector, req.KeySelector) {
|
||||
return errors.New("unexpected key selector")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewNonceResponse build new nonce response based on the given data.
|
||||
func NewNonceResponse(data []byte) (*NonceResponse, error) {
|
||||
if len(data) != 32 {
|
||||
return nil, fmt.Errorf("unexpected message length %d", len(data))
|
||||
}
|
||||
|
||||
return &NonceResponse{
|
||||
NonceRequest: NonceRequest{
|
||||
KeySelector: data[4:8],
|
||||
CryptoTS: data[12:16],
|
||||
Nonce: data[16:],
|
||||
},
|
||||
Type: data[:4],
|
||||
Crypto: data[8:12],
|
||||
}, nil
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ProxyRequestFlags uint32
|
||||
|
||||
const (
|
||||
ProxyRequestFlagsHasAdTag ProxyRequestFlags = 0x8
|
||||
ProxyRequestFlagsEncrypted ProxyRequestFlags = 0x2
|
||||
ProxyRequestFlagsMagic ProxyRequestFlags = 0x1000
|
||||
ProxyRequestFlagsExtMode2 ProxyRequestFlags = 0x20000
|
||||
ProxyRequestFlagsIntermediate ProxyRequestFlags = 0x20000000
|
||||
ProxyRequestFlagsAbdridged ProxyRequestFlags = 0x40000000
|
||||
ProxyRequestFlagsQuickAck ProxyRequestFlags = 0x80000000
|
||||
ProxyRequestFlagsPad ProxyRequestFlags = 0x8000000
|
||||
)
|
||||
|
||||
var ProxyRequestFlagsEncryptedPrefix [8]byte
|
||||
|
||||
func (r ProxyRequestFlags) Bytes() []byte {
|
||||
converted := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(converted, uint32(r))
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
func (r ProxyRequestFlags) String() string {
|
||||
flags := make([]string, 0, 7)
|
||||
|
||||
if r&ProxyRequestFlagsHasAdTag != 0 {
|
||||
flags = append(flags, "HAS_AD_TAG")
|
||||
}
|
||||
|
||||
if r&ProxyRequestFlagsEncrypted != 0 {
|
||||
flags = append(flags, "ENCRYPTED")
|
||||
}
|
||||
|
||||
if r&ProxyRequestFlagsMagic != 0 {
|
||||
flags = append(flags, "MAGIC")
|
||||
}
|
||||
|
||||
if r&ProxyRequestFlagsExtMode2 != 0 {
|
||||
flags = append(flags, "EXT_MODE_2")
|
||||
}
|
||||
|
||||
if r&ProxyRequestFlagsIntermediate != 0 {
|
||||
flags = append(flags, "INTERMEDIATE")
|
||||
}
|
||||
|
||||
if r&ProxyRequestFlagsAbdridged != 0 {
|
||||
flags = append(flags, "ABRIDGED")
|
||||
}
|
||||
|
||||
if r&ProxyRequestFlagsQuickAck != 0 {
|
||||
flags = append(flags, "QUICK_ACK")
|
||||
}
|
||||
|
||||
if r&ProxyRequestFlagsPad != 0 {
|
||||
flags = append(flags, "PAD")
|
||||
}
|
||||
|
||||
return strings.Join(flags, " | ")
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
)
|
||||
|
||||
type ProxyResponseType uint8
|
||||
|
||||
const (
|
||||
ProxyResponseTypeAns ProxyResponseType = iota
|
||||
ProxyResponseTypeSimpleAck
|
||||
ProxyResponseTypeCloseExt
|
||||
)
|
||||
|
||||
type ProxyResponse struct {
|
||||
Type ProxyResponseType
|
||||
ConnID conntypes.ConnID
|
||||
Payload conntypes.Packet
|
||||
}
|
||||
|
||||
func ParseProxyResponse(packet conntypes.Packet) (*ProxyResponse, error) {
|
||||
var response ProxyResponse
|
||||
|
||||
if len(packet) < 4 {
|
||||
return nil, fmt.Errorf("incorrect packet length: %d", len(packet))
|
||||
}
|
||||
|
||||
tag := packet[:4]
|
||||
|
||||
switch {
|
||||
case bytes.Equal(tag, TagProxyAns):
|
||||
response.Type = ProxyResponseTypeAns
|
||||
copy(response.ConnID[:], packet[8:16])
|
||||
response.Payload = packet[16:]
|
||||
|
||||
return &response, nil
|
||||
case bytes.Equal(tag, TagSimpleAck):
|
||||
response.Type = ProxyResponseTypeSimpleAck
|
||||
copy(response.ConnID[:], packet[4:12])
|
||||
response.Payload = packet[12:]
|
||||
|
||||
return &response, nil
|
||||
case bytes.Equal(tag, TagCloseExt):
|
||||
response.Type = ProxyResponseTypeCloseExt
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown response type %x", tag)
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package ntp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/beevik/ntp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const autoUpdatePeriod = time.Minute
|
||||
|
||||
// Fetch fetches the data on time drift.
|
||||
func Fetch() (time.Duration, error) {
|
||||
url := config.C.NTPServers[rand.Intn(len(config.C.NTPServers))] // nolint: gosec
|
||||
|
||||
resp, err := ntp.Query(url)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot fetch NTP server %s: %w", url, err)
|
||||
}
|
||||
|
||||
offsetInt := int64(resp.ClockOffset)
|
||||
if offsetInt < 0 {
|
||||
offsetInt = -offsetInt
|
||||
}
|
||||
|
||||
offset := time.Duration(offsetInt)
|
||||
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// AutoUpdate runs periodic check of current time .drift state.
|
||||
func AutoUpdate() {
|
||||
logger := zap.S().Named("ntp")
|
||||
|
||||
for range time.Tick(autoUpdatePeriod) {
|
||||
diff, err := Fetch()
|
||||
if err != nil {
|
||||
logger.Debugw("Cannot fetch time from NTP", "error", err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case diff < 400*time.Millisecond:
|
||||
logger.Debugw("NTP time drift", "value", diff.String())
|
||||
case diff < 600*time.Millisecond:
|
||||
logger.Infow("NTP time drift", "value", diff.String())
|
||||
case diff < 800*time.Millisecond:
|
||||
logger.Warnw("NTP time drift", "value", diff.String())
|
||||
default:
|
||||
logger.Errorw("NTP time drift", "value", diff.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package obfuscated2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/antireplay"
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"github.com/9seconds/mtg/stats"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
"github.com/9seconds/mtg/wrappers/stream"
|
||||
)
|
||||
|
||||
const clientProtocolHandshakeTimeout = 10 * time.Second
|
||||
|
||||
type ClientProtocol struct {
|
||||
connectionType conntypes.ConnectionType
|
||||
connectionProtocol conntypes.ConnectionProtocol
|
||||
dc conntypes.DC
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) ConnectionType() conntypes.ConnectionType {
|
||||
return c.connectionType
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) ConnectionProtocol() conntypes.ConnectionProtocol {
|
||||
return c.connectionProtocol
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) DC() conntypes.DC {
|
||||
return c.dc
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conntypes.StreamReadWriteCloser, error) {
|
||||
fm, err := c.ReadFrame(socket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot make a client handshake: %w", err)
|
||||
}
|
||||
|
||||
decHasher := sha256.New()
|
||||
decHasher.Write(fm.Key()) // nolint: errcheck
|
||||
decHasher.Write(config.C.Secret) // nolint: errcheck
|
||||
decryptor := utils.MakeStreamCipher(decHasher.Sum(nil), fm.IV())
|
||||
|
||||
invertedFrame := fm.Invert()
|
||||
encHasher := sha256.New()
|
||||
encHasher.Write(invertedFrame.Key()) // nolint: errcheck
|
||||
encHasher.Write(config.C.Secret) // nolint: errcheck
|
||||
encryptor := utils.MakeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
|
||||
|
||||
decryptedFrame := Frame{}
|
||||
decryptor.XORKeyStream(decryptedFrame.Bytes(), fm.Bytes())
|
||||
|
||||
magic := decryptedFrame.Magic()
|
||||
|
||||
switch {
|
||||
case bytes.Equal(magic, conntypes.ConnectionTagAbridged):
|
||||
c.connectionType = conntypes.ConnectionTypeAbridged
|
||||
case bytes.Equal(magic, conntypes.ConnectionTagIntermediate):
|
||||
c.connectionType = conntypes.ConnectionTypeIntermediate
|
||||
case bytes.Equal(magic, conntypes.ConnectionTagSecure):
|
||||
c.connectionType = conntypes.ConnectionTypeSecure
|
||||
default:
|
||||
return nil, errors.New("unknown connection type")
|
||||
}
|
||||
|
||||
c.connectionProtocol = conntypes.ConnectionProtocolIPv4
|
||||
if socket.LocalAddr().IP.To4() == nil {
|
||||
c.connectionProtocol = conntypes.ConnectionProtocolIPv6
|
||||
}
|
||||
|
||||
buf := bytes.NewReader(decryptedFrame.DC())
|
||||
if err := binary.Read(buf, binary.LittleEndian, &c.dc); err != nil {
|
||||
c.dc = conntypes.DCDefaultIdx
|
||||
}
|
||||
|
||||
replayKey := decryptedFrame.Unique()
|
||||
if antireplay.Cache.HasObfuscated2(replayKey) {
|
||||
stats.Stats.ReplayDetected()
|
||||
|
||||
return nil, errors.New("replay attack is detected")
|
||||
}
|
||||
|
||||
antireplay.Cache.AddObfuscated2(replayKey)
|
||||
|
||||
return stream.NewObfuscated2(socket, encryptor, decryptor), nil
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) ReadFrame(socket conntypes.StreamReader) (fm Frame, err error) {
|
||||
if _, err = io.ReadFull(handshakeReader{socket}, fm.Bytes()); err != nil {
|
||||
err = fmt.Errorf("cannot extract obfuscated2 frame: %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type handshakeReader struct {
|
||||
parent conntypes.StreamReader
|
||||
}
|
||||
|
||||
func (h handshakeReader) Read(p []byte) (int, error) {
|
||||
return h.parent.ReadTimeout(p, clientProtocolHandshakeTimeout)
|
||||
}
|
||||
|
||||
func MakeClientProtocol() protocol.ClientProtocol {
|
||||
return &ClientProtocol{}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package obfuscated2
|
||||
|
||||
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] = f.data[frameOffsetIV-1-i]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package obfuscated2
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"github.com/9seconds/mtg/telegram"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
"github.com/9seconds/mtg/wrappers/stream"
|
||||
)
|
||||
|
||||
func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.StreamReadWriteCloser, error) {
|
||||
conn, err := telegram.Direct.Dial(req.ClientProtocol.DC(),
|
||||
req.ClientProtocol.ConnectionProtocol())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot dial to telegram: %w", err)
|
||||
}
|
||||
|
||||
conn = stream.NewTimeout(conn)
|
||||
conn = stream.NewCtx(req.Ctx, req.Cancel, conn)
|
||||
fm := generateFrame(req.ClientProtocol)
|
||||
data := fm.Bytes()
|
||||
|
||||
encryptor := utils.MakeStreamCipher(fm.Key(), fm.IV())
|
||||
decryptedFrame := fm.Invert()
|
||||
decryptor := utils.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 := conn.Write(data); err != nil {
|
||||
return nil, fmt.Errorf("cannot write handshake frame to telegram: %w", err)
|
||||
}
|
||||
|
||||
return stream.NewObfuscated2(conn, encryptor, decryptor), nil
|
||||
}
|
||||
|
||||
func generateFrame(cp protocol.ClientProtocol) (fm Frame) {
|
||||
data := fm.Bytes()
|
||||
|
||||
for {
|
||||
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(), cp.ConnectionType().Tag())
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package protocol
|
||||
|
||||
import "github.com/9seconds/mtg/conntypes"
|
||||
|
||||
type ClientProtocol interface {
|
||||
Handshake(conntypes.StreamReadWriteCloser) (conntypes.StreamReadWriteCloser, error)
|
||||
ConnectionType() conntypes.ConnectionType
|
||||
ConnectionProtocol() conntypes.ConnectionProtocol
|
||||
DC() conntypes.DC
|
||||
}
|
||||
|
||||
type ClientProtocolMaker func() ClientProtocol
|
||||
@@ -1,17 +0,0 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type TelegramRequest struct {
|
||||
Logger *zap.SugaredLogger
|
||||
ClientConn conntypes.StreamReadWriteCloser
|
||||
ConnID conntypes.ConnID
|
||||
Ctx context.Context
|
||||
Cancel context.CancelFunc
|
||||
ClientProtocol ClientProtocol
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/obfuscated2"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const directPipeBufferSize = 1024
|
||||
|
||||
func directConnection(request *protocol.TelegramRequest) error {
|
||||
telegramConnRaw, err := obfuscated2.TelegramProtocol(request)
|
||||
if err != nil {
|
||||
return err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
telegramConn := telegramConnRaw.(conntypes.StreamReadWriteCloser)
|
||||
|
||||
defer telegramConn.Close()
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
go directPipe(telegramConn, request.ClientConn, wg, request.Logger)
|
||||
|
||||
go directPipe(request.ClientConn, telegramConn, wg, request.Logger)
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func directPipe(dst io.WriteCloser, src io.ReadCloser, wg *sync.WaitGroup, logger *zap.SugaredLogger) {
|
||||
defer func() {
|
||||
dst.Close()
|
||||
src.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
buf := [directPipeBufferSize]byte{}
|
||||
|
||||
if _, err := io.CopyBuffer(dst, src, buf[:]); err != nil {
|
||||
logger.Debugw("Cannot pump sockets", "error", err)
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"github.com/9seconds/mtg/wrappers/packetack"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func middleConnection(request *protocol.TelegramRequest) {
|
||||
telegramConn, err := packetack.NewProxy(request)
|
||||
if err != nil {
|
||||
request.Logger.Debugw("Cannot dial to Telegram", "error", err)
|
||||
|
||||
return
|
||||
}
|
||||
defer telegramConn.Close()
|
||||
|
||||
var clientConn conntypes.PacketAckFullReadWriteCloser
|
||||
|
||||
switch request.ClientProtocol.ConnectionType() {
|
||||
case conntypes.ConnectionTypeAbridged:
|
||||
clientConn = packetack.NewClientAbridged(request.ClientConn)
|
||||
case conntypes.ConnectionTypeIntermediate:
|
||||
clientConn = packetack.NewClientIntermediate(request.ClientConn)
|
||||
case conntypes.ConnectionTypeSecure:
|
||||
clientConn = packetack.NewClientIntermediateSecure(request.ClientConn)
|
||||
case conntypes.ConnectionTypeUnknown:
|
||||
panic("unknown connection type")
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
go middlePipe(telegramConn, clientConn, wg, request.Logger)
|
||||
|
||||
go middlePipe(clientConn, telegramConn, wg, request.Logger)
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func middlePipe(dst conntypes.PacketAckWriteCloser,
|
||||
src conntypes.PacketAckReadCloser,
|
||||
wg *sync.WaitGroup,
|
||||
logger *zap.SugaredLogger) {
|
||||
defer func() {
|
||||
dst.Close()
|
||||
src.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
for {
|
||||
acks := conntypes.ConnectionAcks{}
|
||||
|
||||
packet, err := src.Read(&acks)
|
||||
if err != nil {
|
||||
logger.Debugw("Cannot read packet", "error", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = dst.Write(packet, &acks); err != nil {
|
||||
logger.Debugw("Cannot send packet", "error", err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"github.com/9seconds/mtg/stats"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
"github.com/9seconds/mtg/wrappers/stream"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Proxy struct {
|
||||
Logger *zap.SugaredLogger
|
||||
Context context.Context
|
||||
ClientProtocolMaker protocol.ClientProtocolMaker
|
||||
}
|
||||
|
||||
func (p *Proxy) Serve(listener net.Listener) {
|
||||
doneChan := p.Context.Done()
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-doneChan:
|
||||
return
|
||||
default:
|
||||
p.Logger.Fatalw("Cannot allocate incoming connection", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
go p.accept(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) accept(conn net.Conn) {
|
||||
defer func() {
|
||||
conn.Close()
|
||||
|
||||
if err := recover(); err != nil {
|
||||
stats.Stats.Crash()
|
||||
p.Logger.Errorw("Crash of accept handler", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
connID := conntypes.NewConnID()
|
||||
logger := p.Logger.With("connection_id", connID)
|
||||
|
||||
if err := utils.InitTCP(conn, config.C.ClientReadBuffer(), config.C.ClientWriteBuffer()); err != nil {
|
||||
logger.Errorw("Cannot initialize client TCP connection", "error", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(p.Context)
|
||||
defer cancel()
|
||||
|
||||
clientConn := stream.NewClientConn(conn, connID)
|
||||
clientConn = stream.NewCtx(ctx, cancel, clientConn)
|
||||
clientConn = stream.NewTimeout(clientConn)
|
||||
|
||||
defer clientConn.Close()
|
||||
|
||||
clientProtocol := p.ClientProtocolMaker()
|
||||
|
||||
clientConn, err := clientProtocol.Handshake(clientConn)
|
||||
if err != nil {
|
||||
stats.Stats.AuthenticationFailed()
|
||||
logger.Warnw("Cannot perform client handshake", "error", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
stats.Stats.ClientConnected(clientProtocol.ConnectionType(), clientConn.RemoteAddr())
|
||||
defer stats.Stats.ClientDisconnected(clientProtocol.ConnectionType(), clientConn.RemoteAddr())
|
||||
logger.Infow("Client connected", "addr", conn.RemoteAddr())
|
||||
|
||||
req := &protocol.TelegramRequest{
|
||||
Logger: logger,
|
||||
ClientConn: clientConn,
|
||||
ConnID: connID,
|
||||
Ctx: ctx,
|
||||
Cancel: cancel,
|
||||
ClientProtocol: clientProtocol,
|
||||
}
|
||||
|
||||
err = nil
|
||||
|
||||
if config.C.MiddleProxyMode() {
|
||||
middleConnection(req)
|
||||
} else {
|
||||
err = directConnection(req)
|
||||
}
|
||||
|
||||
logger.Infow("Client disconnected", "error", err, "addr", conn.RemoteAddr())
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Configuration options (set by environment variables during script execution)
|
||||
# - MTG_CONFIG - directory where mtg stores its configuration
|
||||
# - MTG_IMAGENAME - a name of the docker image to use
|
||||
# - MTG_PORT - which port of the host system should be used
|
||||
# - MTG_CONTAINER - a name of the container to use
|
||||
#
|
||||
# Example:
|
||||
# export MTG_CONFIG="$HOME/mtg_config"
|
||||
# export MTG_IMAGENAME="nineseconds/mtg:latest"
|
||||
# curl -sfL --compressed https://raw.githubusercontent.com/9seconds/mtg/master/run.sh | bash
|
||||
|
||||
set -eu
|
||||
|
||||
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
export MTG_CONFIG="${MTG_CONFIG:-$XDG_CONFIG_HOME/mtg}"
|
||||
|
||||
if ! [ -x "$(command -v docker)" ]; then
|
||||
echo 'Error: docker is not installed.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
id -Gn "$USER" | grep -qw 'docker' > /dev/null
|
||||
if [ $? -eq 0 ] || [ "$(id -u)" -eq '0' ]; then
|
||||
DOCKER_CMD="$(command -v docker)"
|
||||
else
|
||||
DOCKER_CMD="sudo $(command -v docker)"
|
||||
fi
|
||||
|
||||
mkdir -p "$MTG_CONFIG" || true
|
||||
|
||||
MTG_SECRET="$MTG_CONFIG/secret"
|
||||
MTG_ENV="$MTG_CONFIG/env"
|
||||
|
||||
if [ ! -f "$MTG_ENV" ]; then
|
||||
MTG_IMAGENAME="${MTG_IMAGENAME:-nineseconds/mtg:stable}"
|
||||
MTG_PORT="${MTG_PORT:-3128}"
|
||||
MTG_CONTAINER="${MTG_CONTAINER:-mtg}"
|
||||
|
||||
echo "MTG_IMAGENAME=$MTG_IMAGENAME" > "$MTG_ENV"
|
||||
echo "MTG_PORT=$MTG_PORT" >> "$MTG_ENV"
|
||||
echo "MTG_CONTAINER=$MTG_CONTAINER" >> "$MTG_ENV"
|
||||
fi
|
||||
|
||||
set -a
|
||||
source "$MTG_ENV"
|
||||
set +a
|
||||
|
||||
$DOCKER_CMD pull "$MTG_IMAGENAME" > /dev/null
|
||||
if [ ! -f "$MTG_SECRET" ]; then
|
||||
$DOCKER_CMD run \
|
||||
--rm \
|
||||
"$MTG_IMAGENAME" \
|
||||
generate-secret tls -c "$(openssl rand -hex 16).com" \
|
||||
> "$MTG_SECRET"
|
||||
fi
|
||||
|
||||
echo "Proxy secret is $(cat "$MTG_SECRET"). Port is $MTG_PORT."
|
||||
|
||||
$DOCKER_CMD ps --filter "Name=$MTG_CONTAINER" -aq | xargs -r $DOCKER_CMD rm -fv > /dev/null
|
||||
$DOCKER_CMD run \
|
||||
-d \
|
||||
--restart=unless-stopped \
|
||||
--name "$MTG_CONTAINER" \
|
||||
--ulimit nofile=51200:51200 \
|
||||
-p "$MTG_PORT:3128" \
|
||||
"$MTG_IMAGENAME" run "$(cat "$MTG_SECRET")" > /dev/null
|
||||
@@ -1,60 +0,0 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
)
|
||||
|
||||
type IngressTrafficInterface interface {
|
||||
IngressTraffic(int)
|
||||
}
|
||||
|
||||
type EgressTrafficInterface interface {
|
||||
EgressTraffic(int)
|
||||
}
|
||||
|
||||
type ClientConnectedInterface interface {
|
||||
ClientConnected(conntypes.ConnectionType, *net.TCPAddr)
|
||||
}
|
||||
|
||||
type ClientDisconnectedInterface interface {
|
||||
ClientDisconnected(conntypes.ConnectionType, *net.TCPAddr)
|
||||
}
|
||||
|
||||
type TelegramConnectedInterface interface {
|
||||
TelegramConnected(conntypes.DC, *net.TCPAddr)
|
||||
}
|
||||
|
||||
type TelegramDisconnectedInterface interface {
|
||||
TelegramDisconnected(conntypes.DC, *net.TCPAddr)
|
||||
}
|
||||
|
||||
type CrashInterface interface {
|
||||
Crash()
|
||||
}
|
||||
|
||||
type ReplayDetectedInterface interface {
|
||||
ReplayDetected()
|
||||
}
|
||||
|
||||
type AuthenticationFailedInterface interface {
|
||||
AuthenticationFailed()
|
||||
}
|
||||
|
||||
type CloakedRequestInterface interface {
|
||||
CloakedRequest()
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
IngressTrafficInterface
|
||||
EgressTrafficInterface
|
||||
ClientConnectedInterface
|
||||
ClientDisconnectedInterface
|
||||
TelegramConnectedInterface
|
||||
TelegramDisconnectedInterface
|
||||
CrashInterface
|
||||
ReplayDetectedInterface
|
||||
AuthenticationFailedInterface
|
||||
CloakedRequestInterface
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
)
|
||||
|
||||
type multiStats []Interface
|
||||
|
||||
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 conntypes.ConnectionType, addr *net.TCPAddr) {
|
||||
for i := range m {
|
||||
go m[i].ClientConnected(connectionType, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (m multiStats) ClientDisconnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
|
||||
for i := range m {
|
||||
go m[i].ClientDisconnected(connectionType, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (m multiStats) TelegramConnected(dc conntypes.DC, addr *net.TCPAddr) {
|
||||
for i := range m {
|
||||
go m[i].TelegramConnected(dc, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (m multiStats) TelegramDisconnected(dc conntypes.DC, addr *net.TCPAddr) {
|
||||
for i := range m {
|
||||
go m[i].TelegramDisconnected(dc, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (m multiStats) Crash() {
|
||||
for i := range m {
|
||||
go m[i].Crash()
|
||||
}
|
||||
}
|
||||
|
||||
func (m multiStats) ReplayDetected() {
|
||||
for i := range m {
|
||||
go m[i].ReplayDetected()
|
||||
}
|
||||
}
|
||||
|
||||
func (m multiStats) AuthenticationFailed() {
|
||||
for i := range m {
|
||||
go m[i].AuthenticationFailed()
|
||||
}
|
||||
}
|
||||
|
||||
func (m multiStats) CloakedRequest() {
|
||||
for i := range m {
|
||||
go m[i].CloakedRequest()
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
)
|
||||
|
||||
var Stats Interface
|
||||
|
||||
func Init(ctx context.Context) error {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
stats := []Interface{newStatsPrometheus(mux)}
|
||||
if config.C.StatsdAddr != nil {
|
||||
stats = append(stats, newStatsStatsd())
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", config.C.StatsBind.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot initialize stats server: %w", err)
|
||||
}
|
||||
|
||||
srv := http.Server{
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
go srv.Serve(listener) // nolint: errcheck
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
srv.Shutdown(context.Background()) // nolint: errcheck
|
||||
}()
|
||||
|
||||
Stats = multiStats(stats)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
type statsPrometheus struct {
|
||||
connections *prometheus.GaugeVec
|
||||
telegramConnections *prometheus.GaugeVec
|
||||
traffic *prometheus.GaugeVec
|
||||
crashes prometheus.Counter
|
||||
replayAttacks prometheus.Counter
|
||||
authenticationFailed prometheus.Counter
|
||||
cloakedRequests prometheus.Counter
|
||||
}
|
||||
|
||||
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 conntypes.ConnectionType, addr *net.TCPAddr) {
|
||||
s.changeConnections(connectionType, addr, 1.0)
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) ClientDisconnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
|
||||
s.changeConnections(connectionType, addr, -1.0)
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) changeConnections(connectionType conntypes.ConnectionType,
|
||||
addr *net.TCPAddr,
|
||||
increment float64) {
|
||||
labels := [...]string{
|
||||
"intermediate",
|
||||
"ipv4",
|
||||
}
|
||||
|
||||
switch connectionType {
|
||||
case conntypes.ConnectionTypeAbridged:
|
||||
labels[0] = "abridged"
|
||||
case conntypes.ConnectionTypeSecure:
|
||||
labels[0] = "secured"
|
||||
case conntypes.ConnectionTypeIntermediate:
|
||||
labels[0] = "intermediate"
|
||||
case conntypes.ConnectionTypeUnknown:
|
||||
panic("unknown connection type")
|
||||
}
|
||||
|
||||
if addr.IP.To4() == nil {
|
||||
labels[1] = "ipv6"
|
||||
}
|
||||
|
||||
s.connections.WithLabelValues(labels[:]...).Add(increment)
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) TelegramConnected(dc conntypes.DC, addr *net.TCPAddr) {
|
||||
s.changeTelegramConnections(dc, addr, 1.0)
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) TelegramDisconnected(dc conntypes.DC, addr *net.TCPAddr) {
|
||||
s.changeTelegramConnections(dc, addr, -1.0)
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) changeTelegramConnections(dc conntypes.DC, addr *net.TCPAddr, increment float64) {
|
||||
labels := [...]string{
|
||||
strconv.Itoa(int(dc)),
|
||||
"ipv4",
|
||||
}
|
||||
|
||||
if addr.IP.To4() == nil {
|
||||
labels[1] = "ipv6"
|
||||
}
|
||||
|
||||
s.telegramConnections.WithLabelValues(labels[:]...).Add(increment)
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) Crash() {
|
||||
s.crashes.Inc()
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) ReplayDetected() {
|
||||
s.replayAttacks.Inc()
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) AuthenticationFailed() {
|
||||
s.authenticationFailed.Inc()
|
||||
}
|
||||
|
||||
func (s *statsPrometheus) CloakedRequest() {
|
||||
s.cloakedRequests.Inc()
|
||||
}
|
||||
|
||||
func newStatsPrometheus(mux *http.ServeMux) Interface {
|
||||
registry := prometheus.NewPedanticRegistry()
|
||||
|
||||
instance := &statsPrometheus{
|
||||
connections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: config.C.StatsNamespace,
|
||||
Name: "connections",
|
||||
Help: "Current number of client connections to the proxy.",
|
||||
}, []string{"type", "protocol"}),
|
||||
telegramConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: config.C.StatsNamespace,
|
||||
Name: "telegram_connections",
|
||||
Help: "Current number of telegram connections established by this proxy.",
|
||||
}, []string{"dc", "protocol"}),
|
||||
traffic: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: config.C.StatsNamespace,
|
||||
Name: "traffic",
|
||||
Help: "Traffic passed through the proxy in bytes.",
|
||||
}, []string{"direction"}),
|
||||
crashes: prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: config.C.StatsNamespace,
|
||||
Name: "crashes",
|
||||
Help: "How many crashes happened.",
|
||||
}),
|
||||
replayAttacks: prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: config.C.StatsNamespace,
|
||||
Name: "replay_attacks",
|
||||
Help: "How many replay attacks were prevented.",
|
||||
}),
|
||||
authenticationFailed: prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: config.C.StatsNamespace,
|
||||
Name: "authentication_failed",
|
||||
Help: "How many authentication failed events we've seen.",
|
||||
}),
|
||||
cloakedRequests: prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: config.C.StatsNamespace,
|
||||
Name: "cloaked_requests",
|
||||
Help: "How many requests were proxified during cloaking.",
|
||||
}),
|
||||
}
|
||||
|
||||
registry.MustRegister(instance.connections)
|
||||
registry.MustRegister(instance.telegramConnections)
|
||||
registry.MustRegister(instance.traffic)
|
||||
registry.MustRegister(instance.crashes)
|
||||
registry.MustRegister(instance.replayAttacks)
|
||||
registry.MustRegister(instance.authenticationFailed)
|
||||
registry.MustRegister(instance.cloakedRequests)
|
||||
|
||||
handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
|
||||
mux.Handle("/", handler)
|
||||
|
||||
return instance
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
statsd "github.com/smira/go-statsd"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
tagTrafficIngress = &statsStatsdTag{
|
||||
name: "ingress",
|
||||
tag: statsd.StringTag("type", "ingress"),
|
||||
}
|
||||
tagTrafficEgress = &statsStatsdTag{
|
||||
name: "egress",
|
||||
tag: statsd.StringTag("type", "egress"),
|
||||
}
|
||||
|
||||
tagConnectionTypeAbridged = &statsStatsdTag{
|
||||
name: "abridged",
|
||||
tag: statsd.StringTag("type", "abridged"),
|
||||
}
|
||||
tagConnectionTypeIntermediate = &statsStatsdTag{
|
||||
name: "intermediate",
|
||||
tag: statsd.StringTag("type", "intermediate"),
|
||||
}
|
||||
tagConnectionTypeSecured = &statsStatsdTag{
|
||||
name: "secured",
|
||||
tag: statsd.StringTag("type", "secured"),
|
||||
}
|
||||
|
||||
tagConnectionProtocol4 = &statsStatsdTag{
|
||||
name: "ipv4",
|
||||
tag: statsd.StringTag("protocol", "ipv4"),
|
||||
}
|
||||
tagConnectionProtocol6 = &statsStatsdTag{
|
||||
name: "ipv6",
|
||||
tag: statsd.StringTag("protocol", "ipv6"),
|
||||
}
|
||||
)
|
||||
|
||||
type statsStatsdTag struct {
|
||||
tag statsd.Tag
|
||||
name string
|
||||
}
|
||||
|
||||
type statsStatsdLogger struct {
|
||||
log *zap.SugaredLogger
|
||||
}
|
||||
|
||||
func (s statsStatsdLogger) Printf(msg string, args ...interface{}) {
|
||||
s.log.Debugw(fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
type statsStatsd struct {
|
||||
seen map[string]struct{}
|
||||
seenMutex sync.RWMutex
|
||||
client *statsd.Client
|
||||
}
|
||||
|
||||
func (s *statsStatsd) IngressTraffic(traffic int) {
|
||||
s.gauge("traffic", int64(traffic), tagTrafficIngress)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) EgressTraffic(traffic int) {
|
||||
s.gauge("traffic", int64(traffic), tagTrafficEgress)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) ClientConnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
|
||||
s.changeConnections(connectionType, addr, 1)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) ClientDisconnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
|
||||
s.changeConnections(connectionType, addr, -1)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) changeConnections(connectionType conntypes.ConnectionType, addr *net.TCPAddr, increment int64) {
|
||||
tags := make([]*statsStatsdTag, 0, 2)
|
||||
|
||||
switch connectionType {
|
||||
case conntypes.ConnectionTypeAbridged:
|
||||
tags = append(tags, tagConnectionTypeAbridged)
|
||||
case conntypes.ConnectionTypeIntermediate:
|
||||
tags = append(tags, tagConnectionTypeIntermediate)
|
||||
case conntypes.ConnectionTypeSecure:
|
||||
tags = append(tags, tagConnectionTypeSecured)
|
||||
case conntypes.ConnectionTypeUnknown:
|
||||
panic("Unknown connection type")
|
||||
}
|
||||
|
||||
if addr.IP.To4() == nil {
|
||||
tags = append(tags, tagConnectionProtocol6)
|
||||
} else {
|
||||
tags = append(tags, tagConnectionProtocol4)
|
||||
}
|
||||
|
||||
s.gauge("connections", increment, tags...)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) TelegramConnected(dc conntypes.DC, addr *net.TCPAddr) {
|
||||
s.changeTelegramConnections(dc, addr, 1)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) TelegramDisconnected(dc conntypes.DC, addr *net.TCPAddr) {
|
||||
s.changeTelegramConnections(dc, addr, -1)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) changeTelegramConnections(dc conntypes.DC, addr *net.TCPAddr, increment int64) {
|
||||
tags := []*statsStatsdTag{
|
||||
{
|
||||
name: "dc" + strconv.Itoa(int(dc)),
|
||||
tag: statsd.IntTag("dc", int(dc)),
|
||||
},
|
||||
}
|
||||
|
||||
if addr.IP.To4() == nil {
|
||||
tags = append(tags, tagConnectionProtocol6)
|
||||
} else {
|
||||
tags = append(tags, tagConnectionProtocol4)
|
||||
}
|
||||
|
||||
s.gauge("telegram_connections", increment, tags...)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) Crash() {
|
||||
s.gauge("crashes", 1)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) ReplayDetected() {
|
||||
s.gauge("replay_attacks", 1)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) AuthenticationFailed() {
|
||||
s.gauge("authentication_failed", 1)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) CloakedRequest() {
|
||||
s.gauge("cloaked_requests", 1)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) gauge(metric string, value int64, tags ...*statsStatsdTag) {
|
||||
key, tagList := s.prepareVals(metric, tags)
|
||||
s.initGauge(metric, key, tagList)
|
||||
s.client.GaugeDelta(metric, value, tagList...)
|
||||
}
|
||||
|
||||
func (s *statsStatsd) prepareVals(metric string, tags []*statsStatsdTag) (string, []statsd.Tag) {
|
||||
tagList := make([]statsd.Tag, len(tags))
|
||||
builder := strings.Builder{}
|
||||
builder.WriteString(metric)
|
||||
|
||||
for i, v := range tags {
|
||||
builder.WriteRune('.')
|
||||
builder.WriteString(v.name)
|
||||
tagList[i] = v.tag
|
||||
}
|
||||
|
||||
return builder.String(), tagList
|
||||
}
|
||||
|
||||
func (s *statsStatsd) initGauge(metric, key string, tags []statsd.Tag) {
|
||||
s.seenMutex.RLock()
|
||||
if _, ok := s.seen[key]; ok {
|
||||
s.seenMutex.RUnlock()
|
||||
|
||||
return
|
||||
} else { // nolint: golint,revive
|
||||
s.seenMutex.RUnlock()
|
||||
}
|
||||
|
||||
s.seenMutex.Lock()
|
||||
defer s.seenMutex.Unlock()
|
||||
|
||||
if _, ok := s.seen[key]; !ok {
|
||||
s.seen[key] = struct{}{}
|
||||
s.client.Gauge(metric, 0, tags...)
|
||||
}
|
||||
}
|
||||
|
||||
func newStatsStatsd() Interface {
|
||||
prefix := strings.TrimSuffix(config.C.StatsNamespace, ".") + "."
|
||||
logger := statsStatsdLogger{
|
||||
log: zap.S().Named("stats").Named("statsd"),
|
||||
}
|
||||
|
||||
return &statsStatsd{
|
||||
seen: make(map[string]struct{}),
|
||||
client: statsd.NewClient(config.C.StatsdAddr.String(),
|
||||
statsd.SendLoopCount(2),
|
||||
statsd.ReconnectInterval(10*time.Second),
|
||||
statsd.Logger(logger),
|
||||
statsd.MetricPrefix(prefix),
|
||||
statsd.TagStyle(config.C.StatsdTagsFormat),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
)
|
||||
|
||||
const (
|
||||
addressesURLV4 = "https://core.telegram.org/getProxyConfig" // nolint: gas
|
||||
addressesURLV6 = "https://core.telegram.org/getProxyConfigV6" // nolint: gas
|
||||
)
|
||||
|
||||
var addressesProxyForSplitter = regexp.MustCompile(`\s+`)
|
||||
|
||||
func AddressesV4() (map[conntypes.DC][]string, conntypes.DC, error) {
|
||||
return getAddresses(addressesURLV4)
|
||||
}
|
||||
|
||||
func AddressesV6() (map[conntypes.DC][]string, conntypes.DC, error) {
|
||||
return getAddresses(addressesURLV6)
|
||||
}
|
||||
|
||||
func getAddresses(url string) (map[conntypes.DC][]string, conntypes.DC, error) {
|
||||
resp, err := request(url)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("cannot get http response: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Close()
|
||||
|
||||
scanner := bufio.NewScanner(resp)
|
||||
data := map[conntypes.DC][]string{}
|
||||
defaultDC := conntypes.DCDefaultIdx
|
||||
|
||||
for scanner.Scan() {
|
||||
text := strings.TrimSpace(scanner.Text())
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(text, "#"):
|
||||
continue
|
||||
case strings.HasPrefix(text, "proxy_for"):
|
||||
addr, idx, err := addressesParseProxyFor(text)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("cannot parse 'proxy_for' section: %w", err)
|
||||
}
|
||||
|
||||
if addresses, ok := data[idx]; ok {
|
||||
data[idx] = append(addresses, addr)
|
||||
} else {
|
||||
data[idx] = []string{addr}
|
||||
}
|
||||
case strings.HasPrefix(text, "default"):
|
||||
idx, err := addressesParseDefault(text)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("cannot parse 'default' section: %w", err)
|
||||
}
|
||||
|
||||
defaultDC = idx
|
||||
}
|
||||
}
|
||||
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("cannot parse http response: %w", err)
|
||||
}
|
||||
|
||||
return data, defaultDC, nil
|
||||
}
|
||||
|
||||
func addressesParseProxyFor(text string) (string, conntypes.DC, error) {
|
||||
chunks := addressesProxyForSplitter.Split(text, 3)
|
||||
if len(chunks) != 3 || chunks[0] != "proxy_for" {
|
||||
return "", 0, fmt.Errorf("incorrect config %s", text)
|
||||
}
|
||||
|
||||
dc, err := strconv.ParseInt(chunks[1], 10, 16)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("incorrect config '%s': %w", text, err)
|
||||
}
|
||||
|
||||
addr := strings.TrimRight(chunks[2], ";")
|
||||
if _, _, err = net.SplitHostPort(addr); err != nil {
|
||||
return "", 0, fmt.Errorf("incorrect config '%s': %w", text, err)
|
||||
}
|
||||
|
||||
return addr, conntypes.DC(dc), nil
|
||||
}
|
||||
|
||||
func addressesParseDefault(text string) (conntypes.DC, error) {
|
||||
chunks := addressesProxyForSplitter.Split(text, 2)
|
||||
if len(chunks) != 2 || chunks[0] != "default" {
|
||||
return 0, fmt.Errorf("incorrect config '%s'", text)
|
||||
}
|
||||
|
||||
dcString := strings.TrimRight(chunks[1], ";")
|
||||
|
||||
dc, err := strconv.ParseInt(dcString, 10, 16)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("incorrect config '%s': %w", text, err)
|
||||
}
|
||||
|
||||
return conntypes.DC(dc), nil
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
apiUserAgent = "github.com/9seconds/mtg"
|
||||
apiHTTPTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
var httpClient = http.Client{
|
||||
Timeout: apiHTTPTimeout,
|
||||
}
|
||||
|
||||
func request(url string) (io.ReadCloser, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), apiHTTPTimeout)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "text/plan")
|
||||
req.Header.Set("User-Agent", apiUserAgent)
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
if resp != nil {
|
||||
io.Copy(ioutil.Discard, resp.Body) // nolint: errcheck
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("cannot perform a request: %w", err)
|
||||
}
|
||||
|
||||
return resp.Body, err // nolint: wrapcheck
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
const secretURL = "https://core.telegram.org/getProxySecret" // nolint: gas
|
||||
|
||||
func Secret() ([]byte, error) {
|
||||
resp, err := request(secretURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot access telegram server: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Close()
|
||||
|
||||
secret, err := ioutil.ReadAll(resp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read response: %w", err)
|
||||
}
|
||||
|
||||
return secret, nil
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
"github.com/9seconds/mtg/wrappers/stream"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type baseTelegram struct {
|
||||
dialer net.Dialer
|
||||
logger *zap.SugaredLogger
|
||||
|
||||
secret []byte
|
||||
v4DefaultDC conntypes.DC
|
||||
v6DefaultDC conntypes.DC
|
||||
v4Addresses map[conntypes.DC][]string
|
||||
v6Addresses map[conntypes.DC][]string
|
||||
}
|
||||
|
||||
func (b *baseTelegram) Secret() []byte {
|
||||
return b.secret
|
||||
}
|
||||
|
||||
func (b *baseTelegram) dial(dc conntypes.DC,
|
||||
protocol conntypes.ConnectionProtocol) (conntypes.StreamReadWriteCloser, error) {
|
||||
for _, addr := range b.getAddresses(dc, protocol) {
|
||||
conn, err := b.dialer.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
b.logger.Infow("Cannot dial to Telegram", "address", addr, "error", err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := utils.InitTCP(conn, config.C.ProxyReadBuffer(), config.C.ProxyWriteBuffer()); err != nil {
|
||||
b.logger.Infow("Cannot initialize TCP socket", "address", addr, "error", err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return stream.NewTelegramConn(dc, conn), nil
|
||||
}
|
||||
|
||||
return nil, errors.New("cannot dial to the chosen DC")
|
||||
}
|
||||
|
||||
func (b *baseTelegram) getAddresses(dc conntypes.DC, protocol conntypes.ConnectionProtocol) []string {
|
||||
addresses := make([]string, 0, 2)
|
||||
protos := []conntypes.ConnectionProtocol{
|
||||
conntypes.ConnectionProtocolIPv6,
|
||||
conntypes.ConnectionProtocolIPv4,
|
||||
}
|
||||
|
||||
if config.C.PreferIP == config.PreferIPv4 {
|
||||
protos[0], protos[1] = protos[1], protos[0]
|
||||
}
|
||||
|
||||
for _, proto := range protos {
|
||||
switch {
|
||||
case proto&protocol == 0:
|
||||
case proto&conntypes.ConnectionProtocolIPv6 != 0:
|
||||
addresses = append(addresses, b.chooseAddress(b.v6Addresses, dc, b.v6DefaultDC))
|
||||
case proto&conntypes.ConnectionProtocolIPv4 != 0:
|
||||
addresses = append(addresses, b.chooseAddress(b.v4Addresses, dc, b.v4DefaultDC))
|
||||
}
|
||||
}
|
||||
|
||||
return addresses
|
||||
}
|
||||
|
||||
func (b *baseTelegram) chooseAddress(addresses map[conntypes.DC][]string,
|
||||
dc, defaultDC conntypes.DC) string {
|
||||
addrs, ok := addresses[dc]
|
||||
if !ok {
|
||||
addrs = addresses[defaultDC]
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(addrs) == 1:
|
||||
return addrs[0]
|
||||
case len(addrs) > 1:
|
||||
return addrs[rand.Intn(len(addrs))] // nolint: gosec
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package telegram
|
||||
|
||||
import "github.com/9seconds/mtg/conntypes"
|
||||
|
||||
const (
|
||||
directV4DefaultIdx conntypes.DC = 1
|
||||
directV6DefaultIdx conntypes.DC = 1
|
||||
)
|
||||
|
||||
var (
|
||||
directV4Addresses = map[conntypes.DC][]string{
|
||||
0: {"149.154.175.50:443"},
|
||||
1: {"149.154.167.51:443"},
|
||||
2: {"149.154.175.100:443"},
|
||||
3: {"149.154.167.91:443"},
|
||||
4: {"149.154.171.5:443"},
|
||||
}
|
||||
directV6Addresses = map[conntypes.DC][]string{
|
||||
0: {"[2001:b28:f23d:f001::a]:443"},
|
||||
1: {"[2001:67c:04e8:f002::a]:443"},
|
||||
2: {"[2001:b28:f23d:f003::a]:443"},
|
||||
3: {"[2001:67c:04e8:f004::a]:443"},
|
||||
4: {"[2001:b28:f23f:f005::a]:443"},
|
||||
}
|
||||
)
|
||||
|
||||
type directTelegram struct {
|
||||
baseTelegram
|
||||
}
|
||||
|
||||
func (d *directTelegram) Dial(dc conntypes.DC,
|
||||
protocol conntypes.ConnectionProtocol) (conntypes.StreamReadWriteCloser, error) {
|
||||
switch {
|
||||
case dc < 0:
|
||||
dc = -dc
|
||||
case dc == 0:
|
||||
dc = conntypes.DCDefaultIdx
|
||||
}
|
||||
|
||||
return d.baseTelegram.dial(dc-1, conntypes.ConnectionProtocolAny)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const telegramDialTimeout = 10 * time.Second
|
||||
|
||||
var (
|
||||
Direct Telegram
|
||||
Middle Telegram
|
||||
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
func Init() {
|
||||
initOnce.Do(func() {
|
||||
logger := zap.S().Named("telegram")
|
||||
|
||||
Direct = &directTelegram{
|
||||
baseTelegram: baseTelegram{
|
||||
dialer: net.Dialer{Timeout: telegramDialTimeout},
|
||||
logger: logger.Named("direct"),
|
||||
v4DefaultDC: directV4DefaultIdx,
|
||||
v6DefaultDC: directV6DefaultIdx,
|
||||
v4Addresses: directV4Addresses,
|
||||
v6Addresses: directV6Addresses,
|
||||
},
|
||||
}
|
||||
|
||||
tg := &middleTelegram{
|
||||
baseTelegram: baseTelegram{
|
||||
dialer: net.Dialer{Timeout: telegramDialTimeout},
|
||||
logger: logger.Named("middle"),
|
||||
},
|
||||
}
|
||||
if err := tg.update(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go tg.backgroundUpdate()
|
||||
|
||||
Middle = tg
|
||||
})
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package telegram
|
||||
|
||||
import "github.com/9seconds/mtg/conntypes"
|
||||
|
||||
type Telegram interface {
|
||||
Dial(conntypes.DC, conntypes.ConnectionProtocol) (conntypes.StreamReadWriteCloser, error)
|
||||
Secret() []byte
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/telegram/api"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const middleTelegramBackgroundUpdateEvery = time.Hour
|
||||
|
||||
type middleTelegram struct {
|
||||
baseTelegram
|
||||
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (m *middleTelegram) Secret() []byte {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
return m.baseTelegram.Secret()
|
||||
}
|
||||
|
||||
func (m *middleTelegram) update() error {
|
||||
secret, err := api.Secret()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot fetch secret: %w", err)
|
||||
}
|
||||
|
||||
v4Addresses, v4DefaultDC, err := api.AddressesV4()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot fetch addresses for ipv4: %w", err)
|
||||
}
|
||||
|
||||
v6Addresses, v6DefaultDC, err := api.AddressesV6()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot fetch addresses for ipv6: %w", err)
|
||||
}
|
||||
|
||||
m.mutex.Lock()
|
||||
m.secret = secret
|
||||
m.v4DefaultDC = v4DefaultDC
|
||||
m.v6DefaultDC = v6DefaultDC
|
||||
m.v4Addresses = v4Addresses
|
||||
m.v6Addresses = v6Addresses
|
||||
m.mutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *middleTelegram) backgroundUpdate() {
|
||||
logger := zap.S().Named("telegram")
|
||||
|
||||
for range time.Tick(middleTelegramBackgroundUpdateEvery) {
|
||||
if err := m.update(); err != nil {
|
||||
logger.Warnw("Cannot update Telegram proxies", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *middleTelegram) Dial(dc conntypes.DC,
|
||||
protocol conntypes.ConnectionProtocol) (conntypes.StreamReadWriteCloser, error) {
|
||||
if dc == 0 {
|
||||
dc = conntypes.DCDefaultIdx
|
||||
}
|
||||
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
return m.baseTelegram.dial(dc, protocol)
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
)
|
||||
|
||||
type ClientHello struct {
|
||||
Handshake
|
||||
}
|
||||
|
||||
func (c ClientHello) Digest() []byte {
|
||||
dirtyDigest := c.Random
|
||||
c.Random = [32]byte{}
|
||||
|
||||
rec := Record{
|
||||
Type: RecordTypeHandshake,
|
||||
Version: Version10,
|
||||
Data: &c,
|
||||
}
|
||||
|
||||
mac := hmac.New(sha256.New, config.C.Secret)
|
||||
rec.WriteBytes(mac)
|
||||
computedDigest := mac.Sum(nil)
|
||||
|
||||
for i := range computedDigest {
|
||||
computedDigest[i] ^= dirtyDigest[i]
|
||||
}
|
||||
|
||||
return computedDigest
|
||||
}
|
||||
|
||||
func ParseClientHello(raw []byte) (*ClientHello, error) {
|
||||
rv := &ClientHello{}
|
||||
|
||||
rv.Type = HandshakeType(raw[0])
|
||||
if rv.Type != HandshakeTypeClient {
|
||||
return nil, fmt.Errorf("incorrect handshake type %v", rv.Type)
|
||||
}
|
||||
|
||||
raw = raw[1:]
|
||||
sizeUint24 := utils.Uint24{}
|
||||
copy(sizeUint24[:], utils.ReverseBytes(raw[:3]))
|
||||
size := int(utils.FromUint24(sizeUint24))
|
||||
|
||||
raw = raw[3:]
|
||||
if len(raw) != size {
|
||||
return nil, fmt.Errorf("payload size mismatch (%d != %d)", len(raw), size)
|
||||
}
|
||||
|
||||
versionRaw := raw[:2]
|
||||
|
||||
switch {
|
||||
case bytes.Equal(versionRaw, Version13Bytes):
|
||||
rv.Version = Version13
|
||||
case bytes.Equal(versionRaw, Version12Bytes):
|
||||
rv.Version = Version12
|
||||
case bytes.Equal(versionRaw, Version11Bytes):
|
||||
rv.Version = Version11
|
||||
case bytes.Equal(versionRaw, Version10Bytes):
|
||||
rv.Version = Version10
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown protocol version %v", versionRaw)
|
||||
}
|
||||
|
||||
raw = raw[2:]
|
||||
copy(rv.Random[:], raw[:32])
|
||||
raw = raw[32:]
|
||||
|
||||
sessionIDLength := int(raw[0])
|
||||
raw = raw[1:]
|
||||
rv.SessionID = make([]byte, sessionIDLength)
|
||||
copy(rv.SessionID, raw)
|
||||
raw = raw[sessionIDLength:]
|
||||
|
||||
tail := make([]byte, len(raw))
|
||||
copy(tail, raw)
|
||||
rv.Tail = RawBytes(tail)
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package tlstypes
|
||||
|
||||
import "io"
|
||||
|
||||
type RecordType uint8
|
||||
|
||||
const (
|
||||
RecordTypeHandshake RecordType = 0x16
|
||||
RecordTypeApplicationData RecordType = 0x17
|
||||
RecordTypeChangeCipherSpec RecordType = 0x14
|
||||
)
|
||||
|
||||
type HandshakeType uint8
|
||||
|
||||
const (
|
||||
HandshakeTypeClient HandshakeType = 0x01
|
||||
HandshakeTypeServer HandshakeType = 0x02
|
||||
)
|
||||
|
||||
type CipherSuiteType uint8
|
||||
|
||||
const (
|
||||
CipherSuiteType_TLS_AES_128_GCM_SHA256 CipherSuiteType = iota // nolint: stylecheck,golint,revive
|
||||
CipherSuiteType_TLS_AES_256_GCM_SHA384 // nolint: stylecheck,golint,revive
|
||||
CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256 // nolint: stylecheck,golint,revive
|
||||
)
|
||||
|
||||
func (c CipherSuiteType) Bytes() []byte {
|
||||
switch c {
|
||||
case CipherSuiteType_TLS_AES_128_GCM_SHA256:
|
||||
return CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes
|
||||
case CipherSuiteType_TLS_AES_256_GCM_SHA384:
|
||||
return CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes
|
||||
case CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256:
|
||||
return CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes
|
||||
}
|
||||
|
||||
return CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes
|
||||
}
|
||||
|
||||
type Version uint8
|
||||
|
||||
func (v Version) Bytes() []byte {
|
||||
switch v {
|
||||
case Version13:
|
||||
return Version13Bytes
|
||||
case Version12:
|
||||
return Version12Bytes
|
||||
case Version11:
|
||||
return Version11Bytes
|
||||
case Version10, VersionUnknown:
|
||||
return Version10Bytes
|
||||
}
|
||||
|
||||
return Version10Bytes
|
||||
}
|
||||
|
||||
const (
|
||||
VersionUnknown Version = iota
|
||||
Version10
|
||||
Version11
|
||||
Version12
|
||||
Version13
|
||||
)
|
||||
|
||||
var (
|
||||
Version10Bytes = []byte{0x03, 0x01}
|
||||
Version11Bytes = []byte{0x03, 0x02}
|
||||
Version12Bytes = []byte{0x03, 0x03}
|
||||
Version13Bytes = []byte{0x03, 0x04}
|
||||
|
||||
CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes = []byte{0x13, 0x01} // nolint: stylecheck,golint,revive
|
||||
CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes = []byte{0x13, 0x02} // nolint: stylecheck,golint,revive
|
||||
CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes = []byte{0x13, 0x03} // nolint: stylecheck,golint,revive
|
||||
)
|
||||
|
||||
type Byter interface {
|
||||
WriteBytes(io.Writer)
|
||||
Len() int
|
||||
}
|
||||
|
||||
type RawBytes []byte
|
||||
|
||||
func (r RawBytes) WriteBytes(writer io.Writer) {
|
||||
writer.Write(r) // nolint: errcheck
|
||||
}
|
||||
|
||||
func (r RawBytes) Len() int {
|
||||
return len(r)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/9seconds/mtg/utils"
|
||||
)
|
||||
|
||||
type Handshake struct {
|
||||
Type HandshakeType
|
||||
Version Version
|
||||
Random [32]byte
|
||||
SessionID []byte
|
||||
Tail Byter
|
||||
}
|
||||
|
||||
func (h *Handshake) WriteBytes(writer io.Writer) {
|
||||
packetBuf := bytes.Buffer{}
|
||||
|
||||
writer.Write([]byte{byte(h.Type)}) // nolint: errcheck
|
||||
|
||||
packetBuf.Write(h.Version.Bytes())
|
||||
packetBuf.Write(h.Random[:])
|
||||
packetBuf.WriteByte(byte(len(h.SessionID)))
|
||||
packetBuf.Write(h.SessionID)
|
||||
h.Tail.WriteBytes(&packetBuf)
|
||||
|
||||
sizeUint24 := utils.ToUint24(uint32(packetBuf.Len()))
|
||||
sizeUint24Bytes := sizeUint24[:]
|
||||
sizeUint24Bytes[0], sizeUint24Bytes[2] = sizeUint24Bytes[2], sizeUint24Bytes[0]
|
||||
|
||||
writer.Write(sizeUint24Bytes) // nolint: errcheck
|
||||
packetBuf.WriteTo(writer) // nolint: errcheck
|
||||
}
|
||||
|
||||
func (h *Handshake) Len() int {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
h.WriteBytes(&buf)
|
||||
|
||||
return buf.Len()
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const recordMaxChunkSize = 16384 + 24
|
||||
|
||||
type Record struct {
|
||||
Type RecordType
|
||||
Version Version
|
||||
Data Byter
|
||||
}
|
||||
|
||||
func (r Record) WriteBytes(writer io.Writer) {
|
||||
writer.Write([]byte{byte(r.Type)}) // nolint: errcheck
|
||||
writer.Write(r.Version.Bytes()) // nolint: errcheck
|
||||
binary.Write(writer, binary.BigEndian, uint16(r.Data.Len())) // nolint: errcheck
|
||||
r.Data.WriteBytes(writer)
|
||||
}
|
||||
|
||||
func (r Record) Len() int {
|
||||
return 1 + 2 + 2 + r.Data.Len()
|
||||
}
|
||||
|
||||
func ReadRecord(reader io.Reader) (Record, error) {
|
||||
buf := [2]byte{}
|
||||
rec := Record{}
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:1]); err != nil {
|
||||
return rec, fmt.Errorf("cannot read record type: %w", err)
|
||||
}
|
||||
|
||||
rec.Type = RecordType(buf[0])
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:]); err != nil {
|
||||
return rec, fmt.Errorf("cannot read version: %w", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case bytes.Equal(buf[:], Version13Bytes):
|
||||
rec.Version = Version13
|
||||
case bytes.Equal(buf[:], Version12Bytes):
|
||||
rec.Version = Version12
|
||||
case bytes.Equal(buf[:], Version11Bytes):
|
||||
rec.Version = Version11
|
||||
case bytes.Equal(buf[:], Version10Bytes):
|
||||
rec.Version = Version10
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:]); err != nil {
|
||||
return rec, fmt.Errorf("cannot read data length: %w", err)
|
||||
}
|
||||
|
||||
data := make([]byte, binary.BigEndian.Uint16(buf[:]))
|
||||
if _, err := io.ReadFull(reader, data); err != nil {
|
||||
return rec, fmt.Errorf("cannot read data: %w", err)
|
||||
}
|
||||
|
||||
rec.Data = RawBytes(data)
|
||||
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
func MakeRecords(raw []byte) (arr []Record) {
|
||||
for len(raw) > 0 {
|
||||
chunkSize := recordMaxChunkSize
|
||||
if chunkSize > len(raw) {
|
||||
chunkSize = len(raw)
|
||||
}
|
||||
|
||||
arr = append(arr, Record{
|
||||
Type: RecordTypeApplicationData,
|
||||
Version: Version12,
|
||||
Data: RawBytes(raw[:chunkSize]),
|
||||
})
|
||||
raw = raw[chunkSize:]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
mrand "math/rand"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
type ServerHello struct {
|
||||
Handshake
|
||||
|
||||
clientHello *ClientHello
|
||||
}
|
||||
|
||||
func (s ServerHello) WelcomePacket() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
s.Random = [32]byte{}
|
||||
rec := Record{
|
||||
Type: RecordTypeHandshake,
|
||||
Version: Version12,
|
||||
Data: &s,
|
||||
}
|
||||
rec.WriteBytes(buf)
|
||||
|
||||
recChangeCipher := Record{
|
||||
Type: RecordTypeChangeCipherSpec,
|
||||
Version: Version12,
|
||||
Data: RawBytes([]byte{0x01}),
|
||||
}
|
||||
recChangeCipher.WriteBytes(buf)
|
||||
|
||||
hostCert := make([]byte, 1024+mrand.Intn(3092)) // nolint: gosec
|
||||
rand.Read(hostCert) // nolint: errcheck
|
||||
|
||||
recData := Record{
|
||||
Type: RecordTypeApplicationData,
|
||||
Version: Version12,
|
||||
Data: RawBytes(hostCert),
|
||||
}
|
||||
recData.WriteBytes(buf)
|
||||
|
||||
packet := buf.Bytes()
|
||||
|
||||
mac := hmac.New(sha256.New, config.C.Secret)
|
||||
mac.Write(s.clientHello.Random[:]) // nolint: errcheck
|
||||
mac.Write(packet) // nolint: errcheck
|
||||
copy(packet[11:], mac.Sum(nil))
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
func NewServerHello(clientHello *ClientHello) *ServerHello {
|
||||
rv := &ServerHello{
|
||||
clientHello: clientHello,
|
||||
}
|
||||
|
||||
rv.Type = HandshakeTypeServer
|
||||
rv.Version = Version12
|
||||
rv.SessionID = make([]byte, len(clientHello.SessionID))
|
||||
copy(rv.SessionID, clientHello.SessionID)
|
||||
|
||||
tail := bytes.NewBuffer(CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes)
|
||||
tail.WriteByte(0x00) // no compression
|
||||
makeTLSExtensions(tail)
|
||||
rv.Tail = RawBytes(tail.Bytes())
|
||||
|
||||
return rv
|
||||
}
|
||||
|
||||
func makeTLSExtensions(buf io.Writer) {
|
||||
buf.Write([]byte{ // nolint: errcheck
|
||||
0x00, 0x2e, // 46 bytes of data
|
||||
0x00, 0x33, // Extension - Key Share
|
||||
0x00, 0x24, // 36 bytes
|
||||
0x00, 0x1d, // x25519 curve
|
||||
0x00, 0x20, // 32 bytes of key
|
||||
})
|
||||
|
||||
var scalar [32]byte
|
||||
|
||||
rand.Read(scalar[:]) // nolint: errcheck
|
||||
curve, _ := curve25519.X25519(scalar[:], curve25519.Basepoint)
|
||||
buf.Write(curve) // nolint: errcheck
|
||||
|
||||
buf.Write([]byte{ // nolint: errcheck
|
||||
0x00, 0x2b, // Extension - Supported Versions
|
||||
0x00, 0x02, // 2 bytes are following
|
||||
0x03, 0x04, // TLS 1.3
|
||||
})
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func InitTCP(conn net.Conn, readBufferSize int, writeBufferSize int) error {
|
||||
tcpConn := conn.(*net.TCPConn)
|
||||
|
||||
if err := tcpConn.SetNoDelay(true); err != nil {
|
||||
return fmt.Errorf("cannot set TCP_NO_DELAY: %w", err)
|
||||
}
|
||||
|
||||
if err := tcpConn.SetReadBuffer(readBufferSize); err != nil {
|
||||
return fmt.Errorf("cannot set read buffer size: %w", err)
|
||||
}
|
||||
|
||||
if err := tcpConn.SetWriteBuffer(writeBufferSize); err != nil {
|
||||
return fmt.Errorf("cannot set write buffer size: %w", err)
|
||||
}
|
||||
|
||||
if err := tcpConn.SetKeepAlive(true); err != nil {
|
||||
return fmt.Errorf("cannot enable keep-alive: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package utils
|
||||
|
||||
import "io"
|
||||
|
||||
const readFullBufferSize = 1024 + 1 // +1 because telegram opreates with blocks mod 4
|
||||
|
||||
func ReadFull(src io.Reader) (rv []byte, err error) {
|
||||
buf := make([]byte, readFullBufferSize)
|
||||
n := readFullBufferSize
|
||||
|
||||
for n == len(buf) {
|
||||
n, err = src.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
rv = append(rv, buf[:n]...)
|
||||
}
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package utils
|
||||
|
||||
// ReverseBytes is a common slice reverser.
|
||||
func ReverseBytes(data []byte) []byte {
|
||||
dataLen := len(data)
|
||||
rv := make([]byte, dataLen)
|
||||
rv[dataLen/2] = data[dataLen/2]
|
||||
|
||||
for i := dataLen/2 - 1; i >= 0; i-- {
|
||||
opp := dataLen - i - 1
|
||||
rv[i], rv[opp] = data[opp], data[i]
|
||||
}
|
||||
|
||||
return rv
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func SetLimits() error {
|
||||
rLimit := unix.Rlimit{}
|
||||
if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rLimit); err != nil {
|
||||
return fmt.Errorf("cannot get rlimit: %w", err)
|
||||
}
|
||||
|
||||
rLimit.Cur = rLimit.Max
|
||||
|
||||
if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit); err != nil {
|
||||
return fmt.Errorf("cannot set rlimit: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package utils
|
||||
|
||||
func SetLimits() error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func GetSignalContext() context.Context {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
for range sigChan {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return ctx
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
func GetSignalContext() context.Context {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
|
||||
signal.Notify(sigChan, os.Interrupt)
|
||||
go func() {
|
||||
for range sigChan {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return ctx
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
func MakeStreamCipher(key, iv []byte) cipher.Stream {
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
||||
return cipher.NewCTR(block, iv)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package utils
|
||||
|
||||
type Uint24 [3]byte
|
||||
|
||||
func ToUint24(number uint32) Uint24 {
|
||||
return Uint24{byte(number), byte(number >> 8), byte(number >> 16)}
|
||||
}
|
||||
|
||||
func FromUint24(number Uint24) uint32 {
|
||||
return uint32(number[0]) + (uint32(number[1]) << 8) + (uint32(number[2]) << 16)
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
mtprotoFrameMinMessageLength = 12
|
||||
mtprotoFrameMaxMessageLength = 16777216
|
||||
)
|
||||
|
||||
var mtprotoFramePadding = []byte{0x04, 0x00, 0x00, 0x00}
|
||||
|
||||
// MTProtoFrame is a wrapper which converts written data to the MTProtoFrame.
|
||||
// The format of the frame:
|
||||
//
|
||||
// [ MSGLEN(4) | SEQNO(4) | MSG(...) | CRC32(4) | PADDING(4*x) ]
|
||||
//
|
||||
// MSGLEN is the length of the message + len of seqno and msglen.
|
||||
// SEQNO is the number of frame in the receive/send sequence. If client
|
||||
// sends a message with SeqNo 18, it has to receive message with SeqNo 18.
|
||||
// MSG is the data which has to be written
|
||||
// CRC32 is the CRC32 checksum of MSGLEN + SEQNO + MSG
|
||||
// PADDING is custom padding schema to complete frame length to such that
|
||||
// len(frame) % 16 == 0
|
||||
type wrapperMtprotoFrame struct {
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
logger *zap.SugaredLogger
|
||||
readSeqNo int32
|
||||
writeSeqNo int32
|
||||
}
|
||||
|
||||
func (w *wrapperMtprotoFrame) Read() (conntypes.Packet, error) { // nolint: funlen, cyclop
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
sum := crc32.NewIEEE()
|
||||
writer := io.MultiWriter(buf, sum)
|
||||
|
||||
for {
|
||||
buf.Reset()
|
||||
sum.Reset()
|
||||
|
||||
if _, err := io.CopyN(writer, w.parent, 4); err != nil {
|
||||
return nil, fmt.Errorf("cannot read frame padding: %w", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf.Bytes(), mtprotoFramePadding) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
messageLength := binary.LittleEndian.Uint32(buf.Bytes())
|
||||
w.logger.Debugw("Read MTProto frame",
|
||||
"messageLength", messageLength,
|
||||
"sequence_number", w.readSeqNo,
|
||||
)
|
||||
|
||||
if messageLength%4 != 0 || messageLength < mtprotoFrameMinMessageLength ||
|
||||
messageLength > mtprotoFrameMaxMessageLength {
|
||||
return nil, fmt.Errorf("incorrect frame message length %d", messageLength)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
|
||||
if _, err := io.CopyN(writer, w.parent, int64(messageLength)-4-4); err != nil {
|
||||
return nil, fmt.Errorf("cannot read the message frame: %w", err)
|
||||
}
|
||||
|
||||
var seqNo int32
|
||||
|
||||
binary.Read(buf, binary.LittleEndian, &seqNo) // nolint: errcheck
|
||||
|
||||
if seqNo != w.readSeqNo {
|
||||
return nil, fmt.Errorf("unexpected sequence number %d (wait for %d)", seqNo, w.readSeqNo)
|
||||
}
|
||||
|
||||
data, _ := ioutil.ReadAll(buf)
|
||||
buf.Reset()
|
||||
// write to buf, not to writer. This is because we are going to fetch
|
||||
// crc32 checksum.
|
||||
if _, err := io.CopyN(buf, w.parent, 4); err != nil {
|
||||
return nil, fmt.Errorf("cannot read checksum: %w", err)
|
||||
}
|
||||
|
||||
checksum := binary.LittleEndian.Uint32(buf.Bytes())
|
||||
if checksum != sum.Sum32() {
|
||||
return nil, fmt.Errorf("CRC32 checksum mismatch. wait for %d, got %d", sum.Sum32(), checksum)
|
||||
}
|
||||
|
||||
w.logger.Debugw("Read MTProto frame",
|
||||
"messageLength", messageLength,
|
||||
"sequence_number", w.readSeqNo,
|
||||
"dataLength", len(data),
|
||||
"checksum", checksum,
|
||||
)
|
||||
w.readSeqNo++
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (w *wrapperMtprotoFrame) Write(p conntypes.Packet) error {
|
||||
messageLength := 4 + 4 + len(p) + 4
|
||||
paddingLength := (aes.BlockSize - messageLength%aes.BlockSize) % aes.BlockSize
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
binary.Write(buf, binary.LittleEndian, uint32(messageLength)) // nolint: errcheck
|
||||
binary.Write(buf, binary.LittleEndian, w.writeSeqNo) // nolint: errcheck
|
||||
buf.Write(p)
|
||||
|
||||
checksum := crc32.ChecksumIEEE(buf.Bytes())
|
||||
binary.Write(buf, binary.LittleEndian, checksum) // nolint: errcheck
|
||||
buf.Write(bytes.Repeat(mtprotoFramePadding, paddingLength/4))
|
||||
|
||||
w.logger.Debugw("Write MTProto frame",
|
||||
"length", len(p),
|
||||
"sequence_number", w.writeSeqNo,
|
||||
"crc32", checksum,
|
||||
"frame_length", buf.Len(),
|
||||
)
|
||||
w.writeSeqNo++
|
||||
|
||||
_, err := w.parent.Write(buf.Bytes())
|
||||
|
||||
return err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperMtprotoFrame) Close() error {
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func (w *wrapperMtprotoFrame) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperMtprotoFrame) Logger() *zap.SugaredLogger {
|
||||
return w.logger
|
||||
}
|
||||
|
||||
func (w *wrapperMtprotoFrame) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperMtprotoFrame) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func NewMtprotoFrame(parent conntypes.StreamReadWriteCloser, seqNo int32) conntypes.PacketReadWriteCloser {
|
||||
return &wrapperMtprotoFrame{
|
||||
parent: parent,
|
||||
logger: parent.Logger().Named("mtproto-frame"),
|
||||
readSeqNo: seqNo,
|
||||
writeSeqNo: seqNo,
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package packetack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
clientAbridgedSmallPacketLength = 0x7f
|
||||
clientAbridgedQuickAckLength = 0x80
|
||||
clientAbridgedLargePacketLength = 16777216 // 256 ^ 3
|
||||
)
|
||||
|
||||
type wrapperClientAbridged struct {
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
}
|
||||
|
||||
func (w *wrapperClientAbridged) Read(acks *conntypes.ConnectionAcks) (conntypes.Packet, error) {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
buf.Grow(1)
|
||||
|
||||
if _, err := io.CopyN(&buf, w.parent, 1); err != nil {
|
||||
return nil, fmt.Errorf("cannot read message length: %w", err)
|
||||
}
|
||||
|
||||
msgLength := uint32(buf.Bytes()[0])
|
||||
buf.Reset()
|
||||
|
||||
if msgLength >= clientAbridgedQuickAckLength {
|
||||
acks.Quick = true
|
||||
msgLength -= clientAbridgedQuickAckLength
|
||||
}
|
||||
|
||||
if msgLength == clientAbridgedSmallPacketLength {
|
||||
buf.Grow(3)
|
||||
|
||||
if _, err := io.CopyN(&buf, w.parent, 3); err != nil {
|
||||
return nil, fmt.Errorf("cannot read correct message length: %w", err)
|
||||
}
|
||||
|
||||
number := utils.Uint24{}
|
||||
copy(number[:], buf.Bytes())
|
||||
msgLength = utils.FromUint24(number)
|
||||
}
|
||||
|
||||
msgLength *= 4
|
||||
|
||||
buf.Reset()
|
||||
buf.Grow(int(msgLength))
|
||||
|
||||
if _, err := io.CopyN(&buf, w.parent, int64(msgLength)); err != nil {
|
||||
return nil, fmt.Errorf("cannot read message: %w", err)
|
||||
}
|
||||
|
||||
return conntypes.Packet(buf.Bytes()), nil
|
||||
}
|
||||
|
||||
func (w *wrapperClientAbridged) Write(packet conntypes.Packet, acks *conntypes.ConnectionAcks) error {
|
||||
if len(packet)%4 != 0 {
|
||||
return fmt.Errorf("incorrect packet length %d", len(packet))
|
||||
}
|
||||
|
||||
if acks.Simple {
|
||||
if _, err := w.parent.Write(utils.ReverseBytes(packet)); err != nil {
|
||||
return fmt.Errorf("cannot send a simpleacked packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
packetLength := len(packet) / 4
|
||||
|
||||
switch {
|
||||
case packetLength < clientAbridgedSmallPacketLength:
|
||||
data := append([]byte{byte(packetLength)}, packet...)
|
||||
if _, err := w.parent.Write(data); err != nil {
|
||||
return fmt.Errorf("cannot send small packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
case packetLength < clientAbridgedLargePacketLength:
|
||||
length24 := utils.ToUint24(uint32(packetLength))
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
buf.WriteByte(byte(clientAbridgedSmallPacketLength))
|
||||
buf.Write(length24[:])
|
||||
buf.Write(packet)
|
||||
|
||||
if _, err := w.parent.Write(buf.Bytes()); err != nil {
|
||||
return fmt.Errorf("cannot send large packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("packet is too big: %d", len(packet))
|
||||
}
|
||||
|
||||
func (w *wrapperClientAbridged) Close() error {
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func (w *wrapperClientAbridged) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperClientAbridged) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperClientAbridged) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperClientAbridged) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("client-abridged")
|
||||
}
|
||||
|
||||
func NewClientAbridged(parent conntypes.StreamReadWriteCloser) conntypes.PacketAckFullReadWriteCloser {
|
||||
return &wrapperClientAbridged{
|
||||
parent: parent,
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package packetack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const clientIntermediateQuickAckLength = 0x80000000
|
||||
|
||||
type wrapperClientIntermediate struct {
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediate) Read(acks *conntypes.ConnectionAcks) (conntypes.Packet, error) {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
buf.Grow(4)
|
||||
|
||||
if _, err := io.CopyN(&buf, w.parent, 4); err != nil {
|
||||
return nil, fmt.Errorf("cannot read message length: %w", err)
|
||||
}
|
||||
|
||||
length := binary.LittleEndian.Uint32(buf.Bytes())
|
||||
|
||||
if length > clientIntermediateQuickAckLength {
|
||||
acks.Quick = true
|
||||
length -= clientIntermediateQuickAckLength
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
buf.Grow(int(length))
|
||||
|
||||
if _, err := io.CopyN(&buf, w.parent, int64(length)); err != nil {
|
||||
return nil, fmt.Errorf("cannot read the message: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediate) Write(packet conntypes.Packet, acks *conntypes.ConnectionAcks) error {
|
||||
if acks.Simple {
|
||||
if _, err := w.parent.Write(packet); err != nil {
|
||||
return fmt.Errorf("cannot send simpleacked packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
length := [4]byte{}
|
||||
binary.LittleEndian.PutUint32(length[:], uint32(len(packet)))
|
||||
|
||||
if _, err := w.parent.Write(append(length[:], packet...)); err != nil {
|
||||
return fmt.Errorf("cannot send packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediate) Close() error {
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediate) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediate) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediate) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediate) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("client-intermediate")
|
||||
}
|
||||
|
||||
func NewClientIntermediate(parent conntypes.StreamReadWriteCloser) conntypes.PacketAckFullReadWriteCloser {
|
||||
return &wrapperClientIntermediate{
|
||||
parent: parent,
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package packetack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type wrapperClientIntermediateSecure struct {
|
||||
wrapperClientIntermediate
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediateSecure) Read(acks *conntypes.ConnectionAcks) (conntypes.Packet, error) {
|
||||
data, err := w.wrapperClientIntermediate.Read(acks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length := len(data) - (len(data) % 4)
|
||||
|
||||
return data[:length], nil
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediateSecure) Write(packet conntypes.Packet, acks *conntypes.ConnectionAcks) error {
|
||||
if acks.Simple {
|
||||
if _, err := w.parent.Write(packet); err != nil {
|
||||
return fmt.Errorf("cannot send simpleacked packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
paddingLength := rand.Intn(4) // nolint: gosec
|
||||
|
||||
buf.Grow(4 + len(packet) + paddingLength)
|
||||
|
||||
binary.Write(buf, binary.LittleEndian, uint32(len(packet)+paddingLength)) // nolint: errcheck
|
||||
buf.Write(packet)
|
||||
buf.Write(make([]byte, paddingLength))
|
||||
|
||||
if _, err := w.parent.Write(buf.Bytes()); err != nil {
|
||||
return fmt.Errorf("cannot send packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *wrapperClientIntermediateSecure) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("client-intermediate-secure")
|
||||
}
|
||||
|
||||
func NewClientIntermediateSecure(parent conntypes.StreamReadWriteCloser) conntypes.PacketAckFullReadWriteCloser {
|
||||
return &wrapperClientIntermediateSecure{
|
||||
wrapperClientIntermediate: wrapperClientIntermediate{
|
||||
parent: parent,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package packetack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/hub"
|
||||
"github.com/9seconds/mtg/mtproto/rpc"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
)
|
||||
|
||||
type wrapperProxy struct {
|
||||
request *protocol.TelegramRequest
|
||||
proxy *hub.ProxyConn
|
||||
clientIPPort []byte
|
||||
ourIPPort []byte
|
||||
flags rpc.ProxyRequestFlags
|
||||
}
|
||||
|
||||
func (w *wrapperProxy) Write(packet conntypes.Packet, acks *conntypes.ConnectionAcks) error {
|
||||
buf := bytes.Buffer{}
|
||||
flags := w.flags
|
||||
|
||||
if acks.Quick {
|
||||
flags |= rpc.ProxyRequestFlagsQuickAck
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(packet, rpc.ProxyRequestFlagsEncryptedPrefix[:]) {
|
||||
flags |= rpc.ProxyRequestFlagsEncrypted
|
||||
}
|
||||
|
||||
buf.Write(rpc.TagProxyRequest)
|
||||
buf.Write(flags.Bytes())
|
||||
buf.Write(w.request.ConnID[:])
|
||||
buf.Write(w.clientIPPort)
|
||||
buf.Write(w.ourIPPort)
|
||||
buf.Write(rpc.ProxyRequestExtraSize)
|
||||
buf.Write(rpc.ProxyRequestProxyTag)
|
||||
buf.WriteByte(byte(len(config.C.AdTag)))
|
||||
buf.Write(config.C.AdTag)
|
||||
buf.Write(make([]byte, (4-buf.Len()%4)%4))
|
||||
buf.Grow(len(packet))
|
||||
buf.Write(packet)
|
||||
|
||||
return w.proxy.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
func (w *wrapperProxy) Read(acks *conntypes.ConnectionAcks) (conntypes.Packet, error) {
|
||||
resp, err := w.proxy.Read()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read a response: %w", err)
|
||||
}
|
||||
|
||||
if resp.Type == rpc.ProxyResponseTypeSimpleAck {
|
||||
acks.Simple = true
|
||||
}
|
||||
|
||||
return resp.Payload, nil
|
||||
}
|
||||
|
||||
func (w *wrapperProxy) Close() error {
|
||||
w.proxy.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewProxy(request *protocol.TelegramRequest) (conntypes.PacketAckReadWriteCloser, error) {
|
||||
flags := rpc.ProxyRequestFlagsHasAdTag | rpc.ProxyRequestFlagsMagic | rpc.ProxyRequestFlagsExtMode2
|
||||
|
||||
switch request.ClientProtocol.ConnectionType() {
|
||||
case conntypes.ConnectionTypeAbridged:
|
||||
flags |= rpc.ProxyRequestFlagsAbdridged
|
||||
case conntypes.ConnectionTypeIntermediate:
|
||||
flags |= rpc.ProxyRequestFlagsIntermediate
|
||||
case conntypes.ConnectionTypeSecure:
|
||||
flags |= rpc.ProxyRequestFlagsIntermediate | rpc.ProxyRequestFlagsPad
|
||||
case conntypes.ConnectionTypeUnknown:
|
||||
panic("unknown connection type")
|
||||
}
|
||||
|
||||
proxy, err := hub.Hub.Register(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot make a new proxy wrapper: %w", err)
|
||||
}
|
||||
|
||||
return &wrapperProxy{
|
||||
flags: flags,
|
||||
request: request,
|
||||
proxy: proxy,
|
||||
clientIPPort: proxyGetIPPort(request.ClientConn.RemoteAddr()),
|
||||
ourIPPort: proxyGetIPPort(request.ClientConn.LocalAddr()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func proxyGetIPPort(addr *net.TCPAddr) []byte {
|
||||
rv := [16 + 4]byte{}
|
||||
port := [4]byte{}
|
||||
|
||||
copy(rv[:16], addr.IP.To16())
|
||||
binary.LittleEndian.PutUint32(port[:], uint32(addr.Port))
|
||||
copy(rv[16:], port[:])
|
||||
|
||||
return rv[:]
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package rwc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
type wrapperPing struct {
|
||||
parent io.ReadWriteCloser
|
||||
ctx context.Context
|
||||
channelPing chan<- struct{}
|
||||
}
|
||||
|
||||
func (w *wrapperPing) Read(p []byte) (int, error) {
|
||||
n, err := w.parent.Read(p)
|
||||
if err == nil {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
case w.channelPing <- struct{}{}:
|
||||
}
|
||||
}
|
||||
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperPing) Write(p []byte) (int, error) {
|
||||
n, err := w.parent.Write(p)
|
||||
if err == nil {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
case w.channelPing <- struct{}{}:
|
||||
}
|
||||
}
|
||||
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperPing) Close() error {
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func NewPing(ctx context.Context, parent io.ReadWriteCloser, channelPing chan<- struct{}) io.ReadWriteCloser {
|
||||
return &wrapperPing{
|
||||
parent: parent,
|
||||
ctx: ctx,
|
||||
channelPing: channelPing,
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
)
|
||||
|
||||
func NewClientConn(parent net.Conn, connID conntypes.ConnID) conntypes.StreamReadWriteCloser {
|
||||
conn := newConn(parent, connID, connPurposeClient)
|
||||
conn = NewTrafficStats(conn)
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func NewTelegramConn(dc conntypes.DC, parent net.Conn) conntypes.StreamReadWriteCloser {
|
||||
conn := newConn(parent, conntypes.ConnID{}, connPurposeTelegram)
|
||||
conn = NewTelegramStats(dc, conn)
|
||||
|
||||
return conn
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type wrapperBlockCipher struct {
|
||||
bufferedReader
|
||||
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
encryptor cipher.BlockMode
|
||||
decryptor cipher.BlockMode
|
||||
}
|
||||
|
||||
func (w *wrapperBlockCipher) Write(p []byte) (int, error) {
|
||||
encrypted, err := w.encrypt(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return w.parent.Write(encrypted)
|
||||
}
|
||||
|
||||
func (w *wrapperBlockCipher) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
encrypted, err := w.encrypt(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return w.parent.WriteTimeout(encrypted, timeout)
|
||||
}
|
||||
|
||||
func (w *wrapperBlockCipher) encrypt(p []byte) ([]byte, error) {
|
||||
if len(p)%aes.BlockSize > 0 {
|
||||
return nil, fmt.Errorf("incorrect block size %d", len(p))
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(p))
|
||||
w.encryptor.CryptBlocks(encrypted, p)
|
||||
|
||||
return encrypted, nil
|
||||
}
|
||||
|
||||
func (w *wrapperBlockCipher) Close() error {
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func (w *wrapperBlockCipher) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperBlockCipher) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("block-cipher")
|
||||
}
|
||||
|
||||
func (w *wrapperBlockCipher) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperBlockCipher) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func newBlockCipher(parent conntypes.StreamReadWriteCloser,
|
||||
encryptor, decryptor cipher.BlockMode) conntypes.StreamReadWriteCloser {
|
||||
cipher := &wrapperBlockCipher{
|
||||
parent: parent,
|
||||
encryptor: encryptor,
|
||||
decryptor: decryptor,
|
||||
}
|
||||
|
||||
cipher.readFunc = func() ([]byte, error) {
|
||||
var currentBuffer []byte
|
||||
for len(currentBuffer) == 0 || len(currentBuffer)%aes.BlockSize != 0 {
|
||||
rv, err := utils.ReadFull(cipher.parent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read data: %w", err)
|
||||
}
|
||||
|
||||
currentBuffer = append(currentBuffer, rv...)
|
||||
}
|
||||
cipher.decryptor.CryptBlocks(currentBuffer, currentBuffer)
|
||||
|
||||
return currentBuffer, nil
|
||||
}
|
||||
|
||||
return cipher
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
)
|
||||
|
||||
type bufferedReaderReadFunc func() ([]byte, error)
|
||||
|
||||
type bufferedReader struct {
|
||||
buf bytes.Buffer
|
||||
readFunc bufferedReaderReadFunc
|
||||
}
|
||||
|
||||
func (b *bufferedReader) Read(p []byte) (int, error) {
|
||||
if b.buf.Len() > 0 {
|
||||
return b.flush(p)
|
||||
}
|
||||
|
||||
res, err := b.readFunc()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
b.buf.Write(res)
|
||||
|
||||
return b.flush(p)
|
||||
}
|
||||
|
||||
func (b *bufferedReader) ReadTimeout(p []byte, _ time.Duration) (int, error) {
|
||||
return b.Read(p)
|
||||
}
|
||||
|
||||
func (b *bufferedReader) flush(p []byte) (int, error) {
|
||||
if b.buf.Len() > len(p) {
|
||||
return b.buf.Read(p)
|
||||
}
|
||||
|
||||
sizeToReturn := b.buf.Len()
|
||||
copy(p, b.buf.Bytes())
|
||||
b.buf.Reset()
|
||||
|
||||
return sizeToReturn, nil
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type connPurpose uint8
|
||||
|
||||
const (
|
||||
connPurposeClient connPurpose = 1 << iota
|
||||
connPurposeTelegram
|
||||
)
|
||||
|
||||
type wrapperConn struct {
|
||||
parent net.Conn
|
||||
connID conntypes.ConnID
|
||||
logger *zap.SugaredLogger
|
||||
localAddr *net.TCPAddr
|
||||
remoteAddr *net.TCPAddr
|
||||
}
|
||||
|
||||
func (w *wrapperConn) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
if err := w.parent.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
|
||||
w.Close()
|
||||
|
||||
return 0, fmt.Errorf("cannot set write deadline to the socket: %w", err)
|
||||
}
|
||||
|
||||
return w.Write(p)
|
||||
}
|
||||
|
||||
func (w *wrapperConn) Write(p []byte) (int, error) {
|
||||
n, err := w.parent.Write(p)
|
||||
w.logger.Debugw("write to stream", "bytes", n, "error", err)
|
||||
|
||||
if err != nil {
|
||||
w.Close()
|
||||
}
|
||||
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperConn) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
if err := w.parent.SetReadDeadline(time.Now().Add(timeout)); err != nil {
|
||||
w.Close()
|
||||
|
||||
return 0, fmt.Errorf("cannot set read deadline to the socket: %w", err)
|
||||
}
|
||||
|
||||
return w.Read(p)
|
||||
}
|
||||
|
||||
func (w *wrapperConn) Read(p []byte) (int, error) {
|
||||
n, err := w.parent.Read(p)
|
||||
w.logger.Debugw("Read from stream", "bytes", n, "error", err)
|
||||
|
||||
if err != nil {
|
||||
w.Close()
|
||||
}
|
||||
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperConn) Close() error {
|
||||
w.logger.Debugw("Close connection")
|
||||
|
||||
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(parent net.Conn,
|
||||
connID conntypes.ConnID,
|
||||
purpose connPurpose) conntypes.StreamReadWriteCloser {
|
||||
localAddr := *parent.LocalAddr().(*net.TCPAddr)
|
||||
|
||||
if parent.RemoteAddr().(*net.TCPAddr).IP.To4() != nil {
|
||||
if config.C.PublicIPv4.IP != nil {
|
||||
localAddr.IP = config.C.PublicIPv4.IP
|
||||
}
|
||||
} else if config.C.PublicIPv6.IP != nil {
|
||||
localAddr.IP = config.C.PublicIPv6.IP
|
||||
}
|
||||
|
||||
logger := zap.S().With(
|
||||
"local_address", localAddr,
|
||||
"remote_address", parent.RemoteAddr(),
|
||||
).Named("conn")
|
||||
|
||||
if purpose == connPurposeClient {
|
||||
logger = logger.Named("client").With("connection_id", connID.String())
|
||||
} else {
|
||||
logger = logger.Named("telegram")
|
||||
}
|
||||
|
||||
return &wrapperConn{
|
||||
parent: parent,
|
||||
connID: connID,
|
||||
logger: logger,
|
||||
remoteAddr: parent.RemoteAddr().(*net.TCPAddr),
|
||||
localAddr: &localAddr,
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type wrapperCtx struct {
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
w.Close()
|
||||
|
||||
return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err())
|
||||
default:
|
||||
return w.parent.WriteTimeout(p, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) Write(p []byte) (int, error) {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
w.Close()
|
||||
|
||||
return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err())
|
||||
default:
|
||||
return w.parent.Write(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
w.Close()
|
||||
|
||||
return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err())
|
||||
default:
|
||||
return w.parent.ReadTimeout(p, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) Read(p []byte) (int, error) {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
w.Close()
|
||||
|
||||
return 0, fmt.Errorf("cannot write because context was closed: %w", w.ctx.Err())
|
||||
default:
|
||||
return w.parent.Read(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) Close() error {
|
||||
w.cancel()
|
||||
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("ctx")
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperCtx) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func NewCtx(ctx context.Context,
|
||||
cancel context.CancelFunc,
|
||||
parent conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser {
|
||||
return &wrapperCtx{
|
||||
parent: parent,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/tlstypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type wrapperFakeTLS struct {
|
||||
bufferedReader
|
||||
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) Write(p []byte) (int, error) {
|
||||
return w.write(p, func(b []byte) (int, error) {
|
||||
return w.parent.Write(b)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
return w.write(p, func(b []byte) (int, error) {
|
||||
elapsed := time.Since(startTime)
|
||||
if elapsed > timeout {
|
||||
return w.parent.WriteTimeout(b, timeout-elapsed)
|
||||
}
|
||||
|
||||
return 0, errors.New("timeout")
|
||||
})
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) write(p []byte, writeFunc func([]byte) (int, error)) (int, error) {
|
||||
sum := 0
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
for _, v := range tlstypes.MakeRecords(p) {
|
||||
buf.Reset()
|
||||
v.WriteBytes(&buf)
|
||||
|
||||
_, err := writeFunc(buf.Bytes())
|
||||
if err != nil {
|
||||
return sum, err
|
||||
}
|
||||
|
||||
sum += v.Data.Len()
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("faketls")
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) Close() error {
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func NewFakeTLS(socket conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser {
|
||||
faketls := &wrapperFakeTLS{
|
||||
parent: socket,
|
||||
}
|
||||
|
||||
faketls.readFunc = func() ([]byte, error) {
|
||||
for {
|
||||
rec, err := tlstypes.ReadRecord(faketls.parent)
|
||||
if err != nil {
|
||||
return nil, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
switch rec.Type {
|
||||
case tlstypes.RecordTypeChangeCipherSpec:
|
||||
case tlstypes.RecordTypeApplicationData:
|
||||
buf := &bytes.Buffer{}
|
||||
rec.Data.WriteBytes(buf)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
case tlstypes.RecordTypeHandshake:
|
||||
return nil, errors.New("unsupported record type handshake")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported record type %v", rec.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return faketls
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/mtproto/rpc"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
)
|
||||
|
||||
type mtprotoCipherPurpose uint8
|
||||
|
||||
const (
|
||||
mtprotoCipherPurposeClient mtprotoCipherPurpose = iota
|
||||
mtprotoCipherPurposeServer
|
||||
)
|
||||
|
||||
var mtprotoEmptyIP = [4]byte{0x00, 0x00, 0x00, 0x00}
|
||||
|
||||
func NewMiddleProxyCipher(parent conntypes.StreamReadWriteCloser,
|
||||
req *rpc.NonceRequest,
|
||||
resp *rpc.NonceResponse,
|
||||
secret []byte) conntypes.StreamReadWriteCloser {
|
||||
localAddr := parent.LocalAddr()
|
||||
remoteAddr := parent.RemoteAddr()
|
||||
|
||||
encKey, encIV := mtprotoDeriveKeys(mtprotoCipherPurposeClient,
|
||||
req,
|
||||
resp,
|
||||
localAddr,
|
||||
remoteAddr,
|
||||
secret)
|
||||
decKey, decIV := mtprotoDeriveKeys(mtprotoCipherPurposeServer,
|
||||
req,
|
||||
resp,
|
||||
localAddr,
|
||||
remoteAddr,
|
||||
secret)
|
||||
|
||||
enc, _ := mtprotoMakeEncrypterDecrypter(encKey, encIV)
|
||||
_, dec := mtprotoMakeEncrypterDecrypter(decKey, decIV)
|
||||
|
||||
return newBlockCipher(parent, enc, dec)
|
||||
}
|
||||
|
||||
func mtprotoDeriveKeys(purpose mtprotoCipherPurpose,
|
||||
req *rpc.NonceRequest,
|
||||
resp *rpc.NonceResponse,
|
||||
client, remote *net.TCPAddr,
|
||||
secret []byte) ([]byte, []byte) {
|
||||
message := bytes.Buffer{}
|
||||
|
||||
message.Write(resp.Nonce)
|
||||
message.Write(req.Nonce)
|
||||
message.Write(req.CryptoTS)
|
||||
|
||||
clientIPv4 := mtprotoEmptyIP[:]
|
||||
serverIPv4 := mtprotoEmptyIP[:]
|
||||
|
||||
if client.IP.To4() != nil {
|
||||
clientIPv4 = utils.ReverseBytes(client.IP.To4())
|
||||
serverIPv4 = utils.ReverseBytes(remote.IP.To4())
|
||||
}
|
||||
|
||||
message.Write(serverIPv4)
|
||||
|
||||
var port [2]byte
|
||||
|
||||
binary.LittleEndian.PutUint16(port[:], uint16(client.Port))
|
||||
message.Write(port[:])
|
||||
|
||||
switch purpose {
|
||||
case mtprotoCipherPurposeClient:
|
||||
message.WriteString("CLIENT")
|
||||
case mtprotoCipherPurposeServer:
|
||||
message.WriteString("SERVER")
|
||||
default:
|
||||
panic("Unexpected cipher purpose")
|
||||
}
|
||||
|
||||
message.Write(clientIPv4)
|
||||
binary.LittleEndian.PutUint16(port[:], uint16(remote.Port))
|
||||
message.Write(port[:])
|
||||
message.Write(secret)
|
||||
message.Write(resp.Nonce)
|
||||
|
||||
if client.IP.To4() == nil {
|
||||
message.Write(client.IP.To16())
|
||||
message.Write(remote.IP.To16())
|
||||
}
|
||||
|
||||
message.Write(req.Nonce)
|
||||
|
||||
data := message.Bytes()
|
||||
md5sum := md5.Sum(data[1:]) // nolint: gas
|
||||
sha1sum := sha1.Sum(data) // nolint: gosec
|
||||
|
||||
key := append(md5sum[:12], sha1sum[:]...)
|
||||
iv := md5.Sum(data[2:]) // nolint: gas
|
||||
|
||||
return key, iv[:]
|
||||
}
|
||||
|
||||
func mtprotoMakeEncrypterDecrypter(key, iv []byte) (cipher.BlockMode, cipher.BlockMode) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return cipher.NewCBCEncrypter(block, iv), cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type wrapperObfuscated2 struct {
|
||||
encryptor cipher.Stream
|
||||
decryptor cipher.Stream
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
}
|
||||
|
||||
func (w *wrapperObfuscated2) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
n, err := w.parent.ReadTimeout(p, timeout)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot read stream ciphered data: %w", err)
|
||||
}
|
||||
|
||||
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 n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
w.decryptor.XORKeyStream(p, p[:n])
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (w *wrapperObfuscated2) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
buffer := bytes.Buffer{}
|
||||
|
||||
buffer.Write(p)
|
||||
|
||||
buf := buffer.Bytes()
|
||||
|
||||
w.encryptor.XORKeyStream(buf, buf)
|
||||
|
||||
return w.parent.WriteTimeout(buf, timeout)
|
||||
}
|
||||
|
||||
func (w *wrapperObfuscated2) Write(p []byte) (int, error) {
|
||||
buffer := bytes.Buffer{}
|
||||
|
||||
buffer.Write(p)
|
||||
|
||||
buf := buffer.Bytes()
|
||||
|
||||
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 conntypes.StreamReadWriteCloser,
|
||||
encryptor, decryptor cipher.Stream) conntypes.StreamReadWriteCloser {
|
||||
return &wrapperObfuscated2{
|
||||
parent: socket,
|
||||
encryptor: encryptor,
|
||||
decryptor: decryptor,
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ReadWriteCloseRewinder interface {
|
||||
conntypes.StreamReadWriteCloser
|
||||
Rewind()
|
||||
}
|
||||
|
||||
type wrapperRewind struct {
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
activeReader io.Reader
|
||||
buf bytes.Buffer
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) Write(p []byte) (int, error) {
|
||||
return w.parent.Write(p)
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
return w.parent.WriteTimeout(p, timeout)
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) Read(p []byte) (int, error) {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
|
||||
return w.activeReader.Read(p)
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) ReadTimeout(p []byte, _ time.Duration) (int, error) {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
|
||||
return w.activeReader.Read(p)
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("rewinded")
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) Close() error {
|
||||
w.buf.Reset()
|
||||
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func (w *wrapperRewind) Rewind() {
|
||||
w.mutex.Lock()
|
||||
w.activeReader = io.MultiReader(&w.buf, w.parent)
|
||||
w.mutex.Unlock()
|
||||
}
|
||||
|
||||
func NewRewind(parent conntypes.StreamReadWriteCloser) ReadWriteCloseRewinder {
|
||||
rv := &wrapperRewind{
|
||||
parent: parent,
|
||||
}
|
||||
rv.activeReader = io.TeeReader(parent, &rv.buf)
|
||||
|
||||
return rv
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/stats"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type wrapperTelegramStats struct {
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
dc conntypes.DC
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) Write(p []byte) (int, error) {
|
||||
return w.parent.Write(p)
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
return w.parent.WriteTimeout(p, timeout)
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) Read(p []byte) (int, error) {
|
||||
return w.parent.Read(p)
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
return w.parent.ReadTimeout(p, timeout)
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("stats-telegram")
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperTelegramStats) Close() error {
|
||||
var err error
|
||||
|
||||
w.once.Do(func() {
|
||||
err = w.parent.Close()
|
||||
stats.Stats.TelegramDisconnected(w.dc, w.RemoteAddr())
|
||||
})
|
||||
|
||||
return err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func NewTelegramStats(dc conntypes.DC, parent conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser {
|
||||
conn := &wrapperTelegramStats{
|
||||
parent: parent,
|
||||
dc: dc,
|
||||
}
|
||||
|
||||
stats.Stats.TelegramConnected(dc, parent.RemoteAddr())
|
||||
|
||||
return conn
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/stats"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type wrapperTrafficStats struct {
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) Write(p []byte) (int, error) {
|
||||
n, err := w.parent.Write(p)
|
||||
stats.Stats.EgressTraffic(n)
|
||||
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
n, err := w.parent.WriteTimeout(p, timeout)
|
||||
stats.Stats.EgressTraffic(n)
|
||||
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) Read(p []byte) (int, error) {
|
||||
n, err := w.parent.Read(p)
|
||||
stats.Stats.IngressTraffic(n)
|
||||
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
n, err := w.parent.ReadTimeout(p, timeout)
|
||||
stats.Stats.IngressTraffic(n)
|
||||
|
||||
return n, err // nolint: wrapcheck
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("stats-traffic")
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperTrafficStats) Close() error {
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func NewTrafficStats(parent conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser {
|
||||
return &wrapperTrafficStats{parent}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
timeoutRead = 2 * time.Minute
|
||||
timeoutWrite = 2 * time.Minute
|
||||
)
|
||||
|
||||
type wrapperTimeout struct {
|
||||
parent conntypes.StreamReadWriteCloser
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
return w.parent.WriteTimeout(p, timeout)
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) Write(p []byte) (int, error) {
|
||||
return w.parent.WriteTimeout(p, timeoutWrite)
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
|
||||
return w.parent.ReadTimeout(p, timeout)
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) Read(p []byte) (int, error) {
|
||||
return w.parent.ReadTimeout(p, timeoutRead)
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) Close() error {
|
||||
return w.parent.Close()
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) Conn() net.Conn {
|
||||
return w.parent.Conn()
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) Logger() *zap.SugaredLogger {
|
||||
return w.parent.Logger().Named("timeout")
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) LocalAddr() *net.TCPAddr {
|
||||
return w.parent.LocalAddr()
|
||||
}
|
||||
|
||||
func (w *wrapperTimeout) RemoteAddr() *net.TCPAddr {
|
||||
return w.parent.RemoteAddr()
|
||||
}
|
||||
|
||||
func NewTimeout(parent conntypes.StreamReadWriteCloser) conntypes.StreamReadWriteCloser {
|
||||
return &wrapperTimeout{
|
||||
parent: parent,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user