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

Merge pull request #6 from 9seconds/syncpool

Use syncpool
tags/0.9
Sergey Arkhipov 7 лет назад
Родитель
Сommit
4e8e5a51d6
Аккаунт пользователя с таким Email не найден

+ 2
- 2
Gopkg.lock Просмотреть файл

43
 [[projects]]
43
 [[projects]]
44
   name = "github.com/stretchr/testify"
44
   name = "github.com/stretchr/testify"
45
   packages = ["assert"]
45
   packages = ["assert"]
46
-  revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
47
-  version = "v1.2.1"
46
+  revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
47
+  version = "v1.2.2"
48
 
48
 
49
 [[projects]]
49
 [[projects]]
50
   name = "go.uber.org/atomic"
50
   name = "go.uber.org/atomic"

+ 1
- 0
client/direct.go Просмотреть файл

18
 	if err != nil {
18
 	if err != nil {
19
 		return 0, nil, errors.Annotate(err, "Cannot extract frame")
19
 		return 0, nil, errors.Annotate(err, "Cannot extract frame")
20
 	}
20
 	}
21
+	defer obfuscated2.ReturnFrame(frame)
21
 
22
 
22
 	obfs2, dc, err := obfuscated2.ParseObfuscated2ClientFrame(conf.Secret, frame)
23
 	obfs2, dc, err := obfuscated2.ParseObfuscated2ClientFrame(conf.Secret, frame)
