Add correct configuration

This commit is contained in:
9seconds
2021-03-10 14:42:24 +03:00
parent 59f328b804
commit d1dd56550f
6 changed files with 389 additions and 43 deletions
+376 -7
View File
@@ -5,14 +5,383 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net"
"net/url"
"regexp"
"strconv"
"strings"
"time"
"github.com/9seconds/mtg/v2/mtglib" "github.com/9seconds/mtg/v2/mtglib"
"github.com/alecthomas/units"
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
) )
type configTypeHostPort struct {
host configTypeIP
port configTypePort
}
func (c *configTypeHostPort) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
host, port, err := net.SplitHostPort(string(data))
if err != nil {
return fmt.Errorf("incorrect host:port syntax: %w", err)
}
if err := c.port.UnmarshalJSON([]byte(port)); err != nil {
return fmt.Errorf("incorrect port in host:port: %w", err)
}
if err := c.host.UnmarshalText([]byte(host)); err != nil {
return fmt.Errorf("incorrect host: %w", err)
}
return nil
}
func (c configTypeHostPort) String() string {
return c.Value(net.IP{}, 0)
}
func (c configTypeHostPort) Value(defaultHostValue net.IP, defaultPortValue uint) string {
return net.JoinHostPort(c.host.Value(defaultHostValue).String(),
strconv.Itoa(int(c.port.Value(defaultPortValue))))
}
type configTypePort struct {
value uint
}
func (c *configTypePort) UnmarshalJSON(data []byte) error {
if len(data) == 0 {
return nil
}
intValue, err := strconv.ParseUint(string(data), 10, 16)
if err != nil {
return fmt.Errorf("port number is not a number: %w", err)
}
if intValue == 0 || intValue > 65536 {
return fmt.Errorf("port number should be 0 < portNo < 65536: %d", intValue)
}
c.value = uint(intValue)
return nil
}
func (c configTypePort) String() string {
return strconv.Itoa(int(c.value))
}
func (c configTypePort) Value(defaultValue uint) uint {
if c.value == 0 {
return defaultValue
}
return c.value
}
type configTypeBytes struct {
value uint
}
func (c *configTypeBytes) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
value, err := units.ParseStrictBytes(strings.ToUpper(string(data)))
if err != nil {
return fmt.Errorf("incorrect bytes value: %w", err)
}
if value < 0 {
return fmt.Errorf("%d should be positive number", value)
}
c.value = uint(value)
return nil
}
func (c configTypeBytes) String() string {
return units.ToString(int64(c.value), 1024, "ib", "b")
}
func (c configTypeBytes) Value(defaultValue uint) uint {
if c.value == 0 {
return defaultValue
}
return c.value
}
type configTypePreferIP struct {
value string
}
func (c *configTypePreferIP) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
text := strings.ToLower(string(data))
switch text {
case "prefer-ipv4", "prefer-ipv6", "only-ipv4", "only-ipv6":
c.value = text
default:
return fmt.Errorf("incorrect prefer-ip value: %s", string(data))
}
return nil
}
func (c *configTypePreferIP) String() string {
return c.value
}
func (c *configTypePreferIP) Value(defaultValue string) string {
if c.value == "" {
return defaultValue
}
return c.value
}
type configTypeDuration struct {
value time.Duration
}
func (c *configTypeDuration) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
dur, err := time.ParseDuration(strings.ToLower(string(data)))
if err != nil {
return fmt.Errorf("incorrect duration: %w", err)
}
if dur < 0 {
return fmt.Errorf("%s should be positive duration", dur)
}
c.value = dur
return nil
}
func (c configTypeDuration) String() string {
return c.value.String()
}
func (c configTypeDuration) Value(defaultValue time.Duration) time.Duration {
if c.value == 0 {
return defaultValue
}
return c.value
}
type configTypeFloat struct {
value float64
}
func (c *configTypeFloat) UnmarshalJSON(data []byte) error {
value, err := strconv.ParseFloat(string(data), 64)
if err != nil {
return fmt.Errorf("incorrect float value: %w", err)
}
if value < 0 {
return fmt.Errorf("%f should be positive", value)
}
c.value = value
return nil
}
func (c configTypeFloat) String() string {
return strconv.FormatFloat(c.value, 'f', -1, 64)
}
func (c configTypeFloat) Value(defaultValue float64) float64 {
if c.value < 0.00001 {
return defaultValue
}
return c.value
}
type configTypeIP struct {
value net.IP
}
func (c *configTypeIP) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
ip := net.ParseIP(string(data))
if ip == nil {
return fmt.Errorf("incorrect ip address: %s", string(data))
}
c.value = ip
return nil
}
func (c configTypeIP) String() string {
return c.value.String()
}
func (c configTypeIP) Value(defaultValue net.IP) net.IP {
if c.value == nil {
return defaultValue
}
return c.value
}
type configTypeURL struct {
value *url.URL
}
func (c *configTypeURL) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
value, err := url.Parse(string(data))
if err != nil {
return fmt.Errorf("incorrect URL: %w", err)
}
c.value = value
return nil
}
func (c configTypeURL) String() string {
if c.value == nil {
return ""
}
return c.value.String()
}
func (c configTypeURL) Value(defaultValue *url.URL) *url.URL {
if c.value == nil {
return defaultValue
}
return c.value
}
type configTypeMetricPrefix struct {
value string
}
func (c *configTypeMetricPrefix) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
prefix := string(data)
if ok, err := regexp.MatchString("^[a-z0-9]+$", prefix); !ok || err != nil {
return fmt.Errorf("incorrect metric prefix: %s", prefix)
}
c.value = prefix
return nil
}
func (c configTypeMetricPrefix) String() string {
return c.value
}
func (c configTypeMetricPrefix) Value(defaultValue string) string {
if c.value == "" {
return defaultValue
}
return c.value
}
type configTypeHTTPPath struct {
value string
}
func (c *configTypeHTTPPath) UnmarshalText(data []byte) error { // nolint: unparam
if len(data) > 0 {
c.value = "/" + strings.Trim(string(data), "/")
}
return nil
}
func (c configTypeHTTPPath) String() string {
return c.value
}
func (c configTypeHTTPPath) Value(defaultValue string) string {
if c.value == "" {
return defaultValue
}
return c.value
}
type config struct { type config struct {
Debug bool `json:"debug"` Debug bool `json:"debug"`
Secret mtglib.Secret `json:"secret"` Secret mtglib.Secret `json:"secret"`
BindTo configTypeHostPort `json:"bind-to"`
TCPBuffer configTypeBytes `json:"tcp-buffer"`
PreferIP configTypePreferIP `json:"prefer-ip"`
CloakPort configTypePort `json:"cloak-port"`
Probes struct {
Time struct {
Enabled bool `json:"enabled"`
AllowSkewness configTypeDuration `json:"allow-skewness"`
} `json:"time"`
AntiReplay struct {
Enabled bool `json:"enabled"`
MaxSize configTypeBytes `json:"max-size"`
ErrorRate configTypeFloat `json:"error-rate"`
} `json:"anti-replay"`
} `json:"probes"`
Network struct {
PublicIP struct {
IPv4 configTypeIP `json:"ipv4"`
IPv6 configTypeIP `json:"ipv6"`
} `json:"public-ip"`
DOHIP configTypeIP `json:"doh-ip"`
Proxies []configTypeURL `json:"proxies"`
} `json:"network"`
Stats struct {
StatsD struct {
Enabled bool `json:"enabled"`
Address configTypeHostPort `json:"address"`
MetricPrefix configTypeMetricPrefix `json:"metric-prefix"`
} `json:"statsd"`
Prometheus struct {
Enabled bool `json:"enabled"`
BindTo configTypeHostPort `json:"bind-to"`
HTTPPath configTypeHTTPPath `json:"http-path"`
MetricPrefix configTypeMetricPrefix `json:"metric-prefix"`
} `json:"prometheus"`
} `json:"stats"`
} }
func (c *config) Validate() error { func (c *config) Validate() error {
@@ -36,9 +405,9 @@ type configRaw struct {
AllowSkewness string `toml:"allow-skewness" json:"allow-skewness"` AllowSkewness string `toml:"allow-skewness" json:"allow-skewness"`
} `toml:"time" json:"time"` } `toml:"time" json:"time"`
AntiReplay struct { AntiReplay struct {
Enabled bool `toml:"enabled" json:"enabled"` Enabled bool `toml:"enabled" json:"enabled"`
MaxSize string `toml:"max-size" json:"max-size"` MaxSize string `toml:"max-size" json:"max-size"`
TTL string `toml:"ttl" json:"ttl"` ErrorRate float64 `toml:"error-rate" json:"error-rate"`
} `toml:"anti-replay" json:"anti-replay"` } `toml:"anti-replay" json:"anti-replay"`
} `toml:"probes" json:"probes"` } `toml:"probes" json:"probes"`
Network struct { Network struct {
@@ -46,8 +415,8 @@ type configRaw struct {
IPv4 string `toml:"ipv4" json:"ipv4"` IPv4 string `toml:"ipv4" json:"ipv4"`
IPv6 string `toml:"ipv6" json:"ipv6"` IPv6 string `toml:"ipv6" json:"ipv6"`
} `toml:"public-ip" json:"public-ip"` } `toml:"public-ip" json:"public-ip"`
DOHHostname string `toml:"doh-hostname" json:"doh-hostname"` DOHIP string `toml:"doh-ip" json:"doh-ip"`
Proxies []string `toml:"proxies" json:"proxies"` Proxies []string `toml:"proxies" json:"proxies"`
} `toml:"network" json:"network"` } `toml:"network" json:"network"`
Stats struct { Stats struct {
StatsD struct { StatsD struct {
+5 -30
View File
@@ -38,7 +38,7 @@ tcp-buffer = "4kb"
# Only ipv6 connectivity is used # Only ipv6 connectivity is used
# - only-ipv4: # - only-ipv4:
# Only ipv4 connectivity is used # Only ipv4 connectivity is used
prefer-ips = "prefer-ipv6" prefer-ip = "prefer-ipv6"
# FakeTLS uses domain fronting protection. So it needs to know a port to # FakeTLS uses domain fronting protection. So it needs to know a port to
# access. # access.
@@ -58,7 +58,7 @@ cloak-port = 443
# it has to access. # it has to access.
# #
# By default we use Quad9. # By default we use Quad9.
doh-hostname = "9.9.9.9" doh-ip = "9.9.9.9"
# mtg can work via proxies (for now, we support only socks5). Proxy # mtg can work via proxies (for now, we support only socks5). Proxy
# configuration is done via list. So, you can specify many proxies # configuration is done via list. So, you can specify many proxies
@@ -75,7 +75,6 @@ doh-hostname = "9.9.9.9"
# #
# socks5://user:password@host:port?open_threshold=5&half_open_timeout=1m&reset_failures_timeout=10s # socks5://user:password@host:port?open_threshold=5&half_open_timeout=1m&reset_failures_timeout=10s
# #
# Only socks5 proxy is used. user/password is optional. As you can # Only socks5 proxy is used. user/password is optional. As you can
# see, you can specify some parameters in GET query. These parameters # see, you can specify some parameters in GET query. These parameters
# configure circuit breaker. # configure circuit breaker.
@@ -102,31 +101,6 @@ proxies = [
ipv4 = "" ipv4 = ""
ipv6 = "" ipv6 = ""
# you can redefine a dialer for mtg. Dialer is how we 'dial' to either
# some external services or telegram. empty string means default
# connectivity.
#
# it is also possible to use socks5 or shadowsocks here
#
# socks5 example:
# socks5://user:password@host:port
# shadowsocks example (SIP002):
# ss://YWVzLTEyOC1nY206dGVzdA@192.168.100.1:8888
#
# You can define 2 dialers here: telegram and default. Telegram dialer
# is used to connect to Telegram servers only. Default is used for other
# purposes, like accessing ifconfig.co to obtains public address (DNS is
# resolved via DoH)
#
# Please also be aware that dialers are only doing TCP. If UDP is
# required (for statsd for example), then these dialers are going to be
# ignored.
#
# If telegram dialer is not defined, a default one is going to be used.
[network.dialers]
telegram = ""
default = ""
# FakeTLS can compare timestamps to prevent probes. Each message has # FakeTLS can compare timestamps to prevent probes. Each message has
# encrypted timestamp. So, mtg can compare this timestamp and decide if # encrypted timestamp. So, mtg can compare this timestamp and decide if
# we need to proceed with connection or not. # we need to proceed with connection or not.
@@ -154,8 +128,9 @@ enabled = true
# that we can go over this limit for 10-20% under some conditions and # that we can go over this limit for 10-20% under some conditions and
# architectures. # architectures.
max-size = "16mb" max-size = "16mb"
# TTL for each cache record. # we use stable bloom filters for anti-replay cache. This helps
ttl = "8h" # to maintain a desired error ratio.
error-rate = 0.0001
# statsd statistics integration. # statsd statistics integration.
[stats.statsd] [stats.statsd]
+1
View File
@@ -3,6 +3,7 @@ module github.com/9seconds/mtg/v2
go 1.16 go 1.16
require ( require (
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
github.com/kr/pretty v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect
+3
View File
@@ -1,3 +1,5 @@
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4=
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0=
@@ -24,6 +26,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+1 -3
View File
@@ -2,6 +2,4 @@ package mtglib
import "errors" import "errors"
var ( var ErrSecretEmpty = errors.New("secret is empty")
ErrSecretEmpty = errors.New("secret is empty")
)
+3 -3
View File
@@ -58,10 +58,10 @@ func (s *Secret) UnmarshalText(data []byte) error {
} }
func (s Secret) Base64() string { func (s Secret) Base64() string {
data := append([]byte{238}, s.Key...) // 238 = hex ee data := append([]byte{238}, s.Key...) // 238 = hex ee
data = append(data, s.Host...) data = append(data, s.Host...)
return base64.RawURLEncoding.EncodeToString(data) return base64.RawURLEncoding.EncodeToString(data)
} }
func (s Secret) String() string { func (s Secret) String() string {