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.

main.go 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package main
  2. //go:generate scripts/generate_version.sh
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "math/rand"
  8. "os"
  9. "syscall"
  10. "time"
  11. "github.com/juju/errors"
  12. "go.uber.org/zap"
  13. "go.uber.org/zap/zapcore"
  14. kingpin "gopkg.in/alecthomas/kingpin.v2"
  15. "github.com/9seconds/mtg/config"
  16. "github.com/9seconds/mtg/ntp"
  17. "github.com/9seconds/mtg/proxy"
  18. "github.com/9seconds/mtg/stats"
  19. )
  20. var (
  21. app = kingpin.New("mtg", "Simple MTPROTO proxy.")
  22. debug = app.Flag("debug", "Run in debug mode.").
  23. Short('d').
  24. Envar("MTG_DEBUG").
  25. Bool()
  26. verbose = app.Flag("verbose", "Run in verbose mode.").
  27. Short('v').
  28. Envar("MTG_VERBOSE").
  29. Bool()
  30. bindIP = app.Flag("bind-ip", "Which IP to bind to.").
  31. Short('b').
  32. Envar("MTG_IP").
  33. Default("127.0.0.1").
  34. IP()
  35. bindPort = app.Flag("bind-port", "Which port to bind to.").
  36. Short('p').
  37. Envar("MTG_PORT").
  38. Default("3128").
  39. Uint16()
  40. publicIPv4 = app.Flag("public-ipv4", "Which IPv4 address is public.").
  41. Short('4').
  42. Envar("MTG_IPV4").
  43. IP()
  44. publicIPv4Port = app.Flag("public-ipv4-port", "Which IPv4 port is public. Default is 'bind-port' value.").
  45. Envar("MTG_IPV4_PORT").
  46. Uint16()
  47. publicIPv6 = app.Flag("public-ipv6", "Which IPv6 address is public.").
  48. Short('6').
  49. Envar("MTG_IPV6").
  50. IP()
  51. publicIPv6Port = app.Flag("public-ipv6-port", "Which IPv6 port is public. Default is 'bind-port' value.").
  52. Envar("MTG_IPV6_PORT").
  53. Uint16()
  54. statsIP = app.Flag("stats-ip", "Which IP bind stats server to.").
  55. Short('t').
  56. Envar("MTG_STATS_IP").
  57. Default("127.0.0.1").
  58. IP()
  59. statsPort = app.Flag("stats-port", "Which port bind stats to.").
  60. Short('q').
  61. Envar("MTG_STATS_PORT").
  62. Default("3129").
  63. Uint16()
  64. statsdIP = app.Flag("statsd-ip", "Which IP should we use for working with statsd.").
  65. Envar("MTG_STATSD_IP").
  66. String()
  67. statsdPort = app.Flag("statsd-port", "Which port should we use for working with statsd.").
  68. Envar("MTG_STATSD_PORT").
  69. Default("8125").
  70. Uint16()
  71. statsdNetwork = app.Flag("statsd-network", "Which network is used to work with statsd. Only 'tcp' and 'udp' are supported.").
  72. Envar("MTG_STATSD_NETWORK").
  73. Default("udp").
  74. String()
  75. statsdPrefix = app.Flag("statsd-prefix", "Which bucket prefix should we use for sending stats to statsd.").
  76. Envar("MTG_STATSD_PREFIX").
  77. Default("mtg").
  78. String()
  79. statsdTagsFormat = app.Flag("statsd-tags-format", "Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
  80. Envar("MTG_STATSD_TAGS_FORMAT").
  81. String()
  82. statsdTags = app.Flag("statsd-tags", "Tags to use for working with statsd (specified as 'key=value').").
  83. Envar("MTG_STATSD_TAGS").
  84. StringMap()
  85. secret = app.Arg("secret", "Secret of this proxy.").Required().String()
  86. adtag = app.Arg("adtag", "ADTag of the proxy.").String()
  87. )
  88. func init() {
  89. rand.Seed(time.Now().UTC().UnixNano())
  90. app.Version(version)
  91. }
  92. func main() {
  93. kingpin.MustParse(app.Parse(os.Args[1:]))
  94. err := setRLimit()
  95. if err != nil {
  96. usage(err.Error())
  97. }
  98. conf, err := config.NewConfig(*debug, *verbose,
  99. *bindIP, *bindPort,
  100. *publicIPv4, *publicIPv4Port,
  101. *publicIPv6, *publicIPv6Port,
  102. *statsIP, *statsPort,
  103. *secret, *adtag,
  104. *statsdIP, *statsdPort, *statsdNetwork, *statsdPrefix,
  105. *statsdTagsFormat, *statsdTags,
  106. )
  107. if err != nil {
  108. usage(err.Error())
  109. }
  110. atom := zap.NewAtomicLevel()
  111. if conf.Debug {
  112. atom.SetLevel(zapcore.DebugLevel)
  113. } else if conf.Verbose {
  114. atom.SetLevel(zapcore.InfoLevel)
  115. } else {
  116. atom.SetLevel(zapcore.ErrorLevel)
  117. }
  118. encoderCfg := zap.NewProductionEncoderConfig()
  119. logger := zap.New(zapcore.NewCore(
  120. zapcore.NewJSONEncoder(encoderCfg),
  121. zapcore.Lock(os.Stderr),
  122. atom,
  123. ))
  124. zap.ReplaceGlobals(logger)
  125. defer logger.Sync() // nolint: errcheck
  126. printURLs(conf.GetURLs())
  127. if conf.UseMiddleProxy() {
  128. zap.S().Infow("Use middle proxy connection to Telegram")
  129. if diff, err := ntp.Fetch(); err != nil {
  130. zap.S().Warnw("Could not fetch time data from NTP")
  131. } else {
  132. if diff >= time.Second {
  133. usage(fmt.Sprintf("You choose to use middle proxy but your clock drift (%s) is bigger than 1 second. Please, sync your time", diff))
  134. }
  135. go ntp.AutoUpdate()
  136. }
  137. } else {
  138. zap.S().Infow("Use direct connection to Telegram")
  139. }
  140. if err := stats.Start(conf); err != nil {
  141. panic(err)
  142. }
  143. server := proxy.NewProxy(conf)
  144. if err := server.Serve(); err != nil {
  145. zap.S().Fatalw("Server stopped", "error", err)
  146. }
  147. }
  148. func setRLimit() (err error) {
  149. rLimit := syscall.Rlimit{}
  150. err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
  151. if err != nil {
  152. err = errors.Annotate(err, "Cannot get rlimit")
  153. return
  154. }
  155. rLimit.Cur = rLimit.Max
  156. err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
  157. if err != nil {
  158. err = errors.Annotate(err, "Cannot set rlimit")
  159. }
  160. return
  161. }
  162. func printURLs(data interface{}) {
  163. encoder := json.NewEncoder(os.Stdout)
  164. encoder.SetEscapeHTML(false)
  165. encoder.SetIndent("", " ")
  166. err := encoder.Encode(data)
  167. if err != nil {
  168. panic(err)
  169. }
  170. }
  171. func usage(msg string) {
  172. io.WriteString(os.Stderr, msg+"\n") // nolint: errcheck
  173. os.Exit(1)
  174. }