mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 15:24:01 +03:00
Merge pull request #447 from 9seconds/handshake-timeout
Add separate handshake timeout
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),
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ type Config 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"`
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ type tomlConfig 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
|
||||||
|
|||||||
@@ -47,11 +47,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)
|
||||||
}
|
}
|
||||||
@@ -479,11 +460,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