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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "math/rand"
  7. "os"
  8. "syscall"
  9. "time"
  10. "github.com/juju/errors"
  11. "go.uber.org/zap"
  12. "go.uber.org/zap/zapcore"
  13. kingpin "gopkg.in/alecthomas/kingpin.v2"
  14. "github.com/9seconds/mtg/config"
  15. "github.com/9seconds/mtg/ntp"
  16. "github.com/9seconds/mtg/proxy"
  17. "github.com/9seconds/mtg/stats"
  18. )
  19. var version = "dev" // this has to be set by build ld flags
  20. var (
  21. app = kingpin.New("mtg", "Simple MTPROTO proxy.")
  22. debug = app.Flag("debug",
  23. "Run in debug mode.").
  24. Short('d').
  25. Envar("MTG_DEBUG").
  26. Bool()
  27. verbose = app.Flag("verbose",
  28. "Run in verbose mode.").
  29. Short('v').
  30. Envar("MTG_VERBOSE").
  31. Bool()
  32. bindIP = app.Flag("bind-ip",
  33. "Which IP to bind to.").
  34. Short('b').
  35. Envar("MTG_IP").
  36. Default("127.0.0.1").
  37. IP()
  38. bindPort = app.Flag("bind-port",
  39. "Which port to bind to.").
  40. Short('p').
  41. Envar("MTG_PORT").
  42. Default("3128").
  43. Uint16()
  44. publicIPv4 = app.Flag("public-ipv4",
  45. "Which IPv4 address is public.").
  46. Short('4').
  47. Envar("MTG_IPV4").
  48. IP()
  49. publicIPv4Port = app.Flag("public-ipv4-port",
  50. "Which IPv4 port is public. Default is 'bind-port' value.").
  51. Envar("MTG_IPV4_PORT").
  52. Uint16()
  53. publicIPv6 = app.Flag("public-ipv6",
  54. "Which IPv6 address is public.").
  55. Short('6').
  56. Envar("MTG_IPV6").
  57. IP()
  58. publicIPv6Port = app.Flag("public-ipv6-port",
  59. "Which IPv6 port is public. Default is 'bind-port' value.").
  60. Envar("MTG_IPV6_PORT").
  61. Uint16()
  62. statsIP = app.Flag("stats-ip",
  63. "Which IP bind stats server to.").
  64. Short('t').
  65. Envar("MTG_STATS_IP").
  66. Default("127.0.0.1").
  67. IP()
  68. statsPort = app.Flag("stats-port",
  69. "Which port bind stats to.").
  70. Short('q').
  71. Envar("MTG_STATS_PORT").
  72. Default("3129").
  73. Uint16()
  74. statsdIP = app.Flag("statsd-ip",
  75. "Which IP should we use for working with statsd.").
  76. Envar("MTG_STATSD_IP").
  77. String()
  78. statsdPort = app.Flag("statsd-port",
  79. "Which port should we use for working with statsd.").
  80. Envar("MTG_STATSD_PORT").
  81. Default("8125").
  82. Uint16()
  83. statsdNetwork = app.Flag("statsd-network",
  84. "Which network is used to work with statsd. Only 'tcp' and 'udp' are supported.").
  85. Envar("MTG_STATSD_NETWORK").
  86. Default("udp").
  87. String()
  88. statsdPrefix = app.Flag("statsd-prefix",
  89. "Which bucket prefix should we use for sending stats to statsd.").
  90. Envar("MTG_STATSD_PREFIX").
  91. Default("mtg").
  92. String()
  93. statsdTagsFormat = app.Flag("statsd-tags-format",
  94. "Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
  95. Envar("MTG_STATSD_TAGS_FORMAT").
  96. String()
  97. statsdTags = app.Flag("statsd-tags",
  98. "Tags to use for working with statsd (specified as 'key=value').").
  99. Envar("MTG_STATSD_TAGS").
  100. StringMap()
  101. prometheusPrefix = app.Flag("prometheus-prefix",
  102. "Which namespace to use to send stats to Prometheus.").
  103. Envar("MTG_PROMETHEUS_PREFIX").
  104. Default("mtg").
  105. String()
  106. writeBufferSize = app.Flag("write-buffer",
  107. "Write buffer size in bytes. You can think about it as a buffer from client to Telegram.").
  108. Short('w').
  109. Envar("MTG_BUFFER_WRITE").
  110. Default("65536").
  111. Uint32()
  112. readBufferSize = app.Flag("read-buffer",
  113. "Read buffer size in bytes. You can think about it as a buffer from Telegram to client.").
  114. Short('r').
  115. Envar("MTG_BUFFER_READ").
  116. Default("131072").
  117. Uint32()
  118. secureOnly = app.Flag("secure-only",
  119. "Support clients with dd-secrets only.").
  120. Short('s').
  121. Envar("MTG_SECURE_ONLY").
  122. Bool()
  123. antiReplayMaxSize = app.Flag("anti-replay-max-size",
  124. "Max size of antireplay cache in megabytes.").
  125. Envar("MTG_ANTIREPLAY_MAXSIZE").
  126. Default("128").
  127. Int()
  128. antiReplayEvictionTime = app.Flag("anti-replay-eviction-time",
  129. "Eviction time period for obfuscated2 handshakes").
  130. Envar("MTG_ANTIREPLAY_EVICTIONTIME").
  131. Default("168h").
  132. Duration()
  133. secret = app.Arg("secret", "Secret of this proxy.").Required().HexBytes()
  134. adtag = app.Arg("adtag", "ADTag of the proxy.").HexBytes()
  135. )
  136. func main() { // nolint: gocyclo
  137. rand.Seed(time.Now().UTC().UnixNano())
  138. app.Version(version)
  139. app.HelpFlag.Short('h')
  140. kingpin.MustParse(app.Parse(os.Args[1:]))
  141. err := setRLimit()
  142. if err != nil {
  143. usage(err.Error())
  144. }
  145. conf, err := config.NewConfig(*debug, *verbose,
  146. *writeBufferSize, *readBufferSize,
  147. *bindIP, *publicIPv4, *publicIPv6, *statsIP,
  148. *bindPort, *publicIPv4Port, *publicIPv6Port, *statsPort, *statsdPort,
  149. *statsdIP, *statsdNetwork, *statsdPrefix, *statsdTagsFormat,
  150. *statsdTags, *prometheusPrefix, *secureOnly,
  151. *antiReplayMaxSize, *antiReplayEvictionTime,
  152. *secret, *adtag,
  153. )
  154. if err != nil {
  155. usage(err.Error())
  156. }
  157. atom := zap.NewAtomicLevel()
  158. switch {
  159. case conf.Debug:
  160. atom.SetLevel(zapcore.DebugLevel)
  161. case conf.Verbose:
  162. atom.SetLevel(zapcore.InfoLevel)
  163. default:
  164. atom.SetLevel(zapcore.ErrorLevel)
  165. }
  166. encoderCfg := zap.NewProductionEncoderConfig()
  167. logger := zap.New(zapcore.NewCore(
  168. zapcore.NewJSONEncoder(encoderCfg),
  169. zapcore.Lock(os.Stderr),
  170. atom,
  171. ))
  172. zap.ReplaceGlobals(logger)
  173. defer logger.Sync() // nolint: errcheck
  174. printURLs(conf.GetURLs())
  175. zap.S().Debugw("Configuration", "config", conf)
  176. if conf.UseMiddleProxy() {
  177. zap.S().Infow("Use middle proxy connection to Telegram")
  178. if diff, err := ntp.Fetch(); err != nil {
  179. zap.S().Warnw("Could not fetch time data from NTP")
  180. } else {
  181. if diff >= time.Second {
  182. usage(fmt.Sprintf("You choose to use middle proxy but your clock drift (%s) "+
  183. "is bigger than 1 second. Please, sync your time", diff))
  184. }
  185. go ntp.AutoUpdate()
  186. }
  187. } else {
  188. zap.S().Infow("Use direct connection to Telegram")
  189. }
  190. if err := stats.Init(conf); err != nil {
  191. panic(err)
  192. }
  193. server, err := proxy.NewProxy(conf)
  194. if err != nil {
  195. panic(err)
  196. }
  197. if err := server.Serve(); err != nil {
  198. zap.S().Fatalw("Server stopped", "error", err)
  199. }
  200. }
  201. func setRLimit() (err error) {
  202. rLimit := syscall.Rlimit{}
  203. err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
  204. if err != nil {
  205. err = errors.Annotate(err, "Cannot get rlimit")
  206. return
  207. }
  208. rLimit.Cur = rLimit.Max
  209. err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
  210. if err != nil {
  211. err = errors.Annotate(err, "Cannot set rlimit")
  212. }
  213. return
  214. }
  215. func printURLs(data interface{}) {
  216. encoder := json.NewEncoder(os.Stdout)
  217. encoder.SetEscapeHTML(false)
  218. encoder.SetIndent("", " ")
  219. err := encoder.Encode(data)
  220. if err != nil {
  221. panic(err)
  222. }
  223. }
  224. func usage(msg string) {
  225. io.WriteString(os.Stderr, msg+"\n") // nolint: errcheck, gosec
  226. os.Exit(1)
  227. }