9seconds 8 лет назад
Родитель
Сommit
4ad913add2
12 измененных файлов: 101 добавлений и 47 удалений
  1. 2
    2
      main.go
  2. 16
    2
      obfuscated2/frame.go
  3. 15
    4
      obfuscated2/obfuscated2.go
  4. 0
    1
      obfuscated2/obfuscated2_test.go
  5. 11
    6
      proxy/cipherrwc.go
  6. 6
    1
      proxy/ctxrwc.go
  7. 5
    0
      proxy/logrwc.go
  8. 22
    26
      proxy/server.go
  9. 7
    3
      proxy/stats.go
  10. 5
    0
      proxy/telegram.go
  11. 7
    2
      proxy/timeoutrwc.go
  12. 5
    0
      proxy/trafficrwc.go

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

95
 			usage("Cannot get local IP address.")
95
 			usage("Cannot get local IP address.")
96
 		}
96
 		}
97
 		myIPBytes, err := ioutil.ReadAll(resp.Body)
97
 		myIPBytes, err := ioutil.ReadAll(resp.Body)
98
-		resp.Body.Close()
98
+		resp.Body.Close() // nolint: errcheck
99
 
99
 
100
 		if err != nil {
100
 		if err != nil {
101
 			usage("Cannot get local IP address.")
101
 			usage("Cannot get local IP address.")
141
 }
141
 }
142
 
142
 
143
 func usage(msg string) {
143
 func usage(msg string) {
144
-	io.WriteString(os.Stderr, msg+"\n")
144
+	io.WriteString(os.Stderr, msg+"\n") // nolint: errcheck
145
 	os.Exit(1)
145
 	os.Exit(1)
146
 }
146
 }

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

9
 	"github.com/juju/errors"
9
 	"github.com/juju/errors"
10
 )
10
 )
11
 
11
 
12
-// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
13
 // [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
12
 // [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
