Просмотр исходного кода

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).
tags/v2.2.8^2^2
9seconds 4 недель назад
Родитель
Сommit
eb564936c7

+ 1
- 3
example.config.toml Просмотреть файл

205
 # means a timeout on pumping data between sockset when nothing is
205
 # means a timeout on pumping data between sockset when nothing is
206
 # happening.
206
 # happening.
207
 #
207
 #
208
-# please be noticed that handshakes have no timeouts intentionally. You can
209
-# find a reasoning here:
210
-# https://www.ndss-symposium.org/wp-content/uploads/2020/02/23087-paper.pdf
211
 [network.timeout]
208
 [network.timeout]
212
 tcp = "5s"
209
 tcp = "5s"
213
 http = "10s"
210
 http = "10s"
214
 idle = "5m"
211
 idle = "5m"
212
+handshake = "10s"
215
 
213
 
216
 # mtg has to mimic real websites. It does not mean domain fronting, it also
214
 # mtg has to mimic real websites. It does not mean domain fronting, it also
217
 # means that traffic characteristics should be similar to real world traffic.
215
 # means that traffic characteristics should be similar to real world traffic.

+ 1
- 0
internal/cli/run_proxy.go Просмотреть файл

263
 		AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
263
 		AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
264
 		TolerateTimeSkewness:     conf.TolerateTimeSkewness.Value,
264
 		TolerateTimeSkewness:     conf.TolerateTimeSkewness.Value,
265
 		IdleTimeout:              conf.Network.Timeout.Idle.Get(mtglib.DefaultIdleTimeout),
265
 		IdleTimeout:              conf.Network.Timeout.Idle.Get(mtglib.DefaultIdleTimeout),
266
+		HandshakeTimeout:         conf.Network.Timeout.Handshake.Get(mtglib.DefaultHandshakeTimeout),
266
 
267
 
267
 		DoppelGangerURLs:    doppelGangerURLs,
268
 		DoppelGangerURLs:    doppelGangerURLs,
268
 		DoppelGangerPerRaid: conf.Defense.Doppelganger.Repeats.Get(mtglib.DoppelGangerPerRaid),
269
 		DoppelGangerPerRaid: conf.Defense.Doppelganger.Repeats.Get(mtglib.DoppelGangerPerRaid),

+ 4
- 3
internal/config/config.go Просмотреть файл

60
 	} `json:"defense"`
60
 	} `json:"defense"`
61
 	Network struct {
61
 	Network struct {
62
 		Timeout struct {
62
 		Timeout struct {
63
-			TCP  TypeDuration `json:"tcp"`
64
-			HTTP TypeDuration `json:"http"`
65
-			Idle TypeDuration `json:"idle"`
63
+			TCP       TypeDuration `json:"tcp"`
64
+			HTTP      TypeDuration `json:"http"`
65
+			Idle      TypeDuration `json:"idle"`
66
+			Handshake TypeDuration `json:"handshake"`
66
 		} `json:"timeout"`
67
 		} `json:"timeout"`
67
 		DOHIP   TypeIP         `json:"dohIp"`
68
 		DOHIP   TypeIP         `json:"dohIp"`
68
 		DNS     TypeDNSURI     `json:"dns"`
69
 		DNS     TypeDNSURI     `json:"dns"`

+ 4
- 3
internal/config/parse.go Просмотреть файл

55
 	} `toml:"defense" json:"defense,omitempty"`
55
 	} `toml:"defense" json:"defense,omitempty"`
