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

Add background update for middle proxy

tags/1.0^2
9seconds 6 лет назад
Родитель
Сommit
7827d1255a

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

@@ -25,8 +25,4 @@ var (
25 25
 
26 26
 	HandshakeSenderPID = []byte("IPIPPRPDTIME")
27 27
 	HandshakePeerPID   = []byte("IPIPPRPDTIME")
28
-
29
-	HandshakeRequest = append(TagHandshake,
30
-		append(HandshakeFlags,
31
-			append(HandshakeSenderPID, HandshakePeerPID...)...)...)
32 28
 )

+ 7
- 0
mtproto/rpc/handshake_request.go Просмотреть файл

@@ -0,0 +1,7 @@
1
+package rpc
2
+
3
+import "github.com/9seconds/mtg/mtproto"
4
+
5
+var HandshakeRequest = append(mtproto.TagHandshake,
6
+	append(mtproto.HandshakeFlags,
7
+		append(mtproto.HandshakeSenderPID, mtproto.HandshakePeerPID...)...)...)

+ 55
- 0
mtproto/rpc/handshake_response.go Просмотреть файл

@@ -0,0 +1,55 @@
1
+package rpc
2
+
3
+import (
4
+	"bytes"
5
+	"errors"
6
+	"fmt"
7
+
8
+	"github.com/9seconds/mtg/mtproto"
9
+)
10
+
11
+type HandshakeResponse struct {
12
+	Type      []byte
13
+	Flags     []byte
14
+	SenderPID []byte
15
+	PeerPID   []byte
16
+}
17
+
18
+// Bytes returns a serialized handshake response.
19
+func (r *HandshakeResponse) Bytes() []byte {
20
+	buf := &bytes.Buffer{}
21
+
22
+	buf.Write(r.Type)      // nolint: gosec
23
+	buf.Write(r.Flags)     // nolint: gosec
24
+	buf.Write(r.SenderPID) // nolint: gosec
25
+	buf.Write(r.PeerPID)   // nolint: gosec
26
+
27
+	return buf.Bytes()
28
+}
29
+
30
+// Valid checks that handshake response compliments request.
31
+func (r *HandshakeResponse) Valid() error {
32
+	if !bytes.Equal(r.Type, mtproto.TagHandshake) {
33
+		return errors.New("Unexpected handshake tag")
34
+	}
35
+	if !bytes.Equal(r.PeerPID, mtproto.HandshakeSenderPID) {
36
+		return errors.New("Incorrect sender PID")
37
+	}
38
+
39
+	return nil
40
+}
41
+
42
+// NewHandshakeResponse constructs new handshake response from the given
43
+// data.
44
+func NewHandshakeResponse(data []byte) (*HandshakeResponse, error) {
45
+	if len(data) != 32 {
46
+		return nil, fmt.Errorf("Incorrect handshake response length %d", len(data))
47
+	}
48
+
49
+	return &HandshakeResponse{
50
+		Type:      data[:4],
51
+		Flags:     data[4:8],
52
+		SenderPID: data[8:20],
53
+		PeerPID:   data[20:],
54
+	}, nil
55
+}

+ 51
- 0
mtproto/rpc/nonce_request.go Просмотреть файл

@@ -0,0 +1,51 @@
1
+package rpc
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/rand"
6
+	"encoding/binary"
7
+	"fmt"
8
+	"time"
9
+
10
+	"github.com/9seconds/mtg/mtproto"
11
+)
12
+
13
+type NonceRequest struct {
14
+	KeySelector []byte
15
+	CryptoTS    []byte
16
+	Nonce       []byte
17
+}
18
+
19
+// Bytes returns serialized nonce request.
20
+func (r *NonceRequest) Bytes() []byte {
21
+	buf := &bytes.Buffer{}
22
+
23
+	buf.Write(mtproto.TagNonce)       // nolint: gosec
24
+	buf.Write(r.KeySelector)          // nolint: gosec
25
+	buf.Write(mtproto.NonceCryptoAES) // nolint: gosec
26
+	buf.Write(r.CryptoTS)             // nolint: gosec
27
+	buf.Write(r.Nonce)                // nolint: gosec
28
+
29
+	return buf.Bytes()
30
+}
31
+
32
+// NewNonceRequest builds new none request based on proxy secret.
33
+func NewNonceRequest(proxySecret []byte) (*NonceRequest, error) {
34
+	nonce := make([]byte, 16)
35
+	keySelector := make([]byte, 4)
36
+	cryptoTS := make([]byte, 4)
37
+
38
+	if _, err := rand.Read(nonce); err != nil {
39
+		return nil, fmt.Errorf("cannot generate nonce: %w", err)
40
+	}
41
+	copy(keySelector, proxySecret)
42
+
43
+	timestamp := time.Now().Truncate(time.Second).Unix() % 4294967296 // 256 ^ 4 - do not know how to name
44
+	binary.LittleEndian.PutUint32(cryptoTS, uint32(timestamp))
45
+
46
+	return &NonceRequest{
47
+		KeySelector: keySelector,
48
+		CryptoTS:    cryptoTS,
49
+		Nonce:       nonce,
50
+	}, nil
51
+}

