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 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package doppel
  2. import (
  3. "math"
  4. "math/rand/v2"
  5. "time"
  6. )
  7. const (
  8. StatsBisectTimes = 70
  9. StatsLowK = 0.01
  10. StatsHighK = 10.0
  11. // do not calculate statistics if we have < than this number of durations
  12. MinDurationsToCalculate = 100
  13. // these values are taken from ok.ru. measured from moscow site.
  14. StatsDefaultK = 0.37846373895785335
  15. StatsDefaultLambda = 1.73177086015485
  16. // how many bytes should we drift
  17. DRSNoise = 100
  18. )
  19. // Stats is responsible for generating values that are distributed according
  20. // to some statistical distribution.
  21. //
  22. // It follows several ideas:
  23. // 1. Based on nginx and Cloudflare behaviour, even if server is eager
  24. // to send a lot, they all start with small TLS packets that are
  25. // approximately MTU-sized. After
  26. // 2. After ~40 TLS records, server considers TCP session as somewhat solid
  27. // and reliable and ramps up to 4096.
  28. // 3. After ~20 TLS records more it jumps to the max 16384 bytes and keep
  29. // this size as long as it can
  30. // 4. If there is no any byte within a connection for a longer time period,
  31. // this counter resets.
  32. //
  33. // This is called Dynamic TLS Record Sizing
  34. // - https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency/
  35. // - https://community.f5.com/kb/technicalarticles/boosting-tls-performance-with-dynamic-record-sizing-on-big-ip/280798
  36. // - https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
  37. //
  38. // And this optimized for the very first byte, so web browsers could start to
  39. // render as early as possible, showing user some preliminary results, optimizing
  40. // for perceived latency.
  41. //
  42. // Since this is very typical for the website, we also aim for that.
  43. //
  44. // Another important idea is how delays between TLS packets are distributed.
  45. // In case of sending huge heavy content with max sized record, delays have
  46. // lognormal distribution. But a nature of a typical website shows that
  47. // it eagers to deliver as fast as it can in a few very first records and
  48. // could possibly slow down later.
  49. //
  50. // This is perfectly described by Weibull distribution:
  51. // - https://en.wikipedia.org/wiki/Weibull_distribution
  52. // - https://ieeexplore.ieee.org/document/6662948
  53. // - https://www.researchgate.net/publication/224621285_Traffic_modelling_and_cost_optimization_for_transmitting_traffic_messages_over_a_hybrid_broadcast_and_cellular_network
  54. // - https://ir.uitm.edu.my/id/eprint/105386/1/105386.pdf
  55. //
  56. // In other word, a combination of Dynamic TLS Record Sizing hints us for
  57. // Weibull distribution.
  58. //
  59. // But we also have to keep in mind that DRS is not well spread yet. In most cases
  60. // users still rely on OpenSSL or webserver defaults. OpenSSL chunks with
  61. // biggest packet sizes, nginx relies on static setting that is 16k by default.
  62. // Thus, dynamic sizing has to be present but we cannot oblige users to use that.
  63. type Stats struct {
  64. sizeLastRequested time.Time
  65. sizeCounter int
  66. // https://en.wikipedia.org/wiki/Shape_parameter
  67. k float64
  68. // https://en.wikipedia.org/wiki/Scale_parameter
  69. lambda float64
  70. // Dynamic Record Sizing
  71. drs bool
  72. }
  73. func (d *Stats) Delay() time.Duration {
  74. // u ∈ (0, 1], avoids ln(0)
  75. u := 1.0 - rand.Float64()
  76. // X = λ·(-ln U)^(1/k)
  77. generated := d.lambda * math.Pow(-math.Log(u), 1.0/d.k)
  78. // generated is in milliseconds
  79. return time.Duration(generated * float64(time.Millisecond))
  80. }
  81. func (d *Stats) Size() int {
  82. if time.Since(d.sizeLastRequested) > TLSRecordSizeResetAfter {
  83. d.sizeCounter = 0
  84. }
  85. if !d.drs {
  86. return TLSRecordSizeMax
  87. }
  88. d.sizeLastRequested = time.Now()
  89. d.sizeCounter++
  90. switch {
  91. case d.sizeCounter <= TLSCounterAccelAfter:
  92. return TLSRecordSizeStart - rand.IntN(DRSNoise)
  93. case d.sizeCounter <= TLSCounterMaxAfter:
  94. return TLSRecordSizeAccel - rand.IntN(DRSNoise)
  95. }
  96. return TLSRecordSizeMax
  97. }
  98. func NewStats(durations []time.Duration, drs bool) *Stats {
  99. n := float64(len(durations))
  100. // in milliseconds
  101. durFloats := make([]float64, len(durations))
  102. for i, v := range durations {
  103. durFloats[i] = float64(v.Microseconds()) / 1000.0
  104. }
  105. // The bisection solves the standard Weibull MLE equation for shape
  106. // parameter k. There is no any good formula for doing that so we
  107. // approximate it by several bisections. The number of operations
  108. // is statically defined by a constant.
  109. sumLog := 0.0
  110. for _, v := range durFloats {
  111. sumLog += math.Log(v)
  112. }
  113. lowK := StatsLowK
  114. highK := StatsHighK
  115. for range StatsBisectTimes {
  116. midK := (lowK + highK) / 2.0
  117. sumXK := 0.0
  118. sumXKLog := 0.0
  119. for _, v := range durFloats {
  120. xk := math.Pow(v, midK)
  121. sumXK += xk
  122. sumXKLog += xk * math.Log(v)
  123. }
  124. if (1.0/midK)+(sumLog/n)-(sumXKLog/sumXK) > 0 {
  125. lowK = midK
  126. } else {
  127. highK = midK
  128. }
  129. }
  130. k := (lowK + highK) / 2
  131. sumXK := 0.0
  132. for _, v := range durFloats {
  133. sumXK += math.Pow(v, k)
  134. }
  135. // λ = (Σxᵢᵏ / n)^(1/k)
  136. lambda := math.Pow(sumXK/n, 1.0/k)
  137. return &Stats{
  138. k: k,
  139. lambda: lambda,
  140. drs: drs,
  141. }
  142. }