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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net"
  8. "net/url"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/9seconds/mtg/v2/mtglib"
  14. "github.com/alecthomas/units"
  15. "github.com/pelletier/go-toml"
  16. )
  17. type configTypeHostPort struct {
  18. host configTypeIP
  19. port configTypePort
  20. }
  21. func (c *configTypeHostPort) UnmarshalText(data []byte) error {
  22. if len(data) == 0 {
  23. return nil
  24. }
  25. host, port, err := net.SplitHostPort(string(data))
  26. if err != nil {
  27. return fmt.Errorf("incorrect host:port syntax: %w", err)
  28. }
  29. if err := c.port.UnmarshalJSON([]byte(port)); err != nil {
  30. return fmt.Errorf("incorrect port in host:port: %w", err)
  31. }
  32. if err := c.host.UnmarshalText([]byte(host)); err != nil {
  33. return fmt.Errorf("incorrect host: %w", err)
  34. }
  35. return nil
  36. }
  37. func (c configTypeHostPort) MarshalText() ([]byte, error) {
  38. return []byte(c.String()), nil
  39. }
  40. func (c configTypeHostPort) String() string {
  41. return c.Value(net.IP{}, 0)
  42. }
  43. func (c configTypeHostPort) Value(defaultHostValue net.IP, defaultPortValue uint) string {
  44. return net.JoinHostPort(c.host.Value(defaultHostValue).String(),
  45. strconv.Itoa(int(c.port.Value(defaultPortValue))))
  46. }
  47. type configTypePort struct {
  48. value uint
  49. }
  50. func (c *configTypePort) UnmarshalJSON(data []byte) error {
  51. if len(data) == 0 {
  52. return nil
  53. }
  54. intValue, err := strconv.ParseUint(string(data), 10, 16)
  55. if err != nil {
  56. return fmt.Errorf("port number is not a number: %w", err)
  57. }
  58. if intValue == 0 || intValue > 65536 {
  59. return fmt.Errorf("port number should be 0 < portNo < 65536: %d", intValue)
  60. }
  61. c.value = uint(intValue)
  62. return nil
  63. }
  64. func (c *configTypePort) MarshalJSON() ([]byte, error) {
  65. return json.Marshal(c.value)
  66. }
  67. func (c configTypePort) String() string {
  68. return strconv.Itoa(int(c.value))
  69. }
  70. func (c configTypePort) Value(defaultValue uint) uint {
  71. if c.value == 0 {
  72. return defaultValue
  73. }
  74. return c.value
  75. }
  76. type configTypeBytes struct {
  77. value uint
  78. }
  79. func (c *configTypeBytes) UnmarshalText(data []byte) error {
  80. if len(data) == 0 {
  81. return nil
  82. }
  83. value, err := units.ParseStrictBytes(strings.ToUpper(string(data)))
  84. if err != nil {
  85. return fmt.Errorf("incorrect bytes value: %w", err)
  86. }
  87. if value < 0 {
  88. return fmt.Errorf("%d should be positive number", value)
  89. }
  90. c.value = uint(value)
  91. return nil
  92. }
  93. func (c configTypeBytes) MarshalText() ([]byte, error) {
  94. return []byte(c.String()), nil
  95. }
  96. func (c configTypeBytes) String() string {
  97. return units.ToString(int64(c.value), 1024, "ib", "b")
  98. }
  99. func (c configTypeBytes) Value(defaultValue uint) uint {
  100. if c.value == 0 {
  101. return defaultValue
  102. }
  103. return c.value
  104. }
  105. type configTypePreferIP struct {
  106. value string
  107. }
  108. func (c *configTypePreferIP) UnmarshalText(data []byte) error {
  109. if len(data) == 0 {
  110. return nil
  111. }
  112. text := strings.ToLower(string(data))
  113. switch text {
  114. case "prefer-ipv4", "prefer-ipv6", "only-ipv4", "only-ipv6":
  115. c.value = text
  116. default:
  117. return fmt.Errorf("incorrect prefer-ip value: %s", string(data))
  118. }
  119. return nil
  120. }
  121. func (c configTypePreferIP) MarshalText() ([]byte, error) {
  122. return []byte(c.value), nil
  123. }
  124. func (c *configTypePreferIP) String() string {
  125. return c.value
  126. }
  127. func (c *configTypePreferIP) Value(defaultValue string) string {
  128. if c.value == "" {
  129. return defaultValue
  130. }
  131. return c.value
  132. }
  133. type configTypeDuration struct {
  134. value time.Duration
  135. }
  136. func (c *configTypeDuration) UnmarshalText(data []byte) error {
  137. if len(data) == 0 {
  138. return nil
  139. }
  140. dur, err := time.ParseDuration(strings.ToLower(string(data)))
  141. if err != nil {
  142. return fmt.Errorf("incorrect duration: %w", err)
  143. }
  144. if dur < 0 {
  145. return fmt.Errorf("%s should be positive duration", dur)
  146. }
  147. c.value = dur
  148. return nil
  149. }
  150. func (c configTypeDuration) MarshalText() ([]byte, error) {
  151. return []byte(c.value.String()), nil
  152. }
  153. func (c configTypeDuration) String() string {
  154. return c.value.String()
  155. }
  156. func (c configTypeDuration) Value(defaultValue time.Duration) time.Duration {
  157. if c.value == 0 {
  158. return defaultValue
  159. }
  160. return c.value
  161. }
  162. type configTypeFloat struct {
  163. value float64
  164. }
  165. func (c *configTypeFloat) UnmarshalJSON(data []byte) error {
  166. value, err := strconv.ParseFloat(string(data), 64)
  167. if err != nil {
  168. return fmt.Errorf("incorrect float value: %w", err)
  169. }
  170. if value < 0 {
  171. return fmt.Errorf("%f should be positive", value)
  172. }
  173. c.value = value
  174. return nil
  175. }
  176. func (c *configTypeFloat) MarshalText() ([]byte, error) {
  177. return []byte(c.String()), nil
  178. }
  179. func (c configTypeFloat) String() string {
  180. return strconv.FormatFloat(c.value, 'f', -1, 64)
  181. }
  182. func (c configTypeFloat) Value(defaultValue float64) float64 {
  183. if c.value < 0.00001 {
  184. return defaultValue
  185. }
  186. return c.value
  187. }
  188. type configTypeIP struct {
  189. value net.IP
  190. }
  191. func (c *configTypeIP) UnmarshalText(data []byte) error {
  192. if len(data) == 0 {
  193. return nil
  194. }
  195. ip := net.ParseIP(string(data))
  196. if ip == nil {
  197. return fmt.Errorf("incorrect ip address: %s", string(data))
  198. }
  199. c.value = ip
  200. return nil
  201. }
  202. func (c *configTypeIP) MarshalText() ([]byte, error) {
  203. return []byte(c.String()), nil
  204. }
  205. func (c configTypeIP) String() string {
  206. if c.value == nil {
  207. return ""
  208. }
  209. return c.value.String()
  210. }
  211. func (c configTypeIP) Value(defaultValue net.IP) net.IP {
  212. if c.value == nil {
  213. return defaultValue
  214. }
  215. return c.value
  216. }
  217. type configTypeURL struct {
  218. value *url.URL
  219. }
  220. func (c *configTypeURL) UnmarshalText(data []byte) error {
  221. if len(data) == 0 {
  222. return nil
  223. }
  224. value, err := url.Parse(string(data))
  225. if err != nil {
  226. return fmt.Errorf("incorrect URL: %w", err)
  227. }
  228. c.value = value
  229. return nil
  230. }
  231. func (c *configTypeURL) MarshalText() ([]byte, error) {
  232. return []byte(c.String()), nil
  233. }
  234. func (c configTypeURL) String() string {
  235. if c.value == nil {
  236. return ""
  237. }
  238. return c.value.String()
  239. }
  240. func (c configTypeURL) Value(defaultValue *url.URL) *url.URL {
  241. if c.value == nil {
  242. return defaultValue
  243. }
  244. return c.value
  245. }
  246. type configTypeMetricPrefix struct {
  247. value string
  248. }
  249. func (c *configTypeMetricPrefix) UnmarshalText(data []byte) error {
  250. if len(data) == 0 {
  251. return nil
  252. }
  253. prefix := string(data)
  254. if ok, err := regexp.MatchString("^[a-z0-9]+$", prefix); !ok || err != nil {
  255. return fmt.Errorf("incorrect metric prefix: %s", prefix)
  256. }
  257. c.value = prefix
  258. return nil
  259. }
  260. func (c configTypeMetricPrefix) MarshalText() ([]byte, error) {
  261. return []byte(c.String()), nil
  262. }
  263. func (c configTypeMetricPrefix) String() string {
  264. return c.value
  265. }
  266. func (c configTypeMetricPrefix) Value(defaultValue string) string {
  267. if c.value == "" {
  268. return defaultValue
  269. }
  270. return c.value
  271. }
  272. type configTypeHTTPPath struct {
  273. value string
  274. }
  275. func (c *configTypeHTTPPath) UnmarshalText(data []byte) error { // nolint: unparam
  276. if len(data) > 0 {
  277. c.value = "/" + strings.Trim(string(data), "/")
  278. }
  279. return nil
  280. }
  281. func (c configTypeHTTPPath) MarshalText() ([]byte, error) {
  282. return []byte(c.String()), nil
  283. }
  284. func (c configTypeHTTPPath) String() string {
  285. return c.value
  286. }
  287. func (c configTypeHTTPPath) Value(defaultValue string) string {
  288. if c.value == "" {
  289. return defaultValue
  290. }
  291. return c.value
  292. }
  293. type config struct {
  294. Debug bool `json:"debug"`
  295. Secret mtglib.Secret `json:"secret"`
  296. BindTo configTypeHostPort `json:"bind-to"`
  297. TCPBuffer configTypeBytes `json:"tcp-buffer"`
  298. PreferIP configTypePreferIP `json:"prefer-ip"`
  299. CloakPort configTypePort `json:"cloak-port"`
  300. Probes struct {
  301. Time struct {
  302. Enabled bool `json:"enabled"`
  303. AllowSkewness configTypeDuration `json:"allow-skewness"`
  304. } `json:"time"`
  305. AntiReplay struct {
  306. Enabled bool `json:"enabled"`
  307. MaxSize configTypeBytes `json:"max-size"`
  308. ErrorRate configTypeFloat `json:"error-rate"`
  309. } `json:"anti-replay"`
  310. } `json:"probes"`
  311. Network struct {
  312. PublicIP struct {
  313. IPv4 configTypeIP `json:"ipv4"`
  314. IPv6 configTypeIP `json:"ipv6"`
  315. } `json:"public-ip"`
  316. Timeout struct {
  317. TCP configTypeDuration `json:"tcp"`
  318. HTTP configTypeDuration `json:"http"`
  319. } `json:"timeout"`
  320. DOHIP configTypeIP `json:"doh-ip"`
  321. Proxies []configTypeURL `json:"proxies"`
  322. } `json:"network"`
  323. Stats struct {
  324. StatsD struct {
  325. Enabled bool `json:"enabled"`
  326. Address configTypeHostPort `json:"address"`
  327. MetricPrefix configTypeMetricPrefix `json:"metric-prefix"`
  328. } `json:"statsd"`
  329. Prometheus struct {
  330. Enabled bool `json:"enabled"`
  331. BindTo configTypeHostPort `json:"bind-to"`
  332. HTTPPath configTypeHTTPPath `json:"http-path"`
  333. MetricPrefix configTypeMetricPrefix `json:"metric-prefix"`
  334. } `json:"prometheus"`
  335. } `json:"stats"`
  336. }
  337. func (c *config) Validate() error {
  338. if len(c.Secret.Key) == 0 || c.Secret.Host == "" {
  339. return fmt.Errorf("incorrect secret %s", c.Secret.String())
  340. }
  341. return nil
  342. }
  343. func (c *config) String() string {
  344. buf := &bytes.Buffer{}
  345. encoder := json.NewEncoder(buf)
  346. encoder.SetEscapeHTML(false)
  347. if err := encoder.Encode(c); err != nil {
  348. panic(err)
  349. }
  350. return buf.String()
  351. }
  352. type configRaw struct {
  353. Debug bool `toml:"debug" json:"debug"`
  354. Secret string `toml:"secret" json:"secret"`
  355. BindTo string `toml:"bind-to" json:"bind-to"`
  356. TCPBuffer string `toml:"tcp-buffer" json:"tcp-buffer"`
  357. PreferIP string `toml:"prefer-ip" json:"prefer-ip"`
  358. CloakPort uint `toml:"cloak-port" json:"cloak-port"`
  359. Probes struct {
  360. Time struct {
  361. Enabled bool `toml:"enabled" json:"enabled"`
  362. AllowSkewness string `toml:"allow-skewness" json:"allow-skewness"`
  363. } `toml:"time" json:"time"`
  364. AntiReplay struct {
  365. Enabled bool `toml:"enabled" json:"enabled"`
  366. MaxSize string `toml:"max-size" json:"max-size"`
  367. ErrorRate float64 `toml:"error-rate" json:"error-rate"`
  368. } `toml:"anti-replay" json:"anti-replay"`
  369. } `toml:"probes" json:"probes"`
  370. Network struct {
  371. PublicIP struct {
  372. IPv4 string `toml:"ipv4" json:"ipv4"`
  373. IPv6 string `toml:"ipv6" json:"ipv6"`
  374. } `toml:"public-ip" json:"public-ip"`
  375. Timeout struct {
  376. TCP string `toml:"tcp" json:"tcp"`
  377. HTTP string `toml:"http" json:"http"`
  378. } `toml:"timeout" json:"timeout"`
  379. DOHIP string `toml:"doh-ip" json:"doh-ip"`
  380. Proxies []string `toml:"proxies" json:"proxies"`
  381. } `toml:"network" json:"network"`
  382. Stats struct {
  383. StatsD struct {
  384. Enabled bool `toml:"enabled" json:"enabled"`
  385. Address string `toml:"address" json:"address"`
  386. MetricPrefix string `toml:"metric-prefix" json:"metric-prefix"`
  387. } `toml:"statsd" json:"statsd"`
  388. Prometheus struct {
  389. Enabled bool `toml:"enabled" json:"enabled"`
  390. BindTo string `toml:"bind-to" json:"bind-to"`
  391. HTTPPath string `toml:"http-path" json:"http-path"`
  392. MetricPrefix string `toml:"metric-prefix" json:"metric-prefix"`
  393. } `toml:"prometheus" json:"prometheus"`
  394. } `toml:"stats" json:"stats"`
  395. }
  396. func parseConfig(reader io.Reader) (*config, error) {
  397. rawConf := &configRaw{}
  398. if err := toml.NewDecoder(reader).Decode(rawConf); err != nil {
  399. return nil, fmt.Errorf("cannot parse toml config: %w", err)
  400. }
  401. jsonBuf := &bytes.Buffer{}
  402. jsonEncoder := json.NewEncoder(jsonBuf)
  403. jsonEncoder.SetEscapeHTML(false)
  404. jsonEncoder.SetIndent("", "")
  405. if err := jsonEncoder.Encode(rawConf); err != nil {
  406. return nil, fmt.Errorf("cannot dump into interim format: %w", err)
  407. }
  408. conf := &config{}
  409. if err := json.NewDecoder(jsonBuf).Decode(conf); err != nil {
  410. return nil, fmt.Errorf("cannot parse final config: %w", err)
  411. }
  412. if err := conf.Validate(); err != nil {
  413. return nil, fmt.Errorf("cannot validate config: %w", err)
  414. }
  415. return conf, nil
  416. }