Highly-opinionated (ex-bullshit-free) MTPROTO proxy for Telegram. If you use v1.0 or upgrade broke you proxy, please read the chapter Version 2
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

proxy.go 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package cli
  2. import (
  3. "fmt"
  4. "net"
  5. "os"
  6. "github.com/9seconds/mtg/v2/antireplay"
  7. "github.com/9seconds/mtg/v2/events"
  8. "github.com/9seconds/mtg/v2/ipblocklist"
  9. "github.com/9seconds/mtg/v2/logger"
  10. "github.com/9seconds/mtg/v2/mtglib"
  11. "github.com/9seconds/mtg/v2/stats"
  12. "github.com/9seconds/mtg/v2/timeattack"
  13. "github.com/9seconds/mtg/v2/utils"
  14. "github.com/rs/zerolog"
  15. )
  16. type Proxy struct {
  17. base
  18. prometheusListener net.Listener
  19. prometheus *stats.PrometheusFactory
  20. statsdFactory *stats.StatsdFactory
  21. }
  22. func (c *Proxy) Run(cli *CLI, version string) error {
  23. if err := c.ReadConfig(version); err != nil {
  24. return fmt.Errorf("cannot init config: %w", err)
  25. }
  26. return c.Execute()
  27. }
  28. func (c *Proxy) Execute() error { // nolint: funlen
  29. zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs
  30. zerolog.TimestampFieldName = "timestamp"
  31. zerolog.LevelFieldName = "level"
  32. if c.Config.Debug {
  33. zerolog.SetGlobalLevel(zerolog.DebugLevel)
  34. } else {
  35. zerolog.SetGlobalLevel(zerolog.WarnLevel)
  36. }
  37. ctx := utils.RootContext()
  38. opts := mtglib.ProxyOpts{
  39. Logger: logger.NewZeroLogger(zerolog.New(os.Stdout).With().Timestamp().Logger()),
  40. Network: c.Network,
  41. AntiReplayCache: antireplay.NewNoop(),
  42. IPBlocklist: ipblocklist.NewNoop(),
  43. TimeAttackDetector: timeattack.NewNoop(),
  44. EventStream: events.NewNoopStream(),
  45. Secret: c.Config.Secret,
  46. BufferSize: c.Config.TCPBuffer.Value(mtglib.DefaultBufferSize),
  47. DomainFrontingPort: c.Config.DomainFrontingPort.Value(mtglib.DefaultDomainFrontingPort),
  48. IdleTimeout: c.Config.Network.Timeout.Idle.Value(mtglib.DefaultIdleTimeout),
  49. PreferIP: c.Config.PreferIP.Value(mtglib.DefaultPreferIP),
  50. }
  51. defer func() {
  52. opts.AntiReplayCache.Shutdown()
  53. opts.IPBlocklist.Shutdown()
  54. opts.EventStream.Shutdown()
  55. }()
  56. if opts.Concurrency == 0 {
  57. opts.Concurrency = mtglib.DefaultConcurrency
  58. }
  59. opts.Logger.BindStr("configuration", c.Config.String()).Debug("configuration")
  60. c.setupAntiReplayCache(&opts)
  61. c.setupTimeAttackDetector(&opts)
  62. if err := c.setupIPBlocklist(&opts); err != nil {
  63. return fmt.Errorf("cannot setup ipblocklist: %w", err)
  64. }
  65. if err := c.setupEventStream(&opts); err != nil {
  66. return fmt.Errorf("cannot setup event stream: %w", err)
  67. }
  68. proxy, err := mtglib.NewProxy(opts)
  69. if err != nil {
  70. return fmt.Errorf("cannot create a proxy: %w", err)
  71. }
  72. listener, err := net.Listen("tcp", c.Config.BindTo.String())
  73. if err != nil {
  74. return fmt.Errorf("cannot start proxy: %w", err)
  75. }
  76. go proxy.Serve(listener) // nolint: errcheck
  77. <-ctx.Done()
  78. listener.Close()
  79. if c.prometheusListener != nil {
  80. c.prometheusListener.Close()
  81. }
  82. if c.prometheus != nil {
  83. c.prometheus.Close()
  84. }
  85. if c.statsdFactory != nil {
  86. c.statsdFactory.Close()
  87. }
  88. return nil
  89. }
  90. func (c *Proxy) setupAntiReplayCache(opts *mtglib.ProxyOpts) {
  91. if !c.Config.Defense.AntiReplay.Enabled {
  92. return
  93. }
  94. opts.AntiReplayCache = antireplay.NewStableBloomFilter(
  95. c.Config.Defense.AntiReplay.MaxSize.Value(antireplay.DefaultMaxSize),
  96. c.Config.Defense.AntiReplay.ErrorRate.Value(antireplay.DefaultErrorRate),
  97. )
  98. }
  99. func (c *Proxy) setupTimeAttackDetector(opts *mtglib.ProxyOpts) {
  100. if !c.Config.Defense.Time.Enabled {
  101. return
  102. }
  103. opts.TimeAttackDetector = timeattack.NewDetector(
  104. c.Config.Defense.Time.AllowSkewness.Value(timeattack.DefaultDuration),
  105. )
  106. }
  107. func (c *Proxy) setupIPBlocklist(opts *mtglib.ProxyOpts) error {
  108. if !c.Config.Defense.Blocklist.Enabled {
  109. return nil
  110. }
  111. remoteURLs := []string{}
  112. localFiles := []string{}
  113. for _, v := range c.Config.Defense.Blocklist.URLs {
  114. if v.IsRemote() {
  115. remoteURLs = append(remoteURLs, v.String())
  116. } else {
  117. localFiles = append(localFiles, v.String())
  118. }
  119. }
  120. firehol, err := ipblocklist.NewFirehol(opts.Logger.Named("ipblockist"),
  121. c.Network,
  122. c.Config.Defense.Blocklist.DownloadConcurrency,
  123. remoteURLs,
  124. localFiles)
  125. if err != nil {
  126. return err // nolint: wrapcheck
  127. }
  128. go firehol.Run(c.Config.Defense.Blocklist.UpdateEach.Value(ipblocklist.DefaultUpdateEach))
  129. opts.IPBlocklist = firehol
  130. return nil
  131. }
  132. func (c *Proxy) setupEventStream(opts *mtglib.ProxyOpts) error {
  133. factories := make([]events.ObserverFactory, 0, 2)
  134. if c.Config.Stats.StatsD.Enabled {
  135. statsdFactory, err := stats.NewStatsd(
  136. c.Config.Stats.StatsD.Address.String(),
  137. opts.Logger.Named("statsd"),
  138. c.Config.Stats.StatsD.MetricPrefix.Value(stats.DefaultStatsdMetricPrefix),
  139. c.Config.Stats.StatsD.TagFormat.Value(stats.DefaultStatsdTagFormat))
  140. if err != nil {
  141. return fmt.Errorf("cannot build statsd observer: %w", err)
  142. }
  143. c.statsdFactory = &statsdFactory
  144. factories = append(factories, statsdFactory.Make)
  145. }
  146. if c.Config.Stats.Prometheus.Enabled {
  147. prometheus := stats.NewPrometheus(
  148. c.Config.Stats.Prometheus.MetricPrefix.Value(stats.DefaultMetricPrefix),
  149. c.Config.Stats.Prometheus.HTTPPath.Value("/"),
  150. )
  151. listener, err := net.Listen("tcp", c.Config.Stats.Prometheus.BindTo.String())
  152. if err != nil {
  153. return fmt.Errorf("cannot start a listener for prometheus: %w", err)
  154. }
  155. go prometheus.Serve(listener) // nolint: errcheck
  156. c.prometheusListener = listener
  157. c.prometheus = prometheus
  158. factories = append(factories, prometheus.Make)
  159. }
  160. if len(factories) > 0 {
  161. opts.EventStream = events.NewEventStream(factories)
  162. }
  163. return nil
  164. }