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
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. package doppel
  2. import (
  3. "context"
  4. "fmt"
  5. "sync"
  6. "sync/atomic"
  7. "time"
  8. "github.com/9seconds/mtg/v2/essentials"
  9. )
  10. const (
  11. DoppelGangerMaxDurations = 4096
  12. DoppelGangerScoutRaidEach = 6 * time.Hour
  13. DoppelGangerScoutRepeats = 10
  14. MinCertSizesToCalculate = 3
  15. )
  16. // NoiseParams holds the measured cert chain size for FakeTLS noise calibration.
  17. // If Mean is 0, the caller should use a legacy fallback.
  18. type NoiseParams struct {
  19. Mean int
  20. Jitter int
  21. }
  22. type scoutRaidResult struct {
  23. durations []time.Duration
  24. certSizes []int
  25. }
  26. type gangerConnRequest struct {
  27. ret chan<- Conn
  28. payload essentials.Conn
  29. }
  30. type Ganger struct {
  31. ctx context.Context
  32. ctxCancel context.CancelFunc
  33. logger Logger
  34. wg sync.WaitGroup
  35. scout Scout
  36. scoutRaidEach time.Duration
  37. scoutRaidRepeats int
  38. drs bool
  39. stats Stats
  40. durations []time.Duration
  41. certSizes []int
  42. noiseParams atomic.Pointer[NoiseParams]
  43. connRequests chan gangerConnRequest
  44. }
  45. func (g *Ganger) Shutdown() {
  46. g.ctxCancel()
  47. g.wg.Wait()
  48. }
  49. func (g *Ganger) Run() {
  50. g.wg.Go(func() {
  51. g.run()
  52. })
  53. }
  54. // NoiseParams returns the current cert-size-based noise parameters.
  55. // Returns zero-value NoiseParams if not yet measured (caller should use fallback).
  56. func (g *Ganger) NoiseParams() NoiseParams {
  57. if p := g.noiseParams.Load(); p != nil {
  58. return *p
  59. }
  60. return NoiseParams{}
  61. }
  62. func (g *Ganger) NewConn(conn essentials.Conn) (Conn, error) {
  63. rvChan := make(chan Conn)
  64. req := gangerConnRequest{
  65. ret: rvChan,
  66. payload: conn,
  67. }
  68. defer close(req.ret)
  69. select {
  70. case <-g.ctx.Done():
  71. return Conn{}, context.Cause(g.ctx)
  72. case g.connRequests <- req:
  73. }
  74. select {
  75. case <-g.ctx.Done():
  76. return Conn{}, context.Cause(g.ctx)
  77. case conn := <-rvChan:
  78. return conn, nil
  79. }
  80. }
  81. func (g *Ganger) run() {
  82. scoutTicker := time.NewTicker(g.scoutRaidEach)
  83. defer func() {
  84. scoutTicker.Stop()
  85. select {
  86. case <-scoutTicker.C:
  87. default:
  88. }
  89. }()
  90. scoutCollectedChan := make(chan scoutRaidResult)
  91. currentScoutCollectedChan := scoutCollectedChan
  92. updatedStatsChan := make(chan Stats)
  93. g.wg.Go(func() {
  94. g.runScoutRaid(scoutCollectedChan)
  95. })
  96. for {
  97. select {
  98. case <-g.ctx.Done():
  99. return
  100. case result := <-currentScoutCollectedChan:
  101. g.durations = append(g.durations, result.durations...)
  102. if len(g.durations) > DoppelGangerMaxDurations {
  103. copy(g.durations, g.durations[len(g.durations)-DoppelGangerMaxDurations:])
  104. g.durations = g.durations[:DoppelGangerMaxDurations]
  105. }
  106. // Update cert sizes and recompute noise params.
  107. g.certSizes = append(g.certSizes, result.certSizes...)
  108. if len(g.certSizes) > DoppelGangerMaxDurations {
  109. g.certSizes = g.certSizes[len(g.certSizes)-DoppelGangerMaxDurations:]
  110. }
  111. if len(g.certSizes) >= MinCertSizesToCalculate {
  112. g.updateNoiseParams()
  113. }
  114. if len(g.durations) < MinDurationsToCalculate {
  115. continue
  116. }
  117. durations := g.durations
  118. currentScoutCollectedChan = nil
  119. g.wg.Go(func() {
  120. select {
  121. case <-g.ctx.Done():
  122. case updatedStatsChan <- NewStats(durations, g.drs):
  123. }
  124. })
  125. case stats := <-updatedStatsChan:
  126. g.stats = stats
  127. currentScoutCollectedChan = scoutCollectedChan
  128. case <-scoutTicker.C:
  129. g.wg.Go(func() {
  130. g.runScoutRaid(scoutCollectedChan)
  131. })
  132. case req := <-g.connRequests:
  133. select {
  134. case <-g.ctx.Done():
  135. case req.ret <- NewConn(g.ctx, req.payload, g.stats):
  136. }
  137. }
  138. }
  139. }
  140. func (g *Ganger) updateNoiseParams() {
  141. if len(g.certSizes) == 0 {
  142. return
  143. }
  144. sum := 0
  145. for _, s := range g.certSizes {
  146. sum += s
  147. }
  148. mean := sum / len(g.certSizes)
  149. maxDev := 0
  150. for _, s := range g.certSizes {
  151. d := s - mean
  152. if d < 0 {
  153. d = -d
  154. }
  155. if d > maxDev {
  156. maxDev = d
  157. }
  158. }
  159. if maxDev < 100 {
  160. maxDev = 100
  161. }
  162. np := &NoiseParams{Mean: mean, Jitter: maxDev}
  163. g.noiseParams.Store(np)
  164. g.logger.Info(fmt.Sprintf(
  165. "updated noise params: mean=%d jitter=%d samples=%d",
  166. mean, maxDev, len(g.certSizes),
  167. ))
  168. }
  169. func (g *Ganger) runScoutRaid(rvChan chan<- scoutRaidResult) {
  170. var result scoutRaidResult
  171. for range g.scoutRaidRepeats {
  172. learned, err := g.scout.Learn(g.ctx)
  173. if err != nil {
  174. g.logger.WarningError("cannot learn", err)
  175. continue
  176. }
  177. result.durations = append(result.durations, learned.Durations...)
  178. if learned.CertSize > 0 {
  179. result.certSizes = append(result.certSizes, learned.CertSize)
  180. }
  181. }
  182. select {
  183. case <-g.ctx.Done():
  184. return
  185. case rvChan <- result:
  186. }
  187. }
  188. func NewGanger(
  189. ctx context.Context,
  190. network Network,
  191. logger Logger,
  192. scoutEach time.Duration,
  193. scoutRepeats int,
  194. urls []string,
  195. drs bool,
  196. ) *Ganger {
  197. ctx, cancel := context.WithCancel(ctx)
  198. if scoutEach == 0 {
  199. scoutEach = DoppelGangerScoutRaidEach
  200. }
  201. if scoutRepeats == 0 {
  202. scoutRepeats = DoppelGangerScoutRepeats
  203. }
  204. return &Ganger{
  205. ctx: ctx,
  206. ctxCancel: cancel,
  207. logger: logger,
  208. scoutRaidEach: scoutEach,
  209. scoutRaidRepeats: scoutRepeats,
  210. drs: drs,
  211. stats: Stats{
  212. k: StatsDefaultK,
  213. lambda: StatsDefaultLambda,
  214. drs: drs,
  215. },
  216. scout: NewScout(network, urls),
  217. connRequests: make(chan gangerConnRequest),
  218. }
  219. }