23
 	if err != nil {
24
 	if err != nil {

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

5
 import (
5
 import (
6
 	"encoding/json"
6
 	"encoding/json"
7
 	"io"
7
 	"io"
8
+	"math/rand"
8
 	"os"
9
 	"os"
10
+	"time"
9
 
11
 
10
 	"go.uber.org/zap"
12
 	"go.uber.org/zap"
11
 	"go.uber.org/zap/zapcore"
13
 	"go.uber.org/zap/zapcore"
80
 )
82
 )
81
 
83
 
82
 func main() {
84
 func main() {
85
+	rand.Seed(time.Now().UTC().UnixNano())
86
+
83
 	app.Version(version)
87
 	app.Version(version)
84
 	kingpin.MustParse(app.Parse(os.Args[1:]))
88
 	kingpin.MustParse(app.Parse(os.Args[1:]))
85
 
89
 

+ 16
- 10
obfuscated2/frame.go Просмотреть файл

68
 
68
 
69
 // Invert inverts frame for extracting encryption keys. Pkease check that link:
69
 // Invert inverts frame for extracting encryption keys. Pkease check that link:
70
 // https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
70
 // https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
71
-func (f Frame) Invert() Frame {
72
-	reversed := make(Frame, FrameLen)
73
-	copy(reversed, f)
71
+func (f Frame) Invert() *Frame {
72
+	reversed := MakeFrame()
73
+	copy(*reversed, f)
74
 
74
 
75
 	for i := 0; i < frameLenKey+frameLenIV; i++ {
75
 	for i := 0; i < frameLenKey+frameLenIV; i++ {
76
-		reversed[frameOffsetFirst+i] = f[frameOffsetIV-1-i]
76
+		(*reversed)[frameOffsetFirst+i] = f[frameOffsetIV-1-i]
77
 	}
77
 	}
78
 
78
 
79
 	return reversed
79
 	return reversed
80
 }
80
 }
81
 
81
 
82
 // ExtractFrame extracts exact obfuscated2 handshake frame from given reader.
82
 // ExtractFrame extracts exact obfuscated2 handshake frame from given reader.
83
-func ExtractFrame(conn io.Reader) (Frame, error) {
84
-	buf := &bytes.Buffer{}
83
+func ExtractFrame(conn io.Reader) (*Frame, error) {
84
+	frame := MakeFrame()
85
+	buf := bytes.NewBuffer(*frame)
86
+	buf.Reset()
87
+
85
 	if _, err := io.CopyN(buf, conn, FrameLen); err != nil {
88
 	if _, err := io.CopyN(buf, conn, FrameLen); err != nil {
89
+		ReturnFrame(frame)
86
 		return nil, errors.Annotate(err, "Cannot extract obfuscated header")
90
 		return nil, errors.Annotate(err, "Cannot extract obfuscated header")
87
 	}
91
 	}
92
+	copy(*frame, buf.Bytes())
88
 
93
 
89
-	return Frame(buf.Bytes()), nil
94
+	return frame, nil
90
 }
95
 }
91
 
96
 
92
-func generateFrame() Frame {
93
-	data := make(Frame, FrameLen)
97
+func generateFrame() *Frame {
98
+	frame := MakeFrame()
99
+	data := *frame
94
 
100
 
95
 	for {
101
 	for {
96
 		if _, err := rand.Read(data); err != nil {
102
 		if _, err := rand.Read(data); err != nil {
112
 
118
 
113
 		copy(data.Magic(), tgMagicBytes)
119
 		copy(data.Magic(), tgMagicBytes)
114
 
120
 
115
-		return data
121
+		return frame
116
 	}
122
 	}
117
 }
123
 }

+ 24
- 0
obfuscated2/frame_pool.go Просмотреть файл

1
+package obfuscated2
2
+
3
+import "sync"
4
+
5
+var framePool sync.Pool
6
+
7
+// MakeFrame returns new pointer to the handshake frame.
8
+func MakeFrame() *Frame {
9
+	return framePool.Get().(*Frame)
10
+}
11
+
12
+// ReturnFrame returns pointer to the handshake frame back to the pool.
13
+func ReturnFrame(f *Frame) {
14
+	framePool.Put(f)
15
+}
16
+
17
+func init() {
18
+	framePool = sync.Pool{
19
+		New: func() interface{} {
20
+			data := make(Frame, FrameLen)
21
+			return &data
22
+		},
23
+	}
24
+}

+ 5
- 4
obfuscated2/frame_test.go Просмотреть файл

1
 package obfuscated2
1
 package obfuscated2
2
 
2
 
3
 import (
3
 import (
4
+	"bytes"
4
 	"testing"
5
 	"testing"
5
 
6
 
6
 	"github.com/stretchr/testify/assert"
7
 	"github.com/stretchr/testify/assert"
47
 
48
 
48
 func TestFrameDoubleInvert(t *testing.T) {
49
 func TestFrameDoubleInvert(t *testing.T) {
49
 	frame := makeFrame()
50
 	frame := makeFrame()
50
-	assert.Equal(t, frame, frame.Invert().Invert())
51
+	assert.True(t, bytes.Equal(frame, *frame.Invert().Invert()))
51
 }
52
 }
52
 
53
 
53
 func TestFrameInvert(t *testing.T) {
54
 func TestFrameInvert(t *testing.T) {
54
 	frame := makeFrame()
55
 	frame := makeFrame()
55
 	reversed := frame.Invert()
56
 	reversed := frame.Invert()
56
 
57
 
57
-	assert.Exactly(t, frame[:8], reversed[:8])
58
-	assert.Exactly(t, frame[56:], reversed[56:])
58
+	assert.Exactly(t, frame[:8], (*reversed)[:8])
59
+	assert.Exactly(t, frame[56:], (*reversed)[56:])
59
 
60
 
60
 	toCompare := make([]byte, 48)
61
 	toCompare := make([]byte, 48)
61
 	for i := 0; i < 48; i++ {
62
 	for i := 0; i < 48; i++ {
62
 		toCompare[i] = frame[55-i]
63
 		toCompare[i] = frame[55-i]
63
 	}
64
 	}
64
-	assert.Equal(t, []byte(reversed[8:56]), toCompare)
65
+	assert.Equal(t, []byte((*reversed)[8:56]), toCompare)
65
 }
66
 }
66
 
67
 
67
 func TestFrameGenerateValid(t *testing.T) {
68
 func TestFrameGenerateValid(t *testing.T) {

+ 10
- 8
obfuscated2/obfuscated2.go Просмотреть файл

19
 // details: http://telegra.ph/telegram-blocks-wtf-05-26
19
 // details: http://telegra.ph/telegram-blocks-wtf-05-26
20
 //
20
 //
21
 // Beware, link above is in russian.
21
 // Beware, link above is in russian.
22
-func ParseObfuscated2ClientFrame(secret []byte, frame Frame) (*Obfuscated2, int16, error) {
22
+func ParseObfuscated2ClientFrame(secret []byte, frame *Frame) (*Obfuscated2, int16, error) {
23
 	decHasher := sha256.New()
23
 	decHasher := sha256.New()
24
 	decHasher.Write(frame.Key()) // nolint: errcheck
24
 	decHasher.Write(frame.Key()) // nolint: errcheck
25
 	decHasher.Write(secret)      // nolint: errcheck
25
 	decHasher.Write(secret)      // nolint: errcheck
31
 	encHasher.Write(secret)              // nolint: errcheck
31
 	encHasher.Write(secret)              // nolint: errcheck
32
 	encryptor := makeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
32
 	encryptor := makeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
33
 
33
 
34
-	decryptedFrame := make(Frame, FrameLen)
35
-	decryptor.XORKeyStream(decryptedFrame, frame)
34
+	decryptedFrame := MakeFrame()
35
+	defer ReturnFrame(decryptedFrame)
36
+	decryptor.XORKeyStream(*decryptedFrame, *frame)
36
 	if !decryptedFrame.Valid() {
37
 	if !decryptedFrame.Valid() {
37
 		return nil, 0, errors.New("Unknown protocol")
38
 		return nil, 0, errors.New("Unknown protocol")
38
 	}
39
 	}
48
 // MakeTelegramObfuscated2Frame creates new handshake frame to send to
49
 // MakeTelegramObfuscated2Frame creates new handshake frame to send to
49
 // Telegram.
50
 // Telegram.
50
 // https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
51
 // https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
51
-func MakeTelegramObfuscated2Frame() (*Obfuscated2, Frame) {
52
+func MakeTelegramObfuscated2Frame() (*Obfuscated2, *Frame) {
52
 	frame := generateFrame()
53
 	frame := generateFrame()
53
 
54
 
54
 	encryptor := makeStreamCipher(frame.Key(), frame.IV())
55
 	encryptor := makeStreamCipher(frame.Key(), frame.IV())
55
 	decryptorFrame := frame.Invert()
56
 	decryptorFrame := frame.Invert()
56
 	decryptor := makeStreamCipher(decryptorFrame.Key(), decryptorFrame.IV())
57
 	decryptor := makeStreamCipher(decryptorFrame.Key(), decryptorFrame.IV())
57
 
58
 
58
-	copyFrame := make(Frame, frameOffsetIV)
59
-	copy(copyFrame, frame)
60
-	encryptor.XORKeyStream(frame, frame)
61
-	copy(frame, copyFrame)
59
+	copyFrame := MakeFrame()
60
+	defer ReturnFrame(copyFrame)
61
+	copy((*copyFrame)[:frameOffsetIV], (*frame)[:frameOffsetIV])
62
+	encryptor.XORKeyStream(*frame, *frame)
63
+	copy((*frame)[:frameOffsetIV], (*copyFrame)[:frameOffsetIV])
62
 
64
 
63
 	obfs := &Obfuscated2{
65
 	obfs := &Obfuscated2{
64
 		Decryptor: decryptor,
66
 		Decryptor: decryptor,

+ 5
- 5
obfuscated2/obfuscated2_test.go Просмотреть файл

12
 	decryptor := makeStreamCipher(frame.Key(), frame.IV())
12
 	decryptor := makeStreamCipher(frame.Key(), frame.IV())
13
 
13
 
14
 	decrypted := make(Frame, FrameLen)
14
 	decrypted := make(Frame, FrameLen)
15
-	decryptor.XORKeyStream(decrypted, frame)
15
+	decryptor.XORKeyStream(decrypted, *frame)
16
 
16
 
17
 	assert.True(t, decrypted.Valid())
17
 	assert.True(t, decrypted.Valid())
18
 }
18
 }
42
 
42
 
43
 	encryptor := makeStreamCipher(clientKey, clientFrame.IV())
43
 	encryptor := makeStreamCipher(clientKey, clientFrame.IV())
44
 	encrypted := make(Frame, FrameLen)
44
 	encrypted := make(Frame, FrameLen)
45
-	encryptor.XORKeyStream(encrypted, clientFrame)
46
-	copy(encrypted[:56], clientFrame[:56])
45
+	encryptor.XORKeyStream(encrypted, *clientFrame)
46
+	copy(encrypted[:56], (*clientFrame)[:56])
47
 
47
 
48
 	invertedClientFrame := clientFrame.Invert()
48
 	invertedClientFrame := clientFrame.Invert()
49
 	clientHasher = sha256.New()
49
 	clientHasher = sha256.New()
52
 	invertedClientKey := clientHasher.Sum(nil)
52
 	invertedClientKey := clientHasher.Sum(nil)
53
 	clientDecryptor := makeStreamCipher(invertedClientKey, invertedClientFrame.IV())
53
 	clientDecryptor := makeStreamCipher(invertedClientKey, invertedClientFrame.IV())
54
 
54
 
55
-	clientObfs, _, err := ParseObfuscated2ClientFrame(secret, encrypted)
55
+	clientObfs, _, err := ParseObfuscated2ClientFrame(secret, &encrypted)
56
 	assert.Nil(t, err)
56
 	assert.Nil(t, err)
57
 
57
 
58
 	tgObfs, tgFrame := MakeTelegramObfuscated2Frame()
58
 	tgObfs, tgFrame := MakeTelegramObfuscated2Frame()
59
 	tgDecryptor := makeStreamCipher(tgFrame.Key(), tgFrame.IV())
59
 	tgDecryptor := makeStreamCipher(tgFrame.Key(), tgFrame.IV())
60
 	decrypted := make(Frame, FrameLen)
60
 	decrypted := make(Frame, FrameLen)
61
-	tgDecryptor.XORKeyStream(decrypted, tgFrame)
61
+	tgDecryptor.XORKeyStream(decrypted, *tgFrame)
62
 	assert.True(t, decrypted.Valid())
62
 	assert.True(t, decrypted.Valid())
63
 
63
 
64
 	tgInvertedFrame := tgFrame.Invert()
64
 	tgInvertedFrame := tgFrame.Invert()

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

1
+package proxy
2
+
3
+import "sync"
4
+
5
+const copyBufferSize = 30 * 1024
6
+
7
+var copyPool sync.Pool
8
+
9
+func init() {
10
+	copyPool = sync.Pool{
11
+		New: func() interface{} {
12
+			data := make([]byte, copyBufferSize)
13
+			return &data
14
+		},
15
+	}
16
+}

+ 13
- 8
proxy/server.go Просмотреть файл

83
 
83
 
84
 	wait := &sync.WaitGroup{}
84
 	wait := &sync.WaitGroup{}
85
 	wait.Add(2)
85
 	wait.Add(2)
86
-	go func() {
87
-		defer wait.Done()
88
-		io.Copy(clientConn, tgConn) // nolint: errcheck
89
-	}()
90
-	go func() {
91
-		defer wait.Done()
92
-		io.Copy(tgConn, clientConn) // nolint: errcheck
93
-	}()
86
+
87
+	go s.pipe(clientConn, tgConn, wait)
88
+	go s.pipe(tgConn, clientConn, wait)
89
+
94
 	<-ctx.Done()
90
 	<-ctx.Done()
95
 	wait.Wait()
91
 	wait.Wait()
96
 
92
 
131
 	return conn, nil
127
 	return conn, nil
132
 }
128
 }
133
 
129
 
130
+func (s *Server) pipe(dst io.Writer, src io.Reader, wait *sync.WaitGroup) {
131
+	defer wait.Done()
132
+
133
+	buf := copyPool.Get().(*[]byte)
134
+	defer copyPool.Put(buf)
135
+
136
+	io.CopyBuffer(dst, src, *buf) // nolint: errcheck
137
+}
138
+
134
 // NewServer creates new instance of MTPROTO proxy.
139
 // NewServer creates new instance of MTPROTO proxy.
135
 func NewServer(conf *config.Config, logger *zap.SugaredLogger, stat *Stats) *Server {
140
 func NewServer(conf *config.Config, logger *zap.SugaredLogger, stat *Stats) *Server {
136
 	return &Server{
141
 	return &Server{

+ 1
- 1
run-mtg.sh Просмотреть файл

12
   chmod 0400 "$SECRET_PATH"
12
   chmod 0400 "$SECRET_PATH"
13
 )
13
 )
14
 
14
 
15
-# docker pull "$IMAGE_NAME"
15
+docker pull "$IMAGE_NAME"
16
 docker ps --filter "Name=$CONTAINER_NAME" -aq | xargs -r docker rm -fv
16
 docker ps --filter "Name=$CONTAINER_NAME" -aq | xargs -r docker rm -fv
17
 docker run \
17
 docker run \
18
     -d \
18
     -d \

+ 3
- 1
telegram/direct.go Просмотреть файл

43
 
43
 
44
 func (t *directTelegram) Init(conn io.ReadWriteCloser) (io.ReadWriteCloser, error) {
44
 func (t *directTelegram) Init(conn io.ReadWriteCloser) (io.ReadWriteCloser, error) {
45
 	obfs2, frame := obfuscated2.MakeTelegramObfuscated2Frame()
45
 	obfs2, frame := obfuscated2.MakeTelegramObfuscated2Frame()
46
-	if n, err := conn.Write(frame); err != nil || n != len(frame) {
46
+	defer obfuscated2.ReturnFrame(frame)
47
+
48
+	if n, err := conn.Write(*frame); err != nil || n != len(*frame) {
47
 		return nil, errors.Annotate(err, "Cannot write hadnshake frame")
49
 		return nil, errors.Annotate(err, "Cannot write hadnshake frame")
48
 	}
50
 	}
49
 
51
 

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

1
+package wrappers
2
+
3
+import (
4
+	"bytes"
5
+	"sync"
6
+)
7
+
8
+var bufPool sync.Pool
9
+
10
+func getBuffer() *bytes.Buffer {
11
+	buf := bufPool.Get().(*bytes.Buffer)
12
+	buf.Reset()
13
+
14
+	return buf
15
+}
16
+
17
+func putBuffer(buf *bytes.Buffer) {
18
+	bufPool.Put(buf)
19
+}
20
+
21
+func init() {
22
+	bufPool = sync.Pool{
23
+		New: func() interface{} {
24
+			return &bytes.Buffer{}
25
+		},
26
+	}
27
+}

+ 12
- 12
wrappers/streamcipherrwc.go Просмотреть файл

22
 
22
 
23
 // Write writes into connection.
23
 // Write writes into connection.
24
 func (c *StreamCipherReadWriteCloser) Write(p []byte) (int, error) {
24
 func (c *StreamCipherReadWriteCloser) Write(p []byte) (int, error) {
25
-	encrypted := make([]byte, len(p))
25
+	// This is to decrease an amount of allocations. Unfortunately, escape
26
+	// analysis in (at least Golang 1.10) is absolutely not perfect. For
27
+	// example, it understands that we want to have a slice locally, right?
28
+	// But since slice is effectively 2 ints + uintptr to [number]byte, the
29
+	// most heavyweight part is placed in heap.
30
+	buf := getBuffer()
31
+	defer putBuffer(buf)
32
+	buf.Grow(len(p))
33
+	buf.Write(p)
34
+
35
+	encrypted := buf.Bytes()
26
 	c.encryptor.XORKeyStream(encrypted, p)
36
 	c.encryptor.XORKeyStream(encrypted, p)
27
-	allWritten := 0
28
-
29
-	for len(encrypted) > 0 {
30
-		n, err := c.conn.Write(encrypted)
31
-		allWritten += n
32
-		if err != nil {
33
-			return allWritten, err
34
-		}
35
-		encrypted = encrypted[n:]
36
-	}
37
 
37
 
38
-	return allWritten, nil
38
+	return c.conn.Write(encrypted)
39
 }
39
 }
40
 
40
 
41
 // Close closes underlying connection.
41
 // Close closes underlying connection.

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