mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 11:04:02 +03:00
Add separate handshake timeout
This PR adds a new setting to the config: `network.timeout`. This setting defines a time period during which all handshake procedures and ceremonies must be completed. If not - connection is aborted. This should help in situations when connection is established but client cannot continue for some reason (for example, RST sent by some middle box).
This commit is contained in:
+1
-3
@@ -205,13 +205,11 @@ proxies = [
|
|||||||
# means a timeout on pumping data between sockset when nothing is
|
# means a timeout on pumping data between sockset when nothing is
|
||||||
# happening.
|
# happening.
|
||||||
#
|
#
|
||||||
# please be noticed that handshakes have no timeouts intentionally. You can
|
|
||||||
# find a reasoning here:
|
|
||||||
# https://www.ndss-symposium.org/wp-content/uploads/2020/02/23087-paper.pdf
|
|
||||||
[network.timeout]
|
[network.timeout]
|
||||||
tcp = "5s"
|
tcp = "5s"
|
||||||
http = "10s"
|
http = "10s"
|
||||||
idle = "5m"
|
idle = "5m"
|
||||||
|
handshake = "10s"
|
||||||
|
|
||||||
# mtg has to mimic real websites. It does not mean domain fronting, it also
|
# mtg has to mimic real websites. It does not mean domain fronting, it also
|
||||||
# means that traffic characteristics should be similar to real world traffic.
|
# means that traffic characteristics should be similar to real world traffic.
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
|
|||||||
AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
|
AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
|
||||||
TolerateTimeSkewness: conf.TolerateTimeSkewness.Value,
|
TolerateTimeSkewness: conf.TolerateTimeSkewness.Value,
|
||||||
IdleTimeout: conf.Network.Timeout.Idle.Get(mtglib.DefaultIdleTimeout),
|
IdleTimeout: conf.Network.Timeout.Idle.Get(mtglib.DefaultIdleTimeout),
|
||||||
|
HandshakeTimeout: conf.Network.Timeout.Handshake.Get(mtglib.DefaultHandshakeTimeout),
|
||||||
|
|
||||||
DoppelGangerURLs: doppelGangerURLs,
|
DoppelGangerURLs: doppelGangerURLs,
|
||||||
DoppelGangerPerRaid: conf.Defense.Doppelganger.Repeats.Get(mtglib.DoppelGangerPerRaid),
|
DoppelGangerPerRaid: conf.Defense.Doppelganger.Repeats.Get(mtglib.DoppelGangerPerRaid),
|
||||||
|
|||||||
@@ -60,9 +60,10 @@ type Config struct {
|
|||||||
} `json:"defense"`
|
} `json:"defense"`
|
||||||
Network struct {
|
Network struct {
|
||||||
Timeout struct {
|
Timeout struct {
|
||||||
TCP TypeDuration `json:"tcp"`
|
TCP TypeDuration `json:"tcp"`
|
||||||
HTTP TypeDuration `json:"http"`
|
HTTP TypeDuration `json:"http"`
|
||||||
Idle TypeDuration `json:"idle"`
|
Idle TypeDuration `json:"idle"`
|
||||||
|
Handshake TypeDuration `json:"handshake"`
|
||||||
} `json:"timeout"`
|
} `json:"timeout"`
|
||||||
DOHIP TypeIP `json:"dohIp"`
|
DOHIP TypeIP `json:"dohIp"`
|
||||||
DNS TypeDNSURI `json:"dns"`
|
DNS TypeDNSURI `json:"dns"`
|
||||||
|
|||||||
@@ -55,9 +55,10 @@ type tomlConfig struct {
|
|||||||
} `toml:"defense" json:"defense,omitempty"`
|
} `toml:"defense" json:"defense,omitempty"`
|
||||||
Network struct {
|
Network struct {
|
||||||
Timeout struct {
|
Timeout struct {
|
||||||
TCP string `toml:"tcp" json:"tcp,omitempty"`
|
TCP string `toml:"tcp" json:"tcp,omitempty"`
|
||||||
HTTP string `toml:"http" json:"http,omitempty"`
|
HTTP string `toml:"http" json:"http,omitempty"`
|
||||||
Idle string `toml:"idle" json:"idle,omitempty"`
|
Idle string `toml:"idle" json:"idle,omitempty"`
|
||||||
|
Handshake string `toml:"handshake" json:"handshake,omitempty"`
|
||||||
} `toml:"timeout" json:"timeout,omitempty"`
|
} `toml:"timeout" json:"timeout,omitempty"`
|
||||||
DOHIP string `toml:"doh-ip" json:"dohIp,omitempty"`
|
DOHIP string `toml:"doh-ip" json:"dohIp,omitempty"`
|
||||||
DNS string `toml:"dns" json:"dns,omitempty"`
|
DNS string `toml:"dns" json:"dns,omitempty"`
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ const (
|
|||||||
// avoid racing with MTProto ping_delay_disconnect (~60s interval).
|
// avoid racing with MTProto ping_delay_disconnect (~60s interval).
|
||||||
DefaultIdleTimeout = 5 * time.Minute
|
DefaultIdleTimeout = 5 * time.Minute
|
||||||
|
|
||||||
|
// DefaultHandshakeTimeout defines a time period during which the
|
||||||
|
// all handshake ceremonies must be completed.
|
||||||
|
DefaultHandshakeTimeout = 10 * time.Second
|
||||||
|
|
||||||
// DefaultTolerateTimeSkewness is a default timeout for time skewness on a
|
// DefaultTolerateTimeSkewness is a default timeout for time skewness on a
|
||||||
// faketls timeout verification.
|
// faketls timeout verification.
|
||||||
DefaultTolerateTimeSkewness = 3 * time.Second
|
DefaultTolerateTimeSkewness = 3 * time.Second
|
||||||
|
|||||||
@@ -40,11 +40,6 @@ func ReadClientHello(
|
|||||||
hostname string,
|
hostname string,
|
||||||
tolerateTimeSkewness time.Duration,
|
tolerateTimeSkewness time.Duration,
|
||||||
) (*ClientHello, error) {
|
) (*ClientHello, error) {
|
||||||
if err := conn.SetReadDeadline(time.Now().Add(ClientHelloReadTimeout)); err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot set read deadline: %w", err)
|
|
||||||
}
|
|
||||||
defer conn.SetReadDeadline(resetDeadline) //nolint: errcheck
|
|
||||||
|
|
||||||
// This is how FakeTLS is organized:
|
// This is how FakeTLS is organized:
|
||||||
// 1. We create sha256 HMAC with a given secret
|
// 1. We create sha256 HMAC with a given secret
|
||||||
// 2. We dump there a whole TLS frame except of the fact that random
|
// 2. We dump there a whole TLS frame except of the fact that random
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/9seconds/mtg/v2/mtglib"
|
"github.com/9seconds/mtg/v2/mtglib"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
|
"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
@@ -71,11 +70,6 @@ func (suite *ParseClientHelloSnapshotTestSuite) makeConn(data []byte) *parseClie
|
|||||||
readBuf: readBuf,
|
readBuf: readBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
connMock.
|
|
||||||
On("SetReadDeadline", mock.AnythingOfType("time.Time")).
|
|
||||||
Twice().
|
|
||||||
Return(nil)
|
|
||||||
|
|
||||||
return connMock
|
return connMock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -14,7 +13,6 @@ import (
|
|||||||
"github.com/9seconds/mtg/v2/mtglib"
|
"github.com/9seconds/mtg/v2/mtglib"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/tls"
|
"github.com/9seconds/mtg/v2/mtglib/internal/tls"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
|
"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
@@ -53,11 +51,6 @@ func (suite *ParseClientHelloTestSuite) SetupTest() {
|
|||||||
suite.connMock = &parseClientHelloConnMock{
|
suite.connMock = &parseClientHelloConnMock{
|
||||||
readBuf: suite.readBuf,
|
readBuf: suite.readBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
suite.connMock.
|
|
||||||
On("SetReadDeadline", mock.AnythingOfType("time.Time")).
|
|
||||||
Twice().
|
|
||||||
Return(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloTestSuite) TearDownTest() {
|
func (suite *ParseClientHelloTestSuite) TearDownTest() {
|
||||||
@@ -69,23 +62,11 @@ type ParseClientHello_TLSHeaderTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHello_TLSHeaderTestSuite) TestEmpty() {
|
func (suite *ParseClientHello_TLSHeaderTestSuite) TestEmpty() {
|
||||||
suite.connMock.ExpectedCalls = []*mock.Call{}
|
|
||||||
suite.connMock.
|
|
||||||
On("SetReadDeadline", mock.AnythingOfType("time.Time")).
|
|
||||||
Once().
|
|
||||||
Return(errors.New("fail"))
|
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "fail")
|
suite.ErrorContains(err, "cannot read client hello")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHello_TLSHeaderTestSuite) TestNothing() {
|
func (suite *ParseClientHello_TLSHeaderTestSuite) TestNothing() {
|
||||||
suite.connMock.ExpectedCalls = []*mock.Call{}
|
|
||||||
suite.connMock.
|
|
||||||
On("SetReadDeadline", mock.AnythingOfType("time.Time")).
|
|
||||||
Twice().
|
|
||||||
Return(nil)
|
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorIs(err, io.EOF)
|
suite.ErrorIs(err, io.EOF)
|
||||||
}
|
}
|
||||||
@@ -478,11 +459,6 @@ func (s *ParseClientHelloFragmentedTestSuite) makeConn(data []byte) *parseClient
|
|||||||
readBuf: readBuf,
|
readBuf: readBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
connMock.
|
|
||||||
On("SetReadDeadline", mock.AnythingOfType("time.Time")).
|
|
||||||
Twice().
|
|
||||||
Return(nil)
|
|
||||||
|
|
||||||
return connMock
|
return connMock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,6 @@ package fake
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var ErrBadDigest = errors.New("incorrect client random")
|
||||||
ClientHelloReadTimeout = 5 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
resetDeadline time.Time
|
|
||||||
|
|
||||||
ErrBadDigest = errors.New("incorrect client random")
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type Proxy struct {
|
|||||||
allowFallbackOnUnknownDC bool
|
allowFallbackOnUnknownDC bool
|
||||||
tolerateTimeSkewness time.Duration
|
tolerateTimeSkewness time.Duration
|
||||||
idleTimeout time.Duration
|
idleTimeout time.Duration
|
||||||
|
handshakeTimeout time.Duration
|
||||||
domainFrontingPort int
|
domainFrontingPort int
|
||||||
domainFrontingIP string
|
domainFrontingIP string
|
||||||
domainFrontingProxyProtocol bool
|
domainFrontingProxyProtocol bool
|
||||||
@@ -66,6 +67,11 @@ func (p *Proxy) ServeConn(conn essentials.Conn) {
|
|||||||
ctx := newStreamContext(p.ctx, p.logger, conn)
|
ctx := newStreamContext(p.ctx, p.logger, conn)
|
||||||
defer ctx.Close()
|
defer ctx.Close()
|
||||||
|
|
||||||
|
if err := ctx.clientConn.SetDeadline(time.Now().Add(p.handshakeTimeout)); err != nil {
|
||||||
|
ctx.logger.WarningError("cannot set handshake timeout", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
stop := context.AfterFunc(ctx, func() {
|
stop := context.AfterFunc(ctx, func() {
|
||||||
ctx.Close()
|
ctx.Close()
|
||||||
})
|
})
|
||||||
@@ -97,6 +103,11 @@ func (p *Proxy) ServeConn(conn essentials.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ctx.clientConn.SetDeadline(time.Time{}); err != nil {
|
||||||
|
ctx.logger.WarningError("cannot set deadline", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := p.doTelegramCall(ctx); err != nil {
|
if err := p.doTelegramCall(ctx); err != nil {
|
||||||
ctx.logger.WarningError("cannot dial to telegram", err)
|
ctx.logger.WarningError("cannot dial to telegram", err)
|
||||||
return
|
return
|
||||||
@@ -346,6 +357,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
|
|||||||
domainFrontingIP: opts.DomainFrontingIP,
|
domainFrontingIP: opts.DomainFrontingIP,
|
||||||
tolerateTimeSkewness: opts.getTolerateTimeSkewness(),
|
tolerateTimeSkewness: opts.getTolerateTimeSkewness(),
|
||||||
idleTimeout: opts.getIdleTimeout(),
|
idleTimeout: opts.getIdleTimeout(),
|
||||||
|
handshakeTimeout: opts.getHandshakeTimeout(),
|
||||||
allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC,
|
allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC,
|
||||||
telegram: tg,
|
telegram: tg,
|
||||||
doppelGanger: doppel.NewGanger(
|
doppelGanger: doppel.NewGanger(
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ type ProxyOpts struct {
|
|||||||
// This is an optional setting.
|
// This is an optional setting.
|
||||||
IdleTimeout time.Duration
|
IdleTimeout time.Duration
|
||||||
|
|
||||||
|
// HandshakeTimeout is a timeout during which all handshake ceremonies must
|
||||||
|
// be completed, otherwise this process will be aborted
|
||||||
|
//
|
||||||
|
// This is an optional setting.
|
||||||
|
HandshakeTimeout time.Duration
|
||||||
|
|
||||||
// TolerateTimeSkewness is a time boundary that defines a time range where
|
// TolerateTimeSkewness is a time boundary that defines a time range where
|
||||||
// faketls timestamp is acceptable.
|
// faketls timestamp is acceptable.
|
||||||
//
|
//
|
||||||
@@ -215,6 +221,14 @@ func (p ProxyOpts) getPreferIP() string {
|
|||||||
return p.PreferIP
|
return p.PreferIP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p ProxyOpts) getHandshakeTimeout() time.Duration {
|
||||||
|
if p.HandshakeTimeout == 0 {
|
||||||
|
return DefaultHandshakeTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.HandshakeTimeout
|
||||||
|
}
|
||||||
|
|
||||||
func (p ProxyOpts) getIdleTimeout() time.Duration {
|
func (p ProxyOpts) getIdleTimeout() time.Duration {
|
||||||
if p.IdleTimeout == 0 {
|
if p.IdleTimeout == 0 {
|
||||||
return DefaultIdleTimeout
|
return DefaultIdleTimeout
|
||||||
|
|||||||
Reference in New Issue
Block a user