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
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package doppel
  2. import (
  3. "bytes"
  4. "context"
  5. "sync"
  6. "github.com/9seconds/mtg/v2/essentials"
  7. "github.com/9seconds/mtg/v2/mtglib/internal/tls"
  8. )
  9. type Conn struct {
  10. essentials.Conn
  11. p *connPayload
  12. }
  13. type connPayload struct {
  14. ctx context.Context
  15. ctxCancel context.CancelCauseFunc
  16. clock Clock
  17. wg sync.WaitGroup
  18. syncWriteLock sync.RWMutex
  19. writeStream bytes.Buffer
  20. writeCond *sync.Cond
  21. }
  22. func (c Conn) Write(p []byte) (int, error) {
  23. c.p.syncWriteLock.RLock()
  24. defer c.p.syncWriteLock.RUnlock()
  25. c.p.writeCond.L.Lock()
  26. c.p.writeStream.Write(p)
  27. c.p.writeCond.L.Unlock()
  28. return len(p), context.Cause(c.p.ctx)
  29. }
  30. func (c Conn) SyncWrite(p []byte) (int, error) {
  31. c.p.syncWriteLock.Lock()
  32. defer c.p.syncWriteLock.Unlock()
  33. c.p.writeCond.L.Lock()
  34. // wait until buffer is exhausted
  35. for c.p.writeStream.Len() != 0 && context.Cause(c.p.ctx) == nil {
  36. c.p.writeCond.Wait()
  37. }
  38. c.p.writeStream.Write(p)
  39. c.p.writeCond.L.Unlock()
  40. if err := context.Cause(c.p.ctx); err != nil {
  41. return len(p), err
  42. }
  43. c.p.writeCond.L.Lock()
  44. // wait until data will be sent
  45. for c.p.writeStream.Len() != 0 && context.Cause(c.p.ctx) == nil {
  46. c.p.writeCond.Wait()
  47. }
  48. c.p.writeCond.L.Unlock()
  49. return len(p), context.Cause(c.p.ctx)
  50. }
  51. func (c Conn) Start() {
  52. c.p.wg.Go(func() {
  53. c.start()
  54. })
  55. }
  56. func (c Conn) start() {
  57. defer c.p.writeCond.Broadcast()
  58. buf := [tls.MaxRecordSize]byte{}
  59. for {
  60. select {
  61. case <-c.p.ctx.Done():
  62. return
  63. case <-c.p.clock.tick:
  64. }
  65. c.p.writeCond.L.Lock()
  66. n, err := c.p.writeStream.Read(buf[:c.p.clock.stats.Size()])
  67. c.p.writeCond.L.Unlock()
  68. if n == 0 || err != nil {
  69. continue
  70. }
  71. if err := tls.WriteRecord(c.Conn, buf[:n]); err != nil {
  72. c.p.ctxCancel(err)
  73. return
  74. }
  75. c.p.writeCond.Signal()
  76. }
  77. }
  78. func (c Conn) Stop() {
  79. c.p.ctxCancel(nil)
  80. c.p.wg.Wait()
  81. }
  82. func NewConn(ctx context.Context, conn essentials.Conn, stats *Stats) Conn {
  83. ctx, cancel := context.WithCancelCause(ctx)
  84. rv := Conn{
  85. Conn: conn,
  86. p: &connPayload{
  87. ctx: ctx,
  88. ctxCancel: cancel,
  89. writeCond: sync.NewCond(&sync.Mutex{}),
  90. clock: Clock{
  91. stats: stats,
  92. tick: make(chan struct{}),
  93. },
  94. },
  95. }
  96. rv.p.writeStream.Grow(tls.DefaultBufferSize)
  97. rv.p.wg.Go(func() {
  98. rv.p.clock.Start(ctx)
  99. })
  100. rv.p.wg.Go(func() {
  101. rv.start()
  102. })
  103. return rv
  104. }