56
 	Network struct {
56
 	Network struct {
57
 		Timeout struct {
57
 		Timeout struct {
58
-			TCP  string `toml:"tcp" json:"tcp,omitempty"`
59
-			HTTP string `toml:"http" json:"http,omitempty"`
60
-			Idle string `toml:"idle" json:"idle,omitempty"`
58
+			TCP       string `toml:"tcp" json:"tcp,omitempty"`
59
+			HTTP      string `toml:"http" json:"http,omitempty"`
60
+			Idle      string `toml:"idle" json:"idle,omitempty"`
61
+			Handshake string `toml:"handshake" json:"handshake,omitempty"`
61
 		} `toml:"timeout" json:"timeout,omitempty"`
62
 		} `toml:"timeout" json:"timeout,omitempty"`
62
 		DOHIP   string   `toml:"doh-ip" json:"dohIp,omitempty"`
63
 		DOHIP   string   `toml:"doh-ip" json:"dohIp,omitempty"`
63
 		DNS     string   `toml:"dns" json:"dns,omitempty"`
64
 		DNS     string   `toml:"dns" json:"dns,omitempty"`

+ 4
- 0
mtglib/init.go Просмотреть файл

81
 	// avoid racing with MTProto ping_delay_disconnect (~60s interval).
81
 	// avoid racing with MTProto ping_delay_disconnect (~60s interval).
82
 	DefaultIdleTimeout = 5 * time.Minute
82
 	DefaultIdleTimeout = 5 * time.Minute
83
 
83
 
84
+	// DefaultHandshakeTimeout defines a time period during which the
85
+	// all handshake ceremonies must be completed.
86
+	DefaultHandshakeTimeout = 10 * time.Second
87
+
84
 	// DefaultTolerateTimeSkewness is a default timeout for time skewness on a
88
 	// DefaultTolerateTimeSkewness is a default timeout for time skewness on a
85
 	// faketls timeout verification.
89
 	// faketls timeout verification.
86
 	DefaultTolerateTimeSkewness = 3 * time.Second
90
 	DefaultTolerateTimeSkewness = 3 * time.Second

+ 0
- 5
mtglib/internal/tls/fake/client_side.go Просмотреть файл

40
 	hostname string,
40
 	hostname string,
41
 	tolerateTimeSkewness time.Duration,
41
 	tolerateTimeSkewness time.Duration,
42
 ) (*ClientHello, error) {
42
 ) (*ClientHello, error) {
43
-	if err := conn.SetReadDeadline(time.Now().Add(ClientHelloReadTimeout)); err != nil {
44
-		return nil, fmt.Errorf("cannot set read deadline: %w", err)
45
-	}
46
-	defer conn.SetReadDeadline(resetDeadline) //nolint: errcheck
47
-
48
 	// This is how FakeTLS is organized:
43
 	// This is how FakeTLS is organized:
49
 	//  1. We create sha256 HMAC with a given secret
44
 	//  1. We create sha256 HMAC with a given secret
50
 	//  2. We dump there a whole TLS frame except of the fact that random
45
 	//  2. We dump there a whole TLS frame except of the fact that random

+ 0
- 6
mtglib/internal/tls/fake/client_side_snapshot_test.go Просмотреть файл

12
 	"github.com/9seconds/mtg/v2/mtglib"
12
 	"github.com/9seconds/mtg/v2/mtglib"
13
 	"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
13
 	"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
14
 	"github.com/stretchr/testify/assert"
14
 	"github.com/stretchr/testify/assert"
15
-	"github.com/stretchr/testify/mock"
16
 	"github.com/stretchr/testify/require"
15
 	"github.com/stretchr/testify/require"
17
 	"github.com/stretchr/testify/suite"
16
 	"github.com/stretchr/testify/suite"
18
 )
17
 )
71
 		readBuf: readBuf,
70
 		readBuf: readBuf,
72
 	}
71
 	}
73
 
72
 
74
-	connMock.
75
-		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
76
-		Twice().
77
-		Return(nil)
78
-
79
 	return connMock
73
 	return connMock
80
 }
74
 }
81
 
75
 

+ 1
- 25
mtglib/internal/tls/fake/client_side_test.go Просмотреть файл

4
 	"bytes"
4
 	"bytes"
