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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package mtglib
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "io"
  7. "net"
  8. "sync/atomic"
  9. "time"
  10. "github.com/9seconds/mtg/v2/essentials"
  11. "github.com/pires/go-proxyproto"
  12. )
  13. type connTraffic struct {
  14. essentials.Conn
  15. streamID string
  16. stream EventStream
  17. ctx context.Context
  18. }
  19. func (c connTraffic) Read(b []byte) (int, error) {
  20. n, err := c.Conn.Read(b)
  21. if n > 0 {
  22. c.stream.Send(c.ctx, NewEventTraffic(c.streamID, uint(n), true))
  23. }
  24. return n, err //nolint: wrapcheck
  25. }
  26. func (c connTraffic) Write(b []byte) (int, error) {
  27. n, err := c.Conn.Write(b)
  28. if n > 0 {
  29. c.stream.Send(c.ctx, NewEventTraffic(c.streamID, uint(n), false))
  30. }
  31. return n, err //nolint: wrapcheck
  32. }
  33. type connRewind struct {
  34. essentials.Conn
  35. buf bytes.Buffer
  36. active io.Reader
  37. }
  38. func (c *connRewind) Read(p []byte) (int, error) {
  39. return c.active.Read(p)
  40. }
  41. func (c *connRewind) Rewind() {
  42. c.active = io.MultiReader(&c.buf, c.Conn)
  43. }
  44. func newConnRewind(conn essentials.Conn) *connRewind {
  45. rv := &connRewind{
  46. Conn: conn,
  47. }
  48. rv.active = io.TeeReader(conn, &rv.buf)
  49. return rv
  50. }
  51. type connProxyProtocol struct {
  52. essentials.Conn
  53. sourceAddr net.Addr
  54. headersWritten bool
  55. }
  56. func (c *connProxyProtocol) Write(p []byte) (int, error) {
  57. if !c.headersWritten {
  58. headers := proxyproto.HeaderProxyFromAddrs(2, c.sourceAddr, c.RemoteAddr())
  59. toSend, err := headers.Format()
  60. if err != nil {
  61. panic(err)
  62. }
  63. if _, err := c.Conn.Write(toSend); err != nil {
  64. return 0, fmt.Errorf("cannot send proxy protocol header: %w", err)
  65. }
  66. c.headersWritten = true
  67. }
  68. return c.Conn.Write(p)
  69. }
  70. func newConnProxyProtocol(source, target essentials.Conn) *connProxyProtocol {
  71. return &connProxyProtocol{
  72. Conn: target,
  73. sourceAddr: source.RemoteAddr(),
  74. }
  75. }
  76. // idleTracker is a shared idle tracker for a pair of relay connections.
  77. // Both directions update the same timestamp so that activity in one direction
  78. // prevents the other (idle) direction from timing out.
  79. type idleTracker struct {
  80. lastActive atomic.Int64 // unix nanos
  81. timeout time.Duration
  82. }
  83. func newIdleTracker(timeout time.Duration) *idleTracker {
  84. t := &idleTracker{timeout: timeout}
  85. t.touch()
  86. return t
  87. }
  88. func (t *idleTracker) touch() {
  89. t.lastActive.Store(time.Now().UnixNano())
  90. }
  91. func (t *idleTracker) isIdle() bool {
  92. last := time.Unix(0, t.lastActive.Load())
  93. return time.Since(last) >= t.timeout
  94. }
  95. type connIdleTimeout struct {
  96. essentials.Conn
  97. tracker *idleTracker
  98. }
  99. func (c connIdleTimeout) Read(b []byte) (int, error) {
  100. for {
  101. c.SetReadDeadline(time.Now().Add(c.tracker.timeout)) //nolint: errcheck
  102. n, err := c.Conn.Read(b)
  103. if n > 0 {
  104. c.tracker.touch()
  105. return n, err //nolint: wrapcheck
  106. }
  107. if err != nil {
  108. if netErr, ok := err.(net.Error); ok && netErr.Timeout() && !c.tracker.isIdle() { //nolint: errorlint
  109. continue
  110. }
  111. return 0, err //nolint: wrapcheck
  112. }
  113. return 0, nil
  114. }
  115. }
  116. func (c connIdleTimeout) Write(b []byte) (int, error) {
  117. c.SetWriteDeadline(time.Now().Add(c.tracker.timeout)) //nolint: errcheck
  118. n, err := c.Conn.Write(b)
  119. if n > 0 {
  120. c.tracker.touch()
  121. }
  122. return n, err //nolint: wrapcheck
  123. }