14
 const (
13
 const (
15
 	frameLenKey   = 32
14
 	frameLenKey   = 32
30
 
29
 
31
 var tgMagicBytes = []byte{tgMagicByte, tgMagicByte, tgMagicByte, tgMagicByte}
30
 var tgMagicBytes = []byte{tgMagicByte, tgMagicByte, tgMagicByte, tgMagicByte}
32
 
31
 
32
+// Frame represents handshake frame. Telegram sends 64 bytes of obfuscated2
33
+// initialization data first.
34
+// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
33
 type Frame []byte
35
 type Frame []byte
34
 
36
 
37
+// Key returns AES encryption key.
35
 func (f Frame) Key() []byte {
38
 func (f Frame) Key() []byte {
36
 	return f[frameOffsetFirst:frameOffsetKey]
39
 	return f[frameOffsetFirst:frameOffsetKey]
37
 }
40
 }
38
 
41
 
42
+// IV returns AES encryption initialization vector
39
 func (f Frame) IV() []byte {
43
 func (f Frame) IV() []byte {
40
 	return f[frameOffsetKey:frameOffsetIV]
44
 	return f[frameOffsetKey:frameOffsetIV]
41
 }
45
 }
42
 
46
 
47
+// Magic returns magic bytes from last 8 bytes of frame. Telegram checks
48
+// for values there. If after decryption magic is not as expected,
49
+// connection considered as failed.
43
 func (f Frame) Magic() []byte {
50
 func (f Frame) Magic() []byte {
44
 	return f[frameOffsetIV:frameOffsetMagic]
51
 	return f[frameOffsetIV:frameOffsetMagic]
45
 }
52
 }
46
 
53
 
54
+// DC returns number of datacenter IP client wants to use.
47
 func (f Frame) DC() (n int16) {
55
 func (f Frame) DC() (n int16) {
48
 	buf := bytes.NewReader(f[frameOffsetMagic:frameOffsetDC])
56
 	buf := bytes.NewReader(f[frameOffsetMagic:frameOffsetDC])
49
-	binary.Read(buf, binary.LittleEndian, &n)
57
+	if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
58
+		n = 1
59
+	}
50
 
60
 
51
 	if n < 0 {
61
 	if n < 0 {
52
 		n = -n
62
 		n = -n
57
 	return n - 1
67
 	return n - 1
58
 }
68
 }
59
 
69
 
70
+// Valid checks that *decrypted* frame is valid. Only magic bytes are checked.
60
 func (f Frame) Valid() bool {
71
 func (f Frame) Valid() bool {
61
 	return bytes.Equal(f.Magic(), tgMagicBytes)
72
 	return bytes.Equal(f.Magic(), tgMagicBytes)
62
 }
73
 }
63
 
74
 
75
+// Invert inverts frame for extracting encryption keys. Pkease check that link:
76
+// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
64
 func (f Frame) Invert() Frame {
77
 func (f Frame) Invert() Frame {
65
 	reversed := make(Frame, FrameLen)
78
 	reversed := make(Frame, FrameLen)
66
 	copy(reversed, f)
79
 	copy(reversed, f)
72
 	return reversed
85
 	return reversed
73
 }
86
 }
74
 
87
 
88
+// ExtractFrame extracts exact obfuscated2 handshake frame from given reader.
75
 func ExtractFrame(conn io.Reader) (Frame, error) {
89
 func ExtractFrame(conn io.Reader) (Frame, error) {
76
 	buf := &bytes.Buffer{}
90
 	buf := &bytes.Buffer{}
77
 	if _, err := io.CopyN(buf, conn, FrameLen); err != nil {
91
 	if _, err := io.CopyN(buf, conn, FrameLen); err != nil {

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

8
 	"github.com/juju/errors"
8
 	"github.com/juju/errors"
9
 )
9
 )
10
 
10
 
11
+// Obfuscated2 contains AES CTR encryption and decryption streams
12
+// for telegram connection.
11
 type Obfuscated2 struct {
13
 type Obfuscated2 struct {
12
 	decryptor cipher.Stream
14
 	decryptor cipher.Stream
13
 	encryptor cipher.Stream
15
 	encryptor cipher.Stream
14
 }
16
 }
15
 
17
 
18
+// Encrypt encrypts given data.
16
 func (o *Obfuscated2) Encrypt(data []byte) []byte {
19
 func (o *Obfuscated2) Encrypt(data []byte) []byte {
17
 	buf := make([]byte, len(data))
20
 	buf := make([]byte, len(data))
18
 	o.encryptor.XORKeyStream(buf, data)
21
 	o.encryptor.XORKeyStream(buf, data)
19
 	return buf
22
 	return buf
20
 }
23
 }
21
 
24
 
25
+// Decrypt decrypts given data.
22
 func (o *Obfuscated2) Decrypt(data []byte) []byte {
26
 func (o *Obfuscated2) Decrypt(data []byte) []byte {
23
 	buf := make([]byte, len(data))
27
 	buf := make([]byte, len(data))
24
 	o.decryptor.XORKeyStream(buf, data)
28
 	o.decryptor.XORKeyStream(buf, data)
25
 	return buf
29
 	return buf
26
 }
30
 }
27
 
31
 
32
+// ParseObfuscated2ClientFrame parses client frame. Please check this link for
33
+// details: http://telegra.ph/telegram-blocks-wtf-05-26
34
+//
35
+// Beware, link above is in russian.
28
 func ParseObfuscated2ClientFrame(secret, data []byte) (*Obfuscated2, int16, error) {
36
 func ParseObfuscated2ClientFrame(secret, data []byte) (*Obfuscated2, int16, error) {
29
 	frame := Frame(data)
37
 	frame := Frame(data)
30
 
38
 
31
 	decHasher := sha256.New()
39
 	decHasher := sha256.New()
32
-	decHasher.Write(frame.Key())
33
-	decHasher.Write(secret)
40
+	decHasher.Write(frame.Key()) // nolint: errcheck
41
+	decHasher.Write(secret)      // nolint: errcheck
34
 	decryptor := makeStreamCipher(decHasher.Sum(nil), frame.IV())
42
 	decryptor := makeStreamCipher(decHasher.Sum(nil), frame.IV())
35
 
43
 
36
 	invertedFrame := frame.Invert()
44
 	invertedFrame := frame.Invert()
37
 	encHasher := sha256.New()
45
 	encHasher := sha256.New()
38
-	encHasher.Write(invertedFrame.Key())
39
-	encHasher.Write(secret)
46
+	encHasher.Write(invertedFrame.Key()) // nolint: errcheck
47
+	encHasher.Write(secret)              // nolint: errcheck
40
 	encryptor := makeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
48
 	encryptor := makeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
41
 
49
 
42
 	decryptedFrame := make(Frame, FrameLen)
50
 	decryptedFrame := make(Frame, FrameLen)
53
 	return obfs, decryptedFrame.DC(), nil
61
 	return obfs, decryptedFrame.DC(), nil
54
 }
62
 }
55
 
63
 
64
+// MakeTelegramObfuscated2Frame creates new handshake frame to send to
65
+// Telegram.
66
+// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
56
 func MakeTelegramObfuscated2Frame() (*Obfuscated2, Frame) {
67
 func MakeTelegramObfuscated2Frame() (*Obfuscated2, Frame) {
57
 	frame := generateFrame()
68
 	frame := generateFrame()
58
 
69
 

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

2
 
2
 
3
 import (
3
 import (
4
 	"crypto/sha256"
4
 	"crypto/sha256"
5
-	"fmt"
6
 	"testing"
5
 	"testing"
7
 
6
 
8
 	"github.com/stretchr/testify/assert"
7
 	"github.com/stretchr/testify/assert"

+ 11
- 6
proxy/cipherrwc.go Просмотреть файл

5
 	"io"
5
 	"io"
6
 )
6
 )
7
 
7
 
8
+// Cipher is an interface to anything which can encrypt and decrypt
8
 type Cipher interface {
9
 type Cipher interface {
9
 	Encrypt([]byte) []byte
10
 	Encrypt([]byte) []byte
10
 	Decrypt([]byte) []byte
11
 	Decrypt([]byte) []byte
11
 }
12
 }
12
 
13
 
14
+// CipherReadWriteCloser wraps connection for transparent encryption
13
 type CipherReadWriteCloser struct {
15
 type CipherReadWriteCloser struct {
14
 	crypt Cipher
16
 	crypt Cipher
15
 	conn  io.ReadWriteCloser
17
 	conn  io.ReadWriteCloser
16
 	rest  *bytes.Buffer
18
 	rest  *bytes.Buffer
17
 }
19
 }
18
 
20
 
21
+// Read reads from connection
19
 func (c *CipherReadWriteCloser) Read(p []byte) (n int, err error) {
22
 func (c *CipherReadWriteCloser) Read(p []byte) (n int, err error) {
20
 	n, err = c.conn.Read(p)
23
 	n, err = c.conn.Read(p)
21
 	copy(p, c.crypt.Decrypt(p[:n]))
24
 	copy(p, c.crypt.Decrypt(p[:n]))
22
 	return
25
 	return
23
 }
26
 }
24
 
27
 
25
-func (c *CipherReadWriteCloser) Write(p []byte) (n int, err error) {
28
+// Write writes into connection.
29
+func (c *CipherReadWriteCloser) Write(p []byte) (int, error) {
26
 	encrypted := c.crypt.Encrypt(p)
30
 	encrypted := c.crypt.Encrypt(p)
31
+	allWritten := 0
27
 
32
 
28
-	curN := 0
29
 	for len(encrypted) > 0 {
33
 	for len(encrypted) > 0 {
30
-		curN, err = c.conn.Write(encrypted)
31
-		n += curN
34
+		n, err := c.conn.Write(encrypted)
35
+		allWritten += n
32
 		if err != nil {
36
 		if err != nil {
33
-			return
37
+			return allWritten, err
34
 		}
38
 		}
35
 		encrypted = encrypted[n:]
39
 		encrypted = encrypted[n:]
36
 	}
40
 	}
37
 
41
 
38
-	return
42
+	return allWritten, nil
39
 }
43
 }
40
 
44
 
45
+// Close closes underlying connection.
41
 func (c *CipherReadWriteCloser) Close() error {
46
 func (c *CipherReadWriteCloser) Close() error {
42
 	return c.conn.Close()
47
 	return c.conn.Close()
43
 }
48
 }

+ 6
- 1
proxy/ctxrwc.go Просмотреть файл

7
 	"github.com/juju/errors"
7
 	"github.com/juju/errors"
8
 )
8
 )
9
 
9
 
10
+// CtxReadWriteCloser wraps underlying connection and does management of the
11
+// context and its cancel function.
10
 type CtxReadWriteCloser struct {
12
 type CtxReadWriteCloser struct {
11
 	ctx    context.Context
13
 	ctx    context.Context
12
 	conn   io.ReadWriteCloser
14
 	conn   io.ReadWriteCloser
13
 	cancel context.CancelFunc
15
 	cancel context.CancelFunc
14
 }
16
 }
15
 
17
 
18
+// Read reads from connection
16
 func (c *CtxReadWriteCloser) Read(p []byte) (int, error) {
19
 func (c *CtxReadWriteCloser) Read(p []byte) (int, error) {
17
 	select {
20
 	select {
18
 	case <-c.ctx.Done():
21
 	case <-c.ctx.Done():
26
 	}
29
 	}
27
 }
30
 }
28
 
31
 
32
+// Write writes into connection.
29
 func (c *CtxReadWriteCloser) Write(p []byte) (int, error) {
33
 func (c *CtxReadWriteCloser) Write(p []byte) (int, error) {
30
 	select {
34
 	select {
31
 	case <-c.ctx.Done():
35
 	case <-c.ctx.Done():
39
 	}
43
 	}
40
 }
44
 }
41
 
45
 
46
+// Close closes underlying connection.
42
 func (c *CtxReadWriteCloser) Close() error {
47
 func (c *CtxReadWriteCloser) Close() error {
43
 	return c.conn.Close()
48
 	return c.conn.Close()
44
 }
49
 }
45
 
50
 
46
-func newCtxReadWriteCloser(conn io.ReadWriteCloser, ctx context.Context, cancel context.CancelFunc) io.ReadWriteCloser {
51
+func newCtxReadWriteCloser(ctx context.Context, cancel context.CancelFunc, conn io.ReadWriteCloser) io.ReadWriteCloser {
47
 	return &CtxReadWriteCloser{
52
 	return &CtxReadWriteCloser{
48
 		conn:   conn,
53
 		conn:   conn,
49
 		ctx:    ctx,
54
 		ctx:    ctx,

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

6
 	"go.uber.org/zap"
6
 	"go.uber.org/zap"
7
 )
7
 )
8
 
8
 
9
+// LogReadWriteCloser adds additional logging for reading/writing. All
10
+// logging is performed for debug mode only.
9
 type LogReadWriteCloser struct {
11
 type LogReadWriteCloser struct {
10
 	conn   io.ReadWriteCloser
12
 	conn   io.ReadWriteCloser
11
 	logger *zap.SugaredLogger
13
 	logger *zap.SugaredLogger
13
 	name   string
15
 	name   string
14
 }
16
 }
15
 
17
 
18
+// Read reads from connection
16
 func (l *LogReadWriteCloser) Read(p []byte) (n int, err error) {
19
 func (l *LogReadWriteCloser) Read(p []byte) (n int, err error) {
17
 	n, err = l.conn.Read(p)
20
 	n, err = l.conn.Read(p)
18
 	l.logger.Debugw("Finish reading", "name", l.name, "socketid", l.sockid, "nbytes", n, "error", err)
21
 	l.logger.Debugw("Finish reading", "name", l.name, "socketid", l.sockid, "nbytes", n, "error", err)
19
 	return
22
 	return
20
 }
23
 }
21
 
24
 
25
+// Write writes into connection.
22
 func (l *LogReadWriteCloser) Write(p []byte) (n int, err error) {
26
 func (l *LogReadWriteCloser) Write(p []byte) (n int, err error) {
23
 	n, err = l.conn.Write(p)
27
 	n, err = l.conn.Write(p)
24
 	l.logger.Debugw("Finish writing", "name", l.name, "socketid", l.sockid, "nbytes", n, "error", err)
28
 	l.logger.Debugw("Finish writing", "name", l.name, "socketid", l.sockid, "nbytes", n, "error", err)
25
 	return
29
 	return
26
 }
30
 }
27
 
31
 
32
+// Close closes underlying connection.
28
 func (l *LogReadWriteCloser) Close() error {
33
 func (l *LogReadWriteCloser) Close() error {
29
 	err := l.conn.Close()
34
 	err := l.conn.Close()
30
 	l.logger.Debugw("Finish closing socket", "name", l.name, "socketid", l.sockid, "error", err)
35
 	l.logger.Debugw("Finish closing socket", "name", l.name, "socketid", l.sockid, "error", err)

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

14
 	"go.uber.org/zap"
14
 	"go.uber.org/zap"
15
 )
15
 )
16
 
16
 
17
-const bufferSize = 4096
18
-
17
+// Server is an insgtance of MTPROTO proxy.
19
 type Server struct {
18
 type Server struct {
20
 	ip           net.IP
19
 	ip           net.IP
21
 	port         int
20
 	port         int
22
 	secret       []byte
21
 	secret       []byte
23
 	logger       *zap.SugaredLogger
22
 	logger       *zap.SugaredLogger
24
-	lsock        net.Listener
25
 	ctx          context.Context
23
 	ctx          context.Context
26
 	readTimeout  time.Duration
24
 	readTimeout  time.Duration
27
 	writeTimeout time.Duration
25
 	writeTimeout time.Duration
29
 	ipv6         bool
27
 	ipv6         bool
30
 }
28
 }
31
 
29
 
30
+// Serve does MTPROTO proxying.
32
 func (s *Server) Serve() error {
31
 func (s *Server) Serve() error {
33
-	lsock, err := net.Listen("tcp", s.Addr())
32
+	addr := net.JoinHostPort(s.ip.String(), strconv.Itoa(s.port))
33
+	lsock, err := net.Listen("tcp", addr)
34
 	if err != nil {
34
 	if err != nil {
35
 		return errors.Annotate(err, "Cannot create listen socket")
35
 		return errors.Annotate(err, "Cannot create listen socket")
36
 	}
36
 	}
42
 			go s.accept(conn)
42
 			go s.accept(conn)
43
 		}
43
 		}
44
 	}
44
 	}
45
-
46
-	return nil
47
-}
48
-
49
-func (s *Server) Addr() string {
50
-	return net.JoinHostPort(s.ip.String(), strconv.Itoa(s.port))
51
 }
45
 }
52
 
46
 
53
 func (s *Server) accept(conn net.Conn) {
47
 func (s *Server) accept(conn net.Conn) {
54
 	defer func() {
48
 	defer func() {
55
 		s.stats.closeConnection()
49
 		s.stats.closeConnection()
56
-		conn.Close()
50
+		conn.Close() // nolint: errcheck
57
 
51
 
58
 		if r := recover(); r != nil {
52
 		if r := recover(); r != nil {
59
 			s.logger.Errorw("Crash of accept handler", "error", r)
53
 			s.logger.Errorw("Crash of accept handler", "error", r)
70
 		"socketid", socketID,
64
 		"socketid", socketID,
71
 	)
65
 	)
72
 
66
 
73
-	clientConn, dc, err := s.getClientStream(conn, ctx, cancel, socketID)
67
+	clientConn, dc, err := s.getClientStream(ctx, cancel, conn, socketID)
74
 	if err != nil {
68
 	if err != nil {
75
 		s.logger.Warnw("Cannot initialize client connection",
69
 		s.logger.Warnw("Cannot initialize client connection",
76
 			"secret", s.secret,
70
 			"secret", s.secret,
80
 		)
74
 		)
81
 		return
75
 		return
82
 	}
76
 	}
83
-	defer clientConn.Close()
77
+	defer clientConn.Close() // nolint: errcheck
84
 
78
 
85
-	tgConn, err := s.getTelegramStream(dc, ctx, cancel, socketID)
79
+	tgConn, err := s.getTelegramStream(ctx, cancel, dc, socketID)
86
 	if err != nil {
80
 	if err != nil {
87
 		s.logger.Warnw("Cannot initialize Telegram connection",
81
 		s.logger.Warnw("Cannot initialize Telegram connection",
88
 			"socketid", socketID,
82
 			"socketid", socketID,
90
 		)
84
 		)
91
 		return
85
 		return
92
 	}
86
 	}
93
-	defer tgConn.Close()
87
+	defer tgConn.Close() // nolint: errcheck
94
 
88
 
95
 	wait := &sync.WaitGroup{}
89
 	wait := &sync.WaitGroup{}
96
 	wait.Add(2)
90
 	wait.Add(2)
97
-	go s.pipe(wait, clientConn, tgConn)
98
-	go s.pipe(wait, tgConn, clientConn)
91
+	go func() {
92
+		defer wait.Done()
93
+		io.Copy(clientConn, tgConn) // nolint: errcheck
94
+	}()
95
+	go func() {
96
+		defer wait.Done()
97
+		io.Copy(tgConn, clientConn) // nolint: errcheck
98
+	}()
99
 	<-ctx.Done()
99
 	<-ctx.Done()
100
 	wait.Wait()
100
 	wait.Wait()
101
 
101
 
110
 	return uuid.NewV4().String()
110
 	return uuid.NewV4().String()
111
 }
111
 }
112
 
112
 
113
-func (s *Server) getClientStream(conn net.Conn, ctx context.Context, cancel context.CancelFunc, socketID string) (io.ReadWriteCloser, int16, error) {
113
+func (s *Server) getClientStream(ctx context.Context, cancel context.CancelFunc, conn net.Conn, socketID string) (io.ReadWriteCloser, int16, error) {
114
 	wConn := newTimeoutReadWriteCloser(conn, s.readTimeout, s.writeTimeout)
114
 	wConn := newTimeoutReadWriteCloser(conn, s.readTimeout, s.writeTimeout)
115
 	wConn = newTrafficReadWriteCloser(wConn, s.stats.addIncomingTraffic, s.stats.addOutgoingTraffic)
115
 	wConn = newTrafficReadWriteCloser(wConn, s.stats.addIncomingTraffic, s.stats.addOutgoingTraffic)
116
 	frame, err := obfuscated2.ExtractFrame(wConn)
116
 	frame, err := obfuscated2.ExtractFrame(wConn)
125
 
125
 
126
 	wConn = newLogReadWriteCloser(wConn, s.logger, socketID, "client")
126
 	wConn = newLogReadWriteCloser(wConn, s.logger, socketID, "client")
127
 	wConn = newCipherReadWriteCloser(wConn, obfs2)
127
 	wConn = newCipherReadWriteCloser(wConn, obfs2)
128
-	wConn = newCtxReadWriteCloser(wConn, ctx, cancel)
128
+	wConn = newCtxReadWriteCloser(ctx, cancel, wConn)
129
 
129
 
130
 	return wConn, dc, nil
130
 	return wConn, dc, nil
131
 }
131
 }
132
 
132
 
133
-func (s *Server) getTelegramStream(dc int16, ctx context.Context, cancel context.CancelFunc, socketID string) (io.ReadWriteCloser, error) {
133
+func (s *Server) getTelegramStream(ctx context.Context, cancel context.CancelFunc, dc int16, socketID string) (io.ReadWriteCloser, error) {
134
 	socket, err := dialToTelegram(s.ipv6, dc, s.readTimeout)
134
 	socket, err := dialToTelegram(s.ipv6, dc, s.readTimeout)
135
 	if err != nil {
135
 	if err != nil {
136
 		return nil, errors.Annotate(err, "Cannot dial")
136
 		return nil, errors.Annotate(err, "Cannot dial")
145
 
145
 
146
 	wConn = newLogReadWriteCloser(wConn, s.logger, socketID, "telegram")
146
 	wConn = newLogReadWriteCloser(wConn, s.logger, socketID, "telegram")
147
 	wConn = newCipherReadWriteCloser(wConn, obfs2)
147
 	wConn = newCipherReadWriteCloser(wConn, obfs2)
148
-	wConn = newCtxReadWriteCloser(wConn, ctx, cancel)
148
+	wConn = newCtxReadWriteCloser(ctx, cancel, wConn)
149
 
149
 
150
 	return wConn, nil
150
 	return wConn, nil
151
 }
151
 }
152
 
152
 
153
-func (s *Server) pipe(wait *sync.WaitGroup, reader io.Reader, writer io.Writer) {
154
-	defer wait.Done()
155
-	io.Copy(writer, reader)
156
-}
157
-
153
+// NewServer creates new instance of MTPROTO proxy.
158
 func NewServer(ip net.IP, port int, secret []byte, logger *zap.SugaredLogger,
154
 func NewServer(ip net.IP, port int, secret []byte, logger *zap.SugaredLogger,
159
 	readTimeout, writeTimeout time.Duration, ipv6 bool, stat *Stats) *Server {
155
 	readTimeout, writeTimeout time.Duration, ipv6 bool, stat *Stats) *Server {
160
 	return &Server{
156
 	return &Server{

+ 7
- 3
proxy/stats.go Просмотреть файл

2
 
2
 
3
 import (
3
 import (
4
 	"encoding/json"
4
 	"encoding/json"
5
+	"fmt"
5
 	"net"
6
 	"net"
6
 	"net/http"
7
 	"net/http"
7
 	"net/url"
8
 	"net/url"
17
 	return []byte(strconv.Itoa(uptime)), nil
18
 	return []byte(strconv.Itoa(uptime)), nil
18
 }
19
 }
19
 
20
 
21
+// Stats is a datastructure for statistics on work of this proxy.
20
 type Stats struct {
22
 type Stats struct {
21
 	AllConnections    uint64 `json:"all_connections"`
23
 	AllConnections    uint64 `json:"all_connections"`
22
 	ActiveConnections uint32 `json:"active_connections"`
24
 	ActiveConnections uint32 `json:"active_connections"`
50
 	atomic.AddUint64(&s.Traffic.Outgoing, uint64(n))
52
 	atomic.AddUint64(&s.Traffic.Outgoing, uint64(n))
51
 }
53
 }
52
 
54
 
53
-func (s *Stats) Serve(host net.IP, port uint16) {
55
+// Serve runs statistics HTTP server.
56
+func (s *Stats) Serve(host fmt.Stringer, port uint16) {
54
 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
57
 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
55
 		w.Header().Set("Content-Type", "application/json")
58
 		w.Header().Set("Content-Type", "application/json")
56
 
59
 
57
 		encoder := json.NewEncoder(w)
60
 		encoder := json.NewEncoder(w)
58
 		encoder.SetEscapeHTML(false)
61
 		encoder.SetEscapeHTML(false)
59
 		encoder.SetIndent("", "  ")
62
 		encoder.SetIndent("", "  ")
60
-		encoder.Encode(s)
63
+		encoder.Encode(s) // nolint: errcheck, gas
61
 	})
64
 	})
62
 
65
 
63
 	addr := net.JoinHostPort(host.String(), strconv.Itoa(int(port)))
66
 	addr := net.JoinHostPort(host.String(), strconv.Itoa(int(port)))
64
-	http.ListenAndServe(addr, nil)
67
+	http.ListenAndServe(addr, nil) // nolint: errcheck, gas
65
 }
68
 }
66
 
69
 
70
+// NewStats returns new instance of statistics datastructure.
67
 func NewStats(serverName string, port uint16, secret string) *Stats {
71
 func NewStats(serverName string, port uint16, secret string) *Stats {
68
 	urlQuery := makeURLQuery(serverName, port, secret)
72
 	urlQuery := makeURLQuery(serverName, port, secret)
69
 
73
 

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

7
 	"github.com/juju/errors"
7
 	"github.com/juju/errors"
8
 )
8
 )
9
 
9
 
10
+// TelegramAddress presents a pair of v4 and v6 addresses. This pairization
11
+// is required because we want to use DC indexes.
10
 type TelegramAddress struct {
12
 type TelegramAddress struct {
11
 	v4 string
13
 	v4 string
12
 	v6 string
14
 	v6 string
13
 }
15
 }
14
 
16
 
17
+// IPv4 returns v4 address.
15
 func (t *TelegramAddress) IPv4() string {
18
 func (t *TelegramAddress) IPv4() string {
16
 	return net.JoinHostPort(t.v4, telegramPort)
19
 	return net.JoinHostPort(t.v4, telegramPort)
17
 }
20
 }
18
 
21
 
22
+// IPv6 returns v4 address.
19
 func (t *TelegramAddress) IPv6() string {
23
 func (t *TelegramAddress) IPv6() string {
20
 	return net.JoinHostPort(t.v6, telegramPort)
24
 	return net.JoinHostPort(t.v6, telegramPort)
21
 }
25
 }
22
 
26
 
27
+// TelegramAddresses is a list of all known Telegram addresses for DC indexes.
23
 var TelegramAddresses = []TelegramAddress{
28
 var TelegramAddresses = []TelegramAddress{
24
 	TelegramAddress{v4: "149.154.175.50", v6: "2001:b28:f23d:f001::a"},
29
 	TelegramAddress{v4: "149.154.175.50", v6: "2001:b28:f23d:f001::a"},
25
 	TelegramAddress{v4: "149.154.167.51", v6: "2001:67c:04e8:f002::a"},
30
 	TelegramAddress{v4: "149.154.167.51", v6: "2001:67c:04e8:f002::a"},

+ 7
- 2
proxy/timeoutrwc.go Просмотреть файл

6
 	"time"
6
 	"time"
7
 )
7
 )
8
 
8
 
9
+// TimeoutReadWriteCloser sets timeouts for read/write into underlying
10
+// network connection.
9
 type TimeoutReadWriteCloser struct {
11
 type TimeoutReadWriteCloser struct {
10
 	conn         net.Conn
12
 	conn         net.Conn
11
 	readTimeout  time.Duration
13
 	readTimeout  time.Duration
12
 	writeTimeout time.Duration
14
 	writeTimeout time.Duration
13
 }
15
 }
14
 
16
 
17
+// Read reads from connection
15
 func (t *TimeoutReadWriteCloser) Read(p []byte) (int, error) {
18
 func (t *TimeoutReadWriteCloser) Read(p []byte) (int, error) {
16
-	t.conn.SetReadDeadline(time.Now().Add(t.readTimeout))
19
+	t.conn.SetReadDeadline(time.Now().Add(t.readTimeout)) // nolint: errcheck, gas
17
 	return t.conn.Read(p)
20
 	return t.conn.Read(p)
18
 }
21
 }
19
 
22
 
23
+// Write writes into connection.
20
 func (t *TimeoutReadWriteCloser) Write(p []byte) (int, error) {
24
 func (t *TimeoutReadWriteCloser) Write(p []byte) (int, error) {
21
-	t.conn.SetWriteDeadline(time.Now().Add(t.writeTimeout))
25
+	t.conn.SetWriteDeadline(time.Now().Add(t.writeTimeout)) // nolint: errcheck, gas
22
 	return t.conn.Write(p)
26
 	return t.conn.Write(p)
23
 }
27
 }
24
 
28
 
29
+// Close closes underlying connection.
25
 func (t *TimeoutReadWriteCloser) Close() error {
30
 func (t *TimeoutReadWriteCloser) Close() error {
26
 	return t.conn.Close()
31
 	return t.conn.Close()
27
 }
32
 }

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

2
 
2
 
3
 import "io"
3
 import "io"
4
 
4
 
5
+// TrafficReadWriteCloser counts an amount of ingress/egress traffic by
6
+// calling given callbacks.
5
 type TrafficReadWriteCloser struct {
7
 type TrafficReadWriteCloser struct {
6
 	conn          io.ReadWriteCloser
8
 	conn          io.ReadWriteCloser
7
 	readCallback  func(int)
9
 	readCallback  func(int)
8
 	writeCallback func(int)
10
 	writeCallback func(int)
9
 }
11
 }
10
 
12
 
13
+// Read reads from connection
11
 func (t *TrafficReadWriteCloser) Read(p []byte) (n int, err error) {
14
 func (t *TrafficReadWriteCloser) Read(p []byte) (n int, err error) {
12
 	n, err = t.conn.Read(p)
15
 	n, err = t.conn.Read(p)
13
 	t.readCallback(n)
16
 	t.readCallback(n)
14
 	return
17
 	return
15
 }
18
 }
16
 
19
 
20
+// Write writes into connection.
17
 func (t *TrafficReadWriteCloser) Write(p []byte) (n int, err error) {
21
 func (t *TrafficReadWriteCloser) Write(p []byte) (n int, err error) {
18
 	n, err = t.conn.Write(p)
22
 	n, err = t.conn.Write(p)
19
 	t.writeCallback(n)
23
 	t.writeCallback(n)
20
 	return
24
 	return
21
 }
25
 }
22
 
26
 
27
+// Close closes underlying connection.
23
 func (t *TrafficReadWriteCloser) Close() error {
28
 func (t *TrafficReadWriteCloser) Close() error {
24
 	return t.conn.Close()
29
 	return t.conn.Close()
25
 }
30
 }

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