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

Add base wrappers for mtproto

tags/1.0^2
9seconds 6 лет назад
Родитель
Сommit
d431feb0ba
9 измененных файлов: 464 добавлений и 1 удалений
  1. 1
    1
      go.mod
  2. 32
    0
      mtproto/consts.go
  3. 59
    0
      mtproto/proxy_flags.go
  4. 56
    0
      telegram/middle.go
  5. 157
    0
      wrappers/blockcipher.go
  6. 0
    0
      wrappers/conn.go
  7. 159
    0
      wrappers/mtproto_frame.go
  8. 0
    0
      wrappers/obfuscated2.go
  9. 0
    0
      wrappers/stats.go

+ 1
- 1
go.mod Просмотреть файл

@@ -11,7 +11,7 @@ require (
11 11
 	github.com/cespare/xxhash v1.1.0
12 12
 	github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d
13 13
 	github.com/kr/pretty v0.1.0 // indirect
14
-	github.com/pkg/errors v0.8.1 // indirect
14
+	github.com/pkg/errors v0.8.1
15 15
 	github.com/prometheus/client_golang v1.1.0
16 16
 	github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
17 17
 	github.com/spaolacci/murmur3 v1.1.0 // indirect

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

@@ -0,0 +1,32 @@
1
+package mtproto
2
+
3
+// SeqNo* is the number of the sequence which have special meaning for
4
+// the Telegram.
5
+const (
6
+	SeqNoNonce     = -2
7
+	SeqNoHandshake = -1
8
+)
9
+
10
+// Different constants for RPC protocol
11
+var (
12
+	TagCloseExt     = []byte{0xa2, 0x34, 0xb6, 0x5e}
13
+	TagProxyAns     = []byte{0x0d, 0xda, 0x03, 0x44}
14
+	TagSimpleAck    = []byte{0x9b, 0x40, 0xac, 0x3b}
15
+	TagHandshake    = []byte{0xf5, 0xee, 0x82, 0x76}
16
+	TagNonce        = []byte{0xaa, 0x87, 0xcb, 0x7a}
17
+	TagProxyRequest = []byte{0xee, 0xf1, 0xce, 0x36}
18
+
19
+	NonceCryptoAES = []byte{0x01, 0x00, 0x00, 0x00}
20
+
21
+	HandshakeFlags = []byte{0x00, 0x00, 0x00, 0x00}
22
+
23
+	ProxyRequestExtraSize = []byte{0x18, 0x00, 0x00, 0x00}
24
+	ProxyRequestProxyTag  = []byte{0xae, 0x26, 0x1e, 0xdb}
25
+
26
+	HandshakeSenderPID = []byte("IPIPPRPDTIME")
27
+	HandshakePeerPID   = []byte("IPIPPRPDTIME")
28
+
29
+	HandshakeRequest = append(TagHandshake,
30
+		append(HandshakeFlags,
31
+			append(HandshakeSenderPID, HandshakePeerPID...)...)...)
32
+)

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

@@ -0,0 +1,59 @@
1
+package mtproto
2
+
3
+import (
4
+	"encoding/binary"
5
+	"strings"
6
+)
7
+
8
+type proxyRequestFlags uint32
9
+
10
+const (
11
+	proxyRequestFlagsHasAdTag     proxyRequestFlags = 0x8
12
+	proxyRequestFlagsEncrypted    proxyRequestFlags = 0x2
13
+	proxyRequestFlagsMagic        proxyRequestFlags = 0x1000
14
+	proxyRequestFlagsExtMode2     proxyRequestFlags = 0x20000
15
+	proxyRequestFlagsIntermediate proxyRequestFlags = 0x20000000
16
+	proxyRequestFlagsAbdridged    proxyRequestFlags = 0x40000000
17
+	proxyRequestFlagsQuickAck     proxyRequestFlags = 0x80000000
18
+	proxyRequestFlagsPad          proxyRequestFlags = 0x8000000
19
+)
20
+
21
+var proxyRequestFlagsEncryptedPrefix [8]byte
22
+
23
+func (r proxyRequestFlags) Bytes() []byte {
24
+	converted := make([]byte, 4)
25
+	binary.LittleEndian.PutUint32(converted, uint32(r))
26
+
27
+	return converted
28
+}
29
+
30
+func (r proxyRequestFlags) String() string {
31
+	flags := make([]string, 0, 7)
32
+
33
+	if r&proxyRequestFlagsHasAdTag != 0 {
34
+		flags = append(flags, "HAS_AD_TAG")
35
+	}
36
+	if r&proxyRequestFlagsEncrypted != 0 {
37
+		flags = append(flags, "ENCRYPTED")
38
+	}
39
+	if r&proxyRequestFlagsMagic != 0 {
40
+		flags = append(flags, "MAGIC")
41
+	}
42
+	if r&proxyRequestFlagsExtMode2 != 0 {
43
+		flags = append(flags, "EXT_MODE_2")
44
+	}
45
+	if r&proxyRequestFlagsIntermediate != 0 {
46
+		flags = append(flags, "INTERMEDIATE")
47
+	}
48
+	if r&proxyRequestFlagsAbdridged != 0 {
49
+		flags = append(flags, "ABRIDGED")
50
+	}
51
+	if r&proxyRequestFlagsQuickAck != 0 {
52
+		flags = append(flags, "QUICK_ACK")
53
+	}
54
+	if r&proxyRequestFlagsPad != 0 {
55
+		flags = append(flags, "PAD")
56
+	}
57
+
58
+	return strings.Join(flags, " | ")
59
+}

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

@@ -0,0 +1,56 @@
1
+package telegram
2
+
3
+import (
4
+	"context"
5
+	"fmt"
6
+	"sync"
7
+
8
+	"github.com/9seconds/mtg/conntypes"
9
+	"github.com/9seconds/mtg/telegram/api"
10
+	"github.com/9seconds/mtg/wrappers"
11
+)
12
+
13
+type middleTelegram struct {
14
+	baseTelegram
15
+
16
+	secret []byte
17
+	mutex  sync.RWMutex
18
+}
19
+
20
+func (m *middleTelegram) update() error {
21
+	secret, err := api.Secret()
22
+	if err != nil {
23
+		return fmt.Errorf("cannot fetch secret: %w", err)
24
+	}
25
+
26
+	v4Addresses, v4DefaultDC, err := api.AddressesV4()
27
+	if err != nil {
28
+		return fmt.Errorf("cannot fetch addresses for ipv4: %w", err)
29
+	}
30
+
31
+	v6Addresses, v6DefaultDC, err := api.AddressesV6()
32
+	if err != nil {
33
+		return fmt.Errorf("cannot fetch addresses for ipv6: %w", err)
34
+	}
35
+
36
+	m.mutex.Lock()
37
+	m.secret = secret
38
+	m.v4DefaultDC = v4DefaultDC
39
+	m.V6DefaultDC = v6DefaultDC
40
+	m.v4Addresses = v4Addresses
41
+	m.v6Addresses = v6Addresses
42
+	m.mutex.Unlock()
43
+
44
+	return nil
45
+}
46
+
47
+func (m *middleTelegram) Dial(ctx context.Context,
48
+	cancel context.CancelFunc,
49
+	dc conntypes.DC,
50
+	protocol conntypes.ConnectionProtocol) (wrappers.StreamReadWriteCloser, error) {
51
+	if dc == 0 {
52
+		dc = conntypes.DCDefaultIdx
53
+	}
54
+
55
+	return m.baseTelegram.dial(ctx, cancel, dc, protocol)
56
+}

+ 157
- 0
wrappers/blockcipher.go Просмотреть файл

@@ -0,0 +1,157 @@
1
+package wrappers
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/aes"
6
+	"crypto/cipher"
7
+	"errors"
8
+	"fmt"
9
+	"net"
10
+	"time"
11
+
12
+	"go.uber.org/zap"
13
+)
14
+
15
+const blockCipherReadCurrentDataBufferSize = 1024 + 1 // +1 because telegram operates with blocks mod 4
16
+
17
+type wrapperBlockCipher struct {
18
+	buf bytes.Buffer
19
+
20
+	parent    StreamReadWriteCloser
21
+	encryptor cipher.BlockMode
22
+	decryptor cipher.BlockMode
23
+}
24
+
25
+func (w *wrapperBlockCipher) Write(p []byte) (int, error) {
26
+	encrypted, err := w.encrypt(p)
27
+	if err != nil {
28
+		return 0, err
29
+	}
30
+	return w.parent.Write(encrypted)
31
+}
32
+
33
+func (w *wrapperBlockCipher) WriteTimeout(p []byte, timeout time.Duration) (int, error) {
34
+	encrypted, err := w.encrypt(p)
35
+	if err != nil {
36
+		return 0, err
37
+	}
38
+	return w.parent.WriteTimeout(encrypted, timeout)
39
+}
40
+
41
+func (w *wrapperBlockCipher) Read(p []byte) (int, error) {
42
+	return w.read(p, readAll)
43
+
44
+}
45
+
46
+func (w *wrapperBlockCipher) ReadTimeout(p []byte, timeout time.Duration) (int, error) {
47
+	return w.read(p, readAllTimeout(timeout))
48
+}
49
+
50
+func (w *wrapperBlockCipher) read(p []byte, reader func(StreamReadWriteCloser) ([]byte, error)) (int, error) {
51
+	if w.buf.Len() > 0 {
52
+		return w.flush(p)
53
+	}
54
+
55
+	var buf []byte
56
+	for len(buf) == 0 || len(buf)%aes.BlockSize != 0 {
57
+		rv, err := reader(w.parent)
58
+		if err != nil {
59
+			return 0, fmt.Errorf("cannot read from socket: %w", err)
60
+		}
61
+		buf = append(buf, rv...)
62
+	}
63
+
64
+	w.decryptor.CryptBlocks(buf, buf)
65
+	w.buf.Write(buf)
66
+
67
+	return w.flush(p)
68
+}
69
+
70
+func (w *wrapperBlockCipher) flush(p []byte) (int, error) {
71
+	if w.buf.Len() > len(p) {
72
+		return w.buf.Read(p)
73
+	}
74
+
75
+	sizeToReturn := w.buf.Len()
76
+	copy(p, w.buf.Bytes())
77
+	w.buf.Reset()
78
+
79
+	return sizeToReturn, nil
80
+}
81
+
82
+func (w *wrapperBlockCipher) encrypt(p []byte) ([]byte, error) {
83
+	if len(p)%aes.BlockSize > 0 {
84
+		return nil, fmt.Errorf("incorrect block size %d", len(p))
85
+	}
86
+
87
+	encrypted := make([]byte, len(p))
88
+	w.encryptor.CryptBlocks(encrypted, p)
89
+
90
+	return encrypted, nil
91
+}
92
+
93
+func readAll(src StreamReadWriteCloser) (rv []byte, err error) {
94
+	buf := make([]byte, blockCipherReadCurrentDataBufferSize)
95
+	n := blockCipherReadCurrentDataBufferSize
96
+
97
+	for n == len(buf) {
98
+		n, err = src.Read(buf)
99
+		if err != nil {
100
+			return nil, err
101
+		}
102
+		rv = append(rv, buf[:n]...)
103
+	}
104
+
105
+	return rv, nil
106
+}
107
+
108
+func readAllTimeout(timeout time.Duration) func(StreamReadWriteCloser) ([]byte, error) {
109
+	return func(src StreamReadWriteCloser) (rv []byte, err error) {
110
+		tmo := timeout
111
+		buf := make([]byte, blockCipherReadCurrentDataBufferSize)
112
+		n := blockCipherReadCurrentDataBufferSize
113
+
114
+		for n == len(buf) {
115
+			if tmo <= 0 {
116
+				return nil, errors.New("timeout")
117
+			}
118
+			startTime := time.Now()
119
+			n, err = src.ReadTimeout(buf, tmo)
120
+			if err != nil {
121
+				return nil, err
122
+			}
123
+			rv = append(rv, buf[:n]...)
124
+			tmo -= time.Since(startTime)
125
+		}
126
+
127
+		return rv, nil
128
+	}
129
+}
130
+
131
+func (w *wrapperBlockCipher) Close() error {
132
+	return w.parent.Close()
133
+}
134
+
135
+func (w *wrapperBlockCipher) Conn() net.Conn {
136
+	return w.parent.Conn()
137
+}
138
+
139
+func (w *wrapperBlockCipher) Logger() *zap.SugaredLogger {
140
+	return w.parent.Logger().Named("block-cipher")
141
+}
142
+
143
+func (w *wrapperBlockCipher) LocalAddr() *net.TCPAddr {
144
+	return w.parent.LocalAddr()
145
+}
146
+
147
+func (w *wrapperBlockCipher) RemoteAddr() *net.TCPAddr {
148
+	return w.parent.RemoteAddr()
149
+}
150
+
151
+func NewBlockCipher(parent StreamReadWriteCloser, encryptor, decryptor cipher.BlockMode) StreamReadWriteCloser {
152
+	return &wrapperBlockCipher{
153
+		parent:    parent,
154
+		encryptor: encryptor,
155
+		decryptor: decryptor,
156
+	}
157
+}