5
 	"encoding/binary"
5
 	"encoding/binary"
6
 	"encoding/json"
6
 	"encoding/json"
7
-	"errors"
8
 	"io"
7
 	"io"
9
 	"os"
8
 	"os"
10
 	"testing"
9
 	"testing"
14
 	"github.com/9seconds/mtg/v2/mtglib"
13
 	"github.com/9seconds/mtg/v2/mtglib"
15
 	"github.com/9seconds/mtg/v2/mtglib/internal/tls"
14
 	"github.com/9seconds/mtg/v2/mtglib/internal/tls"
16
 	"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
15
 	"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
17
-	"github.com/stretchr/testify/mock"
18
 	"github.com/stretchr/testify/require"
16
 	"github.com/stretchr/testify/require"
19
 	"github.com/stretchr/testify/suite"
17
 	"github.com/stretchr/testify/suite"
20
 )
18
 )
53
 	suite.connMock = &parseClientHelloConnMock{
51
 	suite.connMock = &parseClientHelloConnMock{
54
 		readBuf: suite.readBuf,
52
 		readBuf: suite.readBuf,
55
 	}
53
 	}
56
-
57
-	suite.connMock.
58
-		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
59
-		Twice().
60
-		Return(nil)
61
 }
54
 }
62
 
55
 
63
 func (suite *ParseClientHelloTestSuite) TearDownTest() {
56
 func (suite *ParseClientHelloTestSuite) TearDownTest() {
69
 }
62
 }
70
 
63
 
71
 func (suite *ParseClientHello_TLSHeaderTestSuite) TestEmpty() {
64
 func (suite *ParseClientHello_TLSHeaderTestSuite) TestEmpty() {
72
-	suite.connMock.ExpectedCalls = []*mock.Call{}
73
-	suite.connMock.
74
-		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
75
-		Once().
76
-		Return(errors.New("fail"))
77
-
78
 	_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
65
 	_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
79
-	suite.ErrorContains(err, "fail")
66
+	suite.ErrorContains(err, "cannot read client hello")
80
 }
67
 }
81
 
68
 
82
 func (suite *ParseClientHello_TLSHeaderTestSuite) TestNothing() {
69
 func (suite *ParseClientHello_TLSHeaderTestSuite) TestNothing() {
83
-	suite.connMock.ExpectedCalls = []*mock.Call{}
84
-	suite.connMock.
85
-		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
86
-		Twice().
87
-		Return(nil)
88
-
89
 	_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
70
 	_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
90
 	suite.ErrorIs(err, io.EOF)
71
 	suite.ErrorIs(err, io.EOF)
91
 }
72
 }
478
 		readBuf: readBuf,
459
 		readBuf: readBuf,
479
 	}
460
 	}
480
 
461
 
481
-	connMock.
482
-		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
483
-		Twice().
484
-		Return(nil)
485
-
486
 	return connMock
462
 	return connMock
487
 }
463
 }
488
 
464
 

+ 1
- 10
mtglib/internal/tls/fake/init.go Просмотреть файл

2
 
2
 
3
 import (
3
 import (
4
 	"errors"
4
 	"errors"
5
-	"time"
6
 )
5
 )
7
 
6
 
8
-const (
9
-	ClientHelloReadTimeout = 5 * time.Second
10
-)
11
-
12
-var (
13
-	resetDeadline time.Time
14
-
15
-	ErrBadDigest = errors.New("incorrect client random")
16
-)
7
+var ErrBadDigest = errors.New("incorrect client random")

+ 12
- 0
mtglib/proxy.go Просмотреть файл

28
 	allowFallbackOnUnknownDC    bool
28
 	allowFallbackOnUnknownDC    bool
29
 	tolerateTimeSkewness        time.Duration
29
 	tolerateTimeSkewness        time.Duration
30
 	idleTimeout                 time.Duration
30
 	idleTimeout                 time.Duration
