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.

middle_caller.go 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package telegram
  2. import (
  3. "bufio"
  4. "context"
  5. "io/ioutil"
  6. "net"
  7. "net/http"
  8. "regexp"
  9. "strconv"
  10. "strings"
  11. "sync"
  12. "time"
  13. "github.com/juju/errors"
  14. "go.uber.org/zap"
  15. "github.com/9seconds/mtg/mtproto"
  16. "github.com/9seconds/mtg/wrappers"
  17. )
  18. const (
  19. middleTelegramAutoUpdateInterval = 6 * time.Hour
  20. middleTelegramHTTPClientTimeout = 30 * time.Second
  21. tgAddrProxySecret = "https://core.telegram.org/getProxySecret" // nolint: gas
  22. tgAddrProxyV4 = "https://core.telegram.org/getProxyConfig" // nolint: gas
  23. tgAddrProxyV6 = "https://core.telegram.org/getProxyConfigV6" // nolint: gas
  24. tgUserAgent = "mtg"
  25. )
  26. var middleTelegramProxyConfigSplitter = regexp.MustCompile(`\s+`)
  27. type middleTelegramCaller struct {
  28. baseTelegram
  29. proxySecret []byte
  30. dialerMutex *sync.RWMutex
  31. httpClient *http.Client
  32. }
  33. func (t *middleTelegramCaller) Dial(ctx context.Context, cancel context.CancelFunc, connID string,
  34. connOpts *mtproto.ConnectionOpts) (wrappers.StreamReadWriteCloser, error) {
  35. dc := connOpts.DC
  36. if dc == 0 {
  37. dc = 1
  38. }
  39. t.dialerMutex.RLock()
  40. defer t.dialerMutex.RUnlock()
  41. return t.baseTelegram.dial(ctx, cancel, dc, connID, connOpts.ConnectionProto)
  42. }
  43. func (t *middleTelegramCaller) autoUpdate() {
  44. for range time.Tick(middleTelegramAutoUpdateInterval) {
  45. if err := t.update(); err != nil {
  46. zap.S().Warnw("Cannot update from Telegram", "error", err)
  47. }
  48. }
  49. }
  50. func (t *middleTelegramCaller) update() error {
  51. secret, err := t.getTelegramProxySecret()
  52. if err != nil {
  53. return errors.Annotate(err, "Cannot get proxy secret")
  54. }
  55. v4Addresses, err := t.getTelegramAddresses(tgAddrProxyV4)
  56. if err != nil {
  57. return errors.Annotate(err, "Cannot get ipv4 addresses")
  58. }
  59. v6Addresses, err := t.getTelegramAddresses(tgAddrProxyV6)
  60. if err != nil {
  61. return errors.Annotate(err, "Cannot get ipv6 addresses")
  62. }
  63. t.dialerMutex.Lock()
  64. t.proxySecret = secret
  65. t.v4Addresses = v4Addresses
  66. t.v6Addresses = v6Addresses
  67. t.dialerMutex.Unlock()
  68. zap.S().Infow("Telegram middle proxy data has been updated")
  69. return nil
  70. }
  71. func (t *middleTelegramCaller) getTelegramProxySecret() ([]byte, error) {
  72. resp, err := t.call(tgAddrProxySecret)
  73. if err != nil {
  74. return nil, errors.Annotate(err, "Cannot access telegram server")
  75. }
  76. defer resp.Body.Close() // nolint: errcheck
  77. secret, err := ioutil.ReadAll(resp.Body)
  78. if err != nil {
  79. return nil, errors.Annotate(err, "Cannot read response")
  80. }
  81. return secret, nil
  82. }
  83. func (t *middleTelegramCaller) getTelegramAddresses(url string) (map[int16][]string, error) {
  84. resp, err := t.call(url)
  85. if err != nil {
  86. return nil, errors.Annotate(err, "Cannot access telegram server")
  87. }
  88. defer resp.Body.Close() // nolint: errcheck
  89. scanner := bufio.NewScanner(resp.Body)
  90. data := map[int16][]string{}
  91. for scanner.Scan() {
  92. text := strings.TrimSpace(scanner.Text())
  93. if strings.HasPrefix(text, "#") {
  94. continue
  95. }
  96. chunks := middleTelegramProxyConfigSplitter.Split(text, 3)
  97. if len(chunks) != 3 || chunks[0] != "proxy_for" {
  98. return nil, errors.Errorf("Incorrect config '%s'", text)
  99. }
  100. dcIdx64, err2 := strconv.ParseInt(chunks[1], 10, 16)
  101. if err2 != nil {
  102. return nil, errors.Errorf("Incorrect config '%s'", text)
  103. }
  104. dcIdx := int16(dcIdx64)
  105. addr := strings.TrimRight(chunks[2], ";")
  106. if _, _, err2 = net.SplitHostPort(addr); err != nil {
  107. return nil, errors.Annotatef(err2, "Incorrect config '%s'", text)
  108. }
  109. if addresses, ok := data[dcIdx]; ok {
  110. data[dcIdx] = append(addresses, addr)
  111. } else {
  112. data[dcIdx] = []string{addr}
  113. }
  114. }
  115. err = scanner.Err()
  116. if err != nil {
  117. return nil, errors.Annotate(err, "Cannot read response from the telegram")
  118. }
  119. return data, nil
  120. }
  121. func (t *middleTelegramCaller) call(url string) (*http.Response, error) {
  122. req, _ := http.NewRequest("GET", url, nil)
  123. req.Header.Set("Accept", "text/plain")
  124. req.Header.Set("User-Agent", tgUserAgent)
  125. return t.httpClient.Do(req)
  126. }