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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package main
  2. import (
  3. "math/rand"
  4. "os"
  5. "runtime/debug"
  6. "strings"
  7. "time"
  8. kingpin "gopkg.in/alecthomas/kingpin.v2"
  9. "github.com/9seconds/mtg/cli"
  10. "github.com/9seconds/mtg/config"
  11. "github.com/9seconds/mtg/utils"
  12. )
  13. var version = "dev" // has to be set by ldflags
  14. var (
  15. app = kingpin.New("mtg", "Simple MTPROTO proxy.")
  16. generateSecretCommand = app.Command("generate-secret",
  17. "Generate new secret")
  18. generateCloakHost = generateSecretCommand.Flag("cloak-host",
  19. "A host to use for TLS cloaking.").
  20. Short('c').
  21. Default("storage.googleapis.com").
  22. String()
  23. generateSecretType = generateSecretCommand.Arg("type",
  24. "A type of secret to generate. Valid options are 'simple', 'secured' and 'tls'").
  25. Required().
  26. Enum("simple", "secured", "tls")
  27. runCommand = app.Command("run",
  28. "Run new proxy instance")
  29. runDebug = runCommand.Flag("debug",
  30. "Run in debug mode.").
  31. Short('d').
  32. Envar("MTG_DEBUG").
  33. Bool()
  34. runVerbose = runCommand.Flag("verbose",
  35. "Run in verbose mode.").
  36. Short('v').
  37. Envar("MTG_VERBOSE").
  38. Bool()
  39. runPreferIP = runCommand.Flag("prefer-ip",
  40. "Prefer this IP protocol if possible. Valid options are 'ipv4' and 'ipv6'").
  41. Envar("MTG_PREFER_DIRECT_IP").
  42. Default("ipv6").
  43. Enum("ipv4", "ipv6")
  44. runBind = runCommand.Flag("bind",
  45. "Host:Port to bind proxy to.").
  46. Short('b').
  47. Envar("MTG_BIND").
  48. Default("0.0.0.0:3128").
  49. TCP()
  50. runPublicIPv4 = runCommand.Flag("public-ipv4",
  51. "Which IPv4 host:port to use.").
  52. Short('4').
  53. Envar("MTG_IPV4").
  54. TCP()
  55. runPublicIPv6 = runCommand.Flag("public-ipv6",
  56. "Which IPv6 host:port to use.").
  57. Short('6').
  58. Envar("MTG_IPV6").
  59. TCP()
  60. runStatsBind = runCommand.Flag("stats-bind",
  61. "Which Host:Port to bind stats server to.").
  62. Short('t').
  63. Envar("MTG_STATS_BIND").
  64. Default("127.0.0.1:3129").
  65. TCP()
  66. runStatsNamespace = runCommand.Flag("stats-namespace",
  67. "Which namespace to use for Prometheus.").
  68. Envar("MTG_STATS_NAMESPACE").
  69. Default("mtg").
  70. String()
  71. runStatsdAddress = runCommand.Flag("statsd-addr",
  72. "Host:port of statsd server").
  73. Envar("MTG_STATSD_ADDR").
  74. TCP()
  75. runStatsdTagsFormat = runCommand.Flag("statsd-tags-format",
  76. "Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
  77. Envar("MTG_STATSD_TAGS_FORMAT").
  78. Default("influxdb").
  79. Enum("datadog", "influxdb")
  80. runStatsdTags = runCommand.Flag("statsd-tags",
  81. "Tags to use for working with statsd (specified as 'key=value').").
  82. Envar("MTG_STATSD_TAGS").
  83. StringMap()
  84. runWriteBufferSize = runCommand.Flag("write-buffer",
  85. "Write buffer size. You can think about it as a buffer from client to Telegram.").
  86. Short('w').
  87. Envar("MTG_BUFFER_WRITE").
  88. Default("32KB").
  89. Bytes()
  90. runReadBufferSize = runCommand.Flag("read-buffer",
  91. "Read buffer size. You can think about it as a buffer from Telegram to client.").
  92. Short('r').
  93. Envar("MTG_BUFFER_READ").
  94. Default("32KB").
  95. Bytes()
  96. runTLSCloakPort = runCommand.Flag("cloak-port",
  97. "Port which should be used for host cloaking.").
  98. Envar("MTG_CLOAK_PORT").
  99. Default("443").
  100. Uint16()
  101. runAntiReplayMaxSize = runCommand.Flag("anti-replay-max-size",
  102. "Max size of antireplay cache.").
  103. Envar("MTG_ANTIREPLAY_MAXSIZE").
  104. Default("128MB").
  105. Bytes()
  106. runMultiplexPerConnection = runCommand.Flag("multiplex-per-connection",
  107. "How many clients can share a single connection to Telegram.").
  108. Envar("MTG_MULTIPLEX_PERCONNECTION").
  109. Default("50").
  110. Uint()
  111. runNTPServers = runCommand.Flag("ntp-server",
  112. "A list of NTP servers to use.").
  113. Envar("MTG_NTP_SERVERS").
  114. Default("0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org").
  115. Strings()
  116. runSecret = runCommand.Arg("secret", "Secret of this proxy.").Required().HexBytes()
  117. runAdtag = runCommand.Arg("adtag", "ADTag of the proxy.").HexBytes()
  118. )
  119. func main() {
  120. rand.Seed(time.Now().UTC().UnixNano())
  121. app.Version(getVersion())
  122. app.HelpFlag.Short('h')
  123. if err := utils.SetLimits(); err != nil {
  124. cli.Fatal(err)
  125. }
  126. switch kingpin.MustParse(app.Parse(os.Args[1:])) {
  127. case generateSecretCommand.FullCommand():
  128. cli.Generate(*generateSecretType, *generateCloakHost)
  129. case runCommand.FullCommand():
  130. err := config.Init(
  131. config.Opt{Option: config.OptionTypeDebug, Value: *runDebug},
  132. config.Opt{Option: config.OptionTypeVerbose, Value: *runVerbose},
  133. config.Opt{Option: config.OptionTypePreferIP, Value: *runPreferIP},
  134. config.Opt{Option: config.OptionTypeBind, Value: *runBind},
  135. config.Opt{Option: config.OptionTypePublicIPv4, Value: *runPublicIPv4},
  136. config.Opt{Option: config.OptionTypePublicIPv6, Value: *runPublicIPv6},
  137. config.Opt{Option: config.OptionTypeStatsBind, Value: *runStatsBind},
  138. config.Opt{Option: config.OptionTypeStatsNamespace, Value: *runStatsNamespace},
  139. config.Opt{Option: config.OptionTypeStatsdAddress, Value: *runStatsdAddress},
  140. config.Opt{Option: config.OptionTypeStatsdTagsFormat, Value: *runStatsdTagsFormat},
  141. config.Opt{Option: config.OptionTypeStatsdTags, Value: *runStatsdTags},
  142. config.Opt{Option: config.OptionTypeWriteBufferSize, Value: *runWriteBufferSize},
  143. config.Opt{Option: config.OptionTypeReadBufferSize, Value: *runReadBufferSize},
  144. config.Opt{Option: config.OptionTypeCloakPort, Value: *runTLSCloakPort},
  145. config.Opt{Option: config.OptionTypeAntiReplayMaxSize, Value: *runAntiReplayMaxSize},
  146. config.Opt{Option: config.OptionTypeMultiplexPerConnection, Value: *runMultiplexPerConnection},
  147. config.Opt{Option: config.OptionTypeNTPServers, Value: *runNTPServers},
  148. config.Opt{Option: config.OptionTypeSecret, Value: *runSecret},
  149. config.Opt{Option: config.OptionTypeAdtag, Value: *runAdtag},
  150. )
  151. if err != nil {
  152. cli.Fatal(err)
  153. }
  154. if err := cli.Proxy(); err != nil {
  155. cli.Fatal(err)
  156. }
  157. }
  158. }
  159. func getVersion() string {
  160. if version != "dev" {
  161. return version
  162. }
  163. info, ok := debug.ReadBuildInfo()
  164. if !ok {
  165. return version
  166. }
  167. builder := strings.Builder{}
  168. builder.WriteString(info.Main.Version)
  169. if info.Main.Sum != "" {
  170. builder.WriteString(" (checksum: ")
  171. builder.WriteString(info.Main.Sum)
  172. builder.WriteRune(')')
  173. }
  174. return builder.String()
  175. }