31
+	handshakeTimeout            time.Duration
31
 	domainFrontingPort          int
32
 	domainFrontingPort          int
32
 	domainFrontingIP            string
33
 	domainFrontingIP            string
33
 	domainFrontingProxyProtocol bool
34
 	domainFrontingProxyProtocol bool
66
 	ctx := newStreamContext(p.ctx, p.logger, conn)
67
 	ctx := newStreamContext(p.ctx, p.logger, conn)
67
 	defer ctx.Close()
68
 	defer ctx.Close()
68
 
69
 
70
+	if err := ctx.clientConn.SetDeadline(time.Now().Add(p.handshakeTimeout)); err != nil {
71
+		ctx.logger.WarningError("cannot set handshake timeout", err)
72
+		return
73
+	}
74
+
69
 	stop := context.AfterFunc(ctx, func() {
75
 	stop := context.AfterFunc(ctx, func() {
70
 		ctx.Close()
76
 		ctx.Close()
71
 	})
77
 	})
97
 		return
103
 		return
98
 	}
104
 	}
99
 
105
 
106
+	if err := ctx.clientConn.SetDeadline(time.Time{}); err != nil {
107
+		ctx.logger.WarningError("cannot set deadline", err)
108
+		return
109
+	}
110
+
100
 	if err := p.doTelegramCall(ctx); err != nil {
111
 	if err := p.doTelegramCall(ctx); err != nil {
101
 		ctx.logger.WarningError("cannot dial to telegram", err)
112
 		ctx.logger.WarningError("cannot dial to telegram", err)
102
 		return
113
 		return
346
 		domainFrontingIP:         opts.DomainFrontingIP,
357
 		domainFrontingIP:         opts.DomainFrontingIP,
347
 		tolerateTimeSkewness:     opts.getTolerateTimeSkewness(),
358
 		tolerateTimeSkewness:     opts.getTolerateTimeSkewness(),
348
 		idleTimeout:              opts.getIdleTimeout(),
359
 		idleTimeout:              opts.getIdleTimeout(),
360
+		handshakeTimeout:         opts.getHandshakeTimeout(),
349
 		allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC,
361
 		allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC,
350
 		telegram:                 tg,
362
 		telegram:                 tg,
351
 		doppelGanger: doppel.NewGanger(
363
 		doppelGanger: doppel.NewGanger(

+ 14
- 0
mtglib/proxy_opts.go Просмотреть файл

70
 	// This is an optional setting.
70
 	// This is an optional setting.
71
 	IdleTimeout time.Duration
71
 	IdleTimeout time.Duration
72
 
72
 
73
+	// HandshakeTimeout is a timeout during which all handshake ceremonies must
74
+	// be completed, otherwise this process will be aborted
75
+	//
76
+	// This is an optional setting.
77
+	HandshakeTimeout time.Duration
78
+
73
 	// TolerateTimeSkewness is a time boundary that defines a time range where
79
 	// TolerateTimeSkewness is a time boundary that defines a time range where
74
 	// faketls timestamp is acceptable.
80
 	// faketls timestamp is acceptable.
75
 	//
81
 	//
215
 	return p.PreferIP
221
 	return p.PreferIP
216
 }
222
 }
217
 
223
 
224
+func (p ProxyOpts) getHandshakeTimeout() time.Duration {
225
+	if p.HandshakeTimeout == 0 {
226
+		return DefaultHandshakeTimeout
227
+	}
228
+
229
+	return p.HandshakeTimeout
230
+}
231
+
218
 func (p ProxyOpts) getIdleTimeout() time.Duration {
232
 func (p ProxyOpts) getIdleTimeout() time.Duration {
219
 	if p.IdleTimeout == 0 {
233
 	if p.IdleTimeout == 0 {
220
 		return DefaultIdleTimeout
234
 		return DefaultIdleTimeout

Загрузка…
Отмена
Сохранить