Переглянути джерело

Merge pull request #6 from 9seconds/syncpool

Use syncpool
tags/0.9
Sergey Arkhipov 7 роки тому
джерело
коміт
4e8e5a51d6
Аккаунт користувача з таким Email не знайдено

+ 2
- 2
Gopkg.lock Переглянути файл

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

+ 1
- 0
client/direct.go Переглянути файл

@@ -18,6 +18,7 @@ func DirectInit(conn net.Conn, conf *config.Config) (int16, io.ReadWriteCloser,
18 18
 	if err != nil {
19 19
 		return 0, nil, errors.Annotate(err, "Cannot extract frame")
20 20
 	}
21
+	defer obfuscated2.ReturnFrame(frame)
21 22
 
22 23
 	obfs2, dc, err := obfuscated2.ParseObfuscated2ClientFrame(conf.Secret, frame)
23 24
 	if err != nil {

+ 4
- 0
main.go Переглянути файл

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

+ 16
- 10
obfuscated2/frame.go Переглянути файл

@@ -68,29 +68,35 @@ func (f Frame) Valid() bool {
68 68
 
69 69
 // Invert inverts frame for extracting encryption keys. Pkease check that link:
70 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 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 79
 	return reversed
80 80
 }
81 81
 
82 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 88
 	if _, err := io.CopyN(buf, conn, FrameLen); err != nil {
89
+		ReturnFrame(frame)
86 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 101
 	for {
96 102
 		if _, err := rand.Read(data); err != nil {
@@ -112,6 +118,6 @@ func generateFrame() Frame {
112 118
 
113 119
 		copy(data.Magic(), tgMagicBytes)
114 120
 
115
-		return data
121
+		return frame
116 122
 	}
117 123
 }

+ 24
- 0
obfuscated2/frame_pool.go Переглянути файл

@@ -0,0 +1,24 @@
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,6 +1,7 @@
1 1
 package obfuscated2
2 2
 
3 3
 import (
4
+	"bytes"
4 5
 	"testing"
5 6
 
6 7
 	"github.com/stretchr/testify/assert"
@@ -47,21 +48,21 @@ func TestFrameValid(t *testing.T) {
47 48
 
48 49
 func TestFrameDoubleInvert(t *testing.T) {
49 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 54
 func TestFrameInvert(t *testing.T) {
54 55
 	frame := makeFrame()
55 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 61
 	toCompare := make([]byte, 48)
61 62
 	for i := 0; i < 48; i++ {
62 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 68
 func TestFrameGenerateValid(t *testing.T) {

+ 10
- 8
obfuscated2/obfuscated2.go Переглянути файл

@@ -19,7 +19,7 @@ type Obfuscated2 struct {
19 19
 // details: http://telegra.ph/telegram-blocks-wtf-05-26
20 20
 //
21 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 23
 	decHasher := sha256.New()
24 24
 	decHasher.Write(frame.Key()) // nolint: errcheck
25 25
 	decHasher.Write(secret)      // nolint: errcheck
@@ -31,8 +31,9 @@ func ParseObfuscated2ClientFrame(secret []byte, frame Frame) (*Obfuscated2, int1
31 31
 	encHasher.Write(secret)              // nolint: errcheck
32 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 37
 	if !decryptedFrame.Valid() {
37 38
 		return nil, 0, errors.New("Unknown protocol")
38 39
 	}
@@ -48,17 +49,18 @@ func ParseObfuscated2ClientFrame(secret []byte, frame Frame) (*Obfuscated2, int1
48 49
 // MakeTelegramObfuscated2Frame creates new handshake frame to send to
49 50
 // Telegram.
50 51
 // https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
51
-func MakeTelegramObfuscated2Frame() (*Obfuscated2, Frame) {
52
+func MakeTelegramObfuscated2Frame() (*Obfuscated2, *Frame) {
52 53
 	frame := generateFrame()
53 54
 
54 55
 	encryptor := makeStreamCipher(frame.Key(), frame.IV())
55 56
 	decryptorFrame := frame.Invert()
56 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 65
 	obfs := &Obfuscated2{
64 66
 		Decryptor: decryptor,

+ 5
- 5
obfuscated2/obfuscated2_test.go Переглянути файл

@@ -12,7 +12,7 @@ func TestObfs2TelegramFrameDecrypt(t *testing.T) {
12 12
 	decryptor := makeStreamCipher(frame.Key(), frame.IV())
13 13
 
14 14
 	decrypted := make(Frame, FrameLen)
15
-	decryptor.XORKeyStream(decrypted, frame)
15
+	decryptor.XORKeyStream(decrypted, *frame)
16 16
 
17 17
 	assert.True(t, decrypted.Valid())
18 18
 }
@@ -42,8 +42,8 @@ func TestObfs2Full(t *testing.T) {
42 42
 
43 43
 	encryptor := makeStreamCipher(clientKey, clientFrame.IV())
44 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 48
 	invertedClientFrame := clientFrame.Invert()
49 49
 	clientHasher = sha256.New()
@@ -52,13 +52,13 @@ func TestObfs2Full(t *testing.T) {
52 52
 	invertedClientKey := clientHasher.Sum(nil)
53 53
 	clientDecryptor := makeStreamCipher(invertedClientKey, invertedClientFrame.IV())
54 54
 
55
-	clientObfs, _, err := ParseObfuscated2ClientFrame(secret, encrypted)
55
+	clientObfs, _, err := ParseObfuscated2ClientFrame(secret, &encrypted)
56 56
 	assert.Nil(t, err)
57 57
 
58 58
 	tgObfs, tgFrame := MakeTelegramObfuscated2Frame()
59 59
 	tgDecryptor := makeStreamCipher(tgFrame.Key(), tgFrame.IV())
60 60
 	decrypted := make(Frame, FrameLen)
61
-	tgDecryptor.XORKeyStream(decrypted, tgFrame)
61
+	tgDecryptor.XORKeyStream(decrypted, *tgFrame)
62 62
 	assert.True(t, decrypted.Valid())
63 63
 
64 64
 	tgInvertedFrame := tgFrame.Invert()

+ 16
- 0
proxy/copy_pool.go Переглянути файл

@@ -0,0 +1,16 @@
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,14 +83,10 @@ func (s *Server) accept(conn net.Conn) {
83 83
 
84 84
 	wait := &sync.WaitGroup{}
85 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 90
 	<-ctx.Done()
95 91
 	wait.Wait()
96 92
 
@@ -131,6 +127,15 @@ func (s *Server) getTelegramStream(ctx context.Context, cancel context.CancelFun
131 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 139
 // NewServer creates new instance of MTPROTO proxy.
135 140
 func NewServer(conf *config.Config, logger *zap.SugaredLogger, stat *Stats) *Server {
136 141
 	return &Server{

+ 1
- 1
run-mtg.sh Переглянути файл

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

+ 3
- 1
telegram/direct.go Переглянути файл

@@ -43,7 +43,9 @@ func (t *directTelegram) Dial(dcIdx int16) (io.ReadWriteCloser, error) {
43 43
 
44 44
 func (t *directTelegram) Init(conn io.ReadWriteCloser) (io.ReadWriteCloser, error) {
45 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 49
 		return nil, errors.Annotate(err, "Cannot write hadnshake frame")
48 50
 	}
49 51
 

+ 27
- 0
wrappers/buffer_pool.go Переглянути файл

@@ -0,0 +1,27 @@
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,20 +22,20 @@ func (c *StreamCipherReadWriteCloser) Read(p []byte) (n int, err error) {
22 22
 
23 23
 // Write writes into connection.
24 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 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 41
 // Close closes underlying connection.

Завантаження…
Відмінити
Зберегти