+ 60
- 0
mtproto/rpc/nonce_response.go Просмотреть файл

@@ -0,0 +1,60 @@
1
+package rpc
2
+
3
+import (
4
+	"bytes"
5
+	"errors"
6
+	"fmt"
7
+
8
+	"github.com/9seconds/mtg/mtproto"
9
+)
10
+
11
+type NonceResponse struct {
12
+	NonceRequest
13
+
14
+	Type   []byte
15
+	Crypto []byte
16
+}
17
+
18
+// Bytes returns serialized form of the nonce response.
19
+func (r *NonceResponse) Bytes() []byte {
20
+	buf := &bytes.Buffer{}
21
+
22
+	buf.Write(r.Type)        // nolint: gosec
23
+	buf.Write(r.KeySelector) // nolint: gosec
24
+	buf.Write(r.Crypto)      // nolint: gosec
25
+	buf.Write(r.CryptoTS)    // nolint: gosec
26
+	buf.Write(r.Nonce)       // nolint: gosec
27
+
28
+	return buf.Bytes()
29
+}
30
+
31
+func (r *NonceResponse) Valid(req *NonceRequest) error {
32
+	if !bytes.Equal(r.Type, mtproto.TagNonce) {
33
+		return errors.New("Unexpected RPC type")
34
+	}
35
+	if !bytes.Equal(r.Crypto, mtproto.NonceCryptoAES) {
36
+		return errors.New("Unexpected crypto type")
37
+	}
38
+	if !bytes.Equal(r.KeySelector, req.KeySelector) {
39
+		return errors.New("Unexpected key selector")
40
+	}
41
+
42
+	return nil
43
+}
44
+
45
+// NewNonceResponse build new nonce response based on the given data.
46
+func NewNonceResponse(data []byte) (*NonceResponse, error) {
47
+	if len(data) != 32 {
48
+		return nil, fmt.Errorf("Unexpected message length %d", len(data))
49
+	}
50
+
51
+	return &NonceResponse{
52
+		NonceRequest: NonceRequest{
53
+			KeySelector: data[4:8],
54
+			CryptoTS:    data[12:16],
55
+			Nonce:       data[16:],
56
+		},
57
+		Type:   data[:4],
58
+		Crypto: data[8:12],
59
+	}, nil
60
+}

mtproto/proxy_flags.go → mtproto/rpc/proxy_flags.go Просмотреть файл

@@ -1,4 +1,4 @@
1
-package mtproto
1
+package rpc
2 2
 
3 3
 import (
4 4
 	"encoding/binary"

+ 32
- 0
telegram/middle.go Просмотреть файл

@@ -3,13 +3,19 @@ package telegram
3 3
 import (
4 4
 	"context"
5 5
 	"fmt"
6
+	"net"
6 7
 	"sync"
8
+	"time"
9
+
10
+	"go.uber.org/zap"
7 11
 
8 12
 	"github.com/9seconds/mtg/conntypes"
9 13
 	"github.com/9seconds/mtg/telegram/api"
10 14
 	"github.com/9seconds/mtg/wrappers"
11 15
 )
12 16
 
17
+const middleTelegramBackgroundUpdateEvery = time.Hour
18
+
13 19
 type middleTelegram struct {
14 20
 	baseTelegram
15 21
 
@@ -44,6 +50,15 @@ func (m *middleTelegram) update() error {
44 50
 	return nil
45 51
 }
46 52
 
53
+func (m *middleTelegram) backgroundUpdate() {
54
+	logger := zap.S().Named("telegram")
55
+	for range time.Tick(middleTelegramBackgroundUpdateEvery) {
56
+		if err := m.update(); err != nil {
57
+			logger.Warnw("Cannot update Telegram proxies", "error", err)
58
+		}
59
+	}
60
+}
61
+
47 62
 func (m *middleTelegram) Dial(ctx context.Context,
48 63
 	cancel context.CancelFunc,
49 64
 	dc conntypes.DC,
@@ -52,5 +67,22 @@ func (m *middleTelegram) Dial(ctx context.Context,
52 67
 		dc = conntypes.DCDefaultIdx
53 68
 	}
54 69
 
70
+	m.mutex.RLock()
71
+	defer m.mutex.RUnlock()
72
+
55 73
 	return m.baseTelegram.dial(ctx, cancel, dc, protocol)
56 74
 }
75
+
76
+func NewMiddleTelegram() Telegram {
77
+	tg := &middleTelegram{
78
+		baseTelegram: baseTelegram{
79
+			dialer: net.Dialer{Timeout: telegramDialTimeout},
80
+		},
81
+	}
82
+	if err := tg.update(); err != nil {
83
+		panic(err)
84
+	}
85
+	go tg.backgroundUpdate()
86
+
87
+	return tg
88
+}

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