wrappers/wrapper_conn.go → wrappers/conn.go Просмотреть файл


+ 159
- 0
wrappers/mtproto_frame.go Просмотреть файл

@@ -0,0 +1,159 @@
1
+package wrappers
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/aes"
6
+	"encoding/binary"
7
+	"fmt"
8
+	"hash/crc32"
9
+	"io"
10
+	"io/ioutil"
11
+	"net"
12
+
13
+	"go.uber.org/zap"
14
+)
15
+
16
+const (
17
+	mtprotoFrameMinMessageLength = 12
18
+	mtprotoFrameMaxMessageLength = 16777216
19
+)
20
+
21
+var mtprotoFramePadding = []byte{0x04, 0x00, 0x00, 0x00}
22
+
23
+// MTProtoFrame is a wrapper which converts written data to the MTProtoFrame.
24
+// The format of the frame:
25
+//
26
+// [ MSGLEN(4) | SEQNO(4) | MSG(...) | CRC32(4) | PADDING(4*x) ]
27
+//
28
+// MSGLEN is the length of the message + len of seqno and msglen.
29
+// SEQNO is the number of frame in the receive/send sequence. If client
30
+//   sends a message with SeqNo 18, it has to receive message with SeqNo 18.
31
+// MSG is the data which has to be written
32
+// CRC32 is the CRC32 checksum of MSGLEN + SEQNO + MSG
33
+// PADDING is custom padding schema to complete frame length to such that
34
+//    len(frame) % 16 == 0
35
+type wrapperMtprotoFrame struct {
36
+	parent     StreamReadWriteCloser
37
+	logger     *zap.SugaredLogger
38
+	readSeqNo  int32
39
+	writeSeqNo int32
40
+}
41
+
42
+func (w *wrapperMtprotoFrame) Read() (Packet, error) {
43
+	buf := &bytes.Buffer{}
44
+	sum := crc32.NewIEEE()
45
+	writer := io.MultiWriter(buf, sum)
46
+
47
+	for {
48
+		buf.Reset()
49
+		sum.Reset()
50
+		if _, err := io.CopyN(writer, w.parent, 4); err != nil {
51
+			return nil, fmt.Errorf("cannot read frame padding: %w", err)
52
+		}
53
+		if !bytes.Equal(buf.Bytes(), mtprotoFramePadding) {
54
+			break
55
+		}
56
+	}
57
+
58
+	messageLength := binary.LittleEndian.Uint32(buf.Bytes())
59
+	w.logger.Debugw("Read MTProto frame",
60
+		"messageLength", messageLength,
61
+		"sequence_number", w.readSeqNo,
62
+	)
63
+	if messageLength%4 != 0 || messageLength < mtprotoFrameMinMessageLength ||
64
+		messageLength > mtprotoFrameMaxMessageLength {
65
+		return nil, fmt.Errorf("Incorrect frame message length %d", messageLength)
66
+	}
67
+
68
+	buf.Reset()
69
+	buf.Grow(int(messageLength) - 4 - 4)
70
+	if _, err := io.CopyN(writer, w.parent, int64(messageLength)-4-4); err != nil {
71
+		return nil, fmt.Errorf("cannot read the message frame: %w", err)
72
+	}
73
+
74
+	var seqNo int32
75
+	binary.Read(buf, binary.LittleEndian, &seqNo) // nolint: errcheck, gosec
76
+	if seqNo != w.readSeqNo {
77
+		return nil, fmt.Errorf("unexpected sequence number %d (wait for %d)", seqNo, w.readSeqNo)
78
+	}
79
+
80
+	data, _ := ioutil.ReadAll(buf) // nolint: gosec
81
+	buf.Reset()
82
+	// write to buf, not to writer. This is because we are going to fetch
83
+	// crc32 checksum.
84
+	if _, err := io.CopyN(buf, w.parent, 4); err != nil {
85
+		return nil, fmt.Errorf("cannot read checksum: %w", err)
86
+	}
87
+
88
+	checksum := binary.LittleEndian.Uint32(buf.Bytes())
89
+	if checksum != sum.Sum32() {
90
+		return nil, fmt.Errorf("CRC32 checksum mismatch. wait for %d, got %d", sum.Sum32(), checksum)
91
+	}
92
+
93
+	w.logger.Debugw("Read MTProto frame",
94
+		"messageLength", messageLength,
95
+		"sequence_number", w.readSeqNo,
96
+		"dataLength", len(data),
97
+		"checksum", checksum,
98
+	)
99
+	w.readSeqNo++
100
+
101
+	return data, nil
102
+}
103
+
104
+func (w *wrapperMtprotoFrame) Write(p Packet) error {
105
+	messageLength := 4 + 4 + len(p) + 4
106
+	paddingLength := (aes.BlockSize - messageLength%aes.BlockSize) % aes.BlockSize
107
+
108
+	buf := &bytes.Buffer{}
109
+	buf.Grow(messageLength + paddingLength)
110
+
111
+	binary.Write(buf, binary.LittleEndian, uint32(messageLength))
112
+	binary.Write(buf, binary.LittleEndian, w.writeSeqNo)
113
+	buf.Write(p)
114
+
115
+	checksum := crc32.ChecksumIEEE(buf.Bytes())
116
+	binary.Write(buf, binary.LittleEndian, checksum)
117
+	buf.Write(bytes.Repeat(mtprotoFramePadding, paddingLength/4))
118
+
119
+	w.logger.Debugw("Write MTProto frame",
120
+		"length", len(p),
121
+		"sequence_number", w.writeSeqNo,
122
+		"crc32", checksum,
123
+		"frame_length", buf.Len(),
124
+	)
125
+	w.writeSeqNo++
126
+
127
+	_, err := w.parent.Write(buf.Bytes())
128
+
129
+	return err
130
+}
131
+
132
+func (w *wrapperMtprotoFrame) Close() error {
133
+	return w.parent.Close()
134
+}
135
+
136
+func (w *wrapperMtprotoFrame) Conn() net.Conn {
137
+	return w.parent.Conn()
138
+}
139
+
140
+func (w *wrapperMtprotoFrame) Logger() *zap.SugaredLogger {
141
+	return w.logger
142
+}
143
+
144
+func (w *wrapperMtprotoFrame) LocalAddr() *net.TCPAddr {
145
+	return w.parent.LocalAddr()
146
+}
147
+
148
+func (w *wrapperMtprotoFrame) RemoteAddr() *net.TCPAddr {
149
+	return w.parent.RemoteAddr()
150
+}
151
+
152
+func NewMtprotoFrame(parent StreamReadWriteCloser, seqNo int32) PacketReadWriteCloser {
153
+	return &wrapperMtprotoFrame{
154
+		parent:     parent,
155
+		logger:     parent.Logger().Named("mtproto-frame"),
156
+		readSeqNo:  seqNo,
157
+		writeSeqNo: seqNo,
158
+	}
159
+}

wrappers/wrapper_obfuscated2.go → wrappers/obfuscated2.go Просмотреть файл


wrappers/wrapper_stats.go → wrappers/stats.go Просмотреть файл


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