mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 21:44:03 +03:00
Merge pull request #7 from 9seconds/connection-types
Support different client connection types
This commit is contained in:
+2
-1
@@ -5,7 +5,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/config"
|
"github.com/9seconds/mtg/config"
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init has to initialize client connection based on given config.
|
// Init has to initialize client connection based on given config.
|
||||||
type Init func(net.Conn, *config.Config) (int16, io.ReadWriteCloser, error)
|
type Init func(net.Conn, *config.Config) (*mtproto.ConnectionOpts, io.ReadWriteCloser, error)
|
||||||
|
|||||||
+6
-5
@@ -7,25 +7,26 @@ import (
|
|||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/config"
|
"github.com/9seconds/mtg/config"
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
"github.com/9seconds/mtg/obfuscated2"
|
"github.com/9seconds/mtg/obfuscated2"
|
||||||
"github.com/9seconds/mtg/wrappers"
|
"github.com/9seconds/mtg/wrappers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DirectInit initializes client to access Telegram bypassing middleproxies.
|
// DirectInit initializes client to access Telegram bypassing middleproxies.
|
||||||
func DirectInit(conn net.Conn, conf *config.Config) (int16, io.ReadWriteCloser, error) {
|
func DirectInit(conn net.Conn, conf *config.Config) (*mtproto.ConnectionOpts, io.ReadWriteCloser, error) {
|
||||||
socket := wrappers.NewTimeoutRWC(conn, conf.TimeoutRead, conf.TimeoutWrite)
|
socket := wrappers.NewTimeoutRWC(conn, conf.TimeoutRead, conf.TimeoutWrite)
|
||||||
frame, err := obfuscated2.ExtractFrame(socket)
|
frame, err := obfuscated2.ExtractFrame(socket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, errors.Annotate(err, "Cannot extract frame")
|
return nil, nil, errors.Annotate(err, "Cannot extract frame")
|
||||||
}
|
}
|
||||||
defer obfuscated2.ReturnFrame(frame)
|
defer obfuscated2.ReturnFrame(frame)
|
||||||
|
|
||||||
obfs2, dc, err := obfuscated2.ParseObfuscated2ClientFrame(conf.Secret, frame)
|
obfs2, connOpts, err := obfuscated2.ParseObfuscated2ClientFrame(conf.Secret, frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, errors.Annotate(err, "Cannot parse obfuscated frame")
|
return nil, nil, errors.Annotate(err, "Cannot parse obfuscated frame")
|
||||||
}
|
}
|
||||||
|
|
||||||
socket = wrappers.NewStreamCipherRWC(socket, obfs2.Encryptor, obfs2.Decryptor)
|
socket = wrappers.NewStreamCipherRWC(socket, obfs2.Encryptor, obfs2.Decryptor)
|
||||||
|
|
||||||
return dc, socket, nil
|
return connOpts, socket, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package mtproto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectionType is a type of obfuscated2/mtproto connection requested
|
||||||
|
// by the user.
|
||||||
|
type ConnectionType uint8
|
||||||
|
|
||||||
|
// ConnectionOpts presents an options, metadata on connection requested
|
||||||
|
// by the user on handshake.
|
||||||
|
type ConnectionOpts struct {
|
||||||
|
DC int16
|
||||||
|
ConnectionType ConnectionType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Different connection types which user requests from Telegram.
|
||||||
|
const (
|
||||||
|
ConnectionTypeUnknown ConnectionType = iota
|
||||||
|
ConnectionTypeAbridged
|
||||||
|
ConnectionTypeIntermediate
|
||||||
|
)
|
||||||
|
|
||||||
|
// Connection tags for mtproto handshakes.
|
||||||
|
var (
|
||||||
|
ConnectionTagAbridged = []byte{0xef, 0xef, 0xef, 0xef}
|
||||||
|
ConnectionTagIntermediate = []byte{0xee, 0xee, 0xee, 0xee}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tag maps connection type to the corresponding handshake tag.
|
||||||
|
func (t ConnectionType) Tag() ([]byte, error) {
|
||||||
|
switch t {
|
||||||
|
case ConnectionTypeAbridged:
|
||||||
|
return ConnectionTagAbridged, nil
|
||||||
|
case ConnectionTypeIntermediate:
|
||||||
|
return ConnectionTagIntermediate, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("Unknown connection type %d", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectionTagFromHandshake maps magic bytes to the connection type.
|
||||||
|
func ConnectionTagFromHandshake(magic []byte) (ConnectionType, error) {
|
||||||
|
if bytes.Equal(magic, ConnectionTagIntermediate) {
|
||||||
|
return ConnectionTypeIntermediate, nil
|
||||||
|
}
|
||||||
|
if bytes.Equal(magic, ConnectionTagAbridged) {
|
||||||
|
return ConnectionTypeAbridged, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnectionTypeUnknown, errors.New("Unknown handshake protocol")
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
|
// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
|
||||||
@@ -22,13 +24,9 @@ const (
|
|||||||
frameOffsetMagic = frameOffsetIV + frameLenMagic
|
frameOffsetMagic = frameOffsetIV + frameLenMagic
|
||||||
frameOffsetDC = frameOffsetMagic + frameLenDC
|
frameOffsetDC = frameOffsetMagic + frameLenDC
|
||||||
|
|
||||||
tgMagicByte = byte(239)
|
|
||||||
|
|
||||||
FrameLen = 64
|
FrameLen = 64
|
||||||
)
|
)
|
||||||
|
|
||||||
var tgMagicBytes = []byte{tgMagicByte, tgMagicByte, tgMagicByte, tgMagicByte}
|
|
||||||
|
|
||||||
// Frame represents handshake frame. Telegram sends 64 bytes of obfuscated2
|
// Frame represents handshake frame. Telegram sends 64 bytes of obfuscated2
|
||||||
// initialization data first.
|
// initialization data first.
|
||||||
// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
|
// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
|
||||||
@@ -61,9 +59,9 @@ func (f Frame) DC() (n int16) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid checks that *decrypted* frame is valid. Only magic bytes are checked.
|
// ConnectionType identifies connection type of the handshake frame.
|
||||||
func (f Frame) Valid() bool {
|
func (f Frame) ConnectionType() (mtproto.ConnectionType, error) {
|
||||||
return bytes.Equal(f.Magic(), tgMagicBytes)
|
return mtproto.ConnectionTagFromHandshake(f.Magic())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invert inverts frame for extracting encryption keys. Pkease check that link:
|
// Invert inverts frame for extracting encryption keys. Pkease check that link:
|
||||||
@@ -94,7 +92,7 @@ func ExtractFrame(conn io.Reader) (*Frame, error) {
|
|||||||
return frame, nil
|
return frame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateFrame() *Frame {
|
func generateFrame(connectionType mtproto.ConnectionType) *Frame {
|
||||||
frame := MakeFrame()
|
frame := MakeFrame()
|
||||||
data := *frame
|
data := *frame
|
||||||
|
|
||||||
@@ -116,7 +114,9 @@ func generateFrame() *Frame {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(data.Magic(), tgMagicBytes)
|
// error has to be checked before calling this function
|
||||||
|
tag, _ := connectionType.Tag() // nolint: errcheck
|
||||||
|
copy(data.Magic(), tag)
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ package obfuscated2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFrameKey(t *testing.T) {
|
func TestFrameKey(t *testing.T) {
|
||||||
@@ -28,7 +31,7 @@ func TestFrameIV(t *testing.T) {
|
|||||||
func TestFrameMagic(t *testing.T) {
|
func TestFrameMagic(t *testing.T) {
|
||||||
toCompare := make([]byte, 4)
|
toCompare := make([]byte, 4)
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
toCompare[i] = tgMagicByte
|
toCompare[i] = 0xee
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, toCompare, makeFrame().Magic())
|
assert.Equal(t, toCompare, makeFrame().Magic())
|
||||||
@@ -40,10 +43,13 @@ func TestFrameDC(t *testing.T) {
|
|||||||
|
|
||||||
func TestFrameValid(t *testing.T) {
|
func TestFrameValid(t *testing.T) {
|
||||||
frame := makeFrame()
|
frame := makeFrame()
|
||||||
assert.True(t, frame.Valid())
|
connType, err := frame.ConnectionType()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, connType, mtproto.ConnectionTypeIntermediate)
|
||||||
|
|
||||||
frame[8+32+16+2] = byte(3)
|
frame[8+32+16+2] = byte(3)
|
||||||
assert.False(t, frame.Valid())
|
_, err = frame.ConnectionType()
|
||||||
|
assert.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFrameDoubleInvert(t *testing.T) {
|
func TestFrameDoubleInvert(t *testing.T) {
|
||||||
@@ -66,7 +72,18 @@ func TestFrameInvert(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFrameGenerateValid(t *testing.T) {
|
func TestFrameGenerateValid(t *testing.T) {
|
||||||
assert.True(t, generateFrame().Valid())
|
validTests := []mtproto.ConnectionType{
|
||||||
|
mtproto.ConnectionTypeIntermediate,
|
||||||
|
mtproto.ConnectionTypeAbridged,
|
||||||
|
}
|
||||||
|
for _, test := range validTests {
|
||||||
|
t.Run(strconv.Itoa(int(test)), func(tt *testing.T) {
|
||||||
|
frame := generateFrame(test)
|
||||||
|
conType, err := frame.ConnectionType()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, conType, test)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeFrame() Frame {
|
func makeFrame() Frame {
|
||||||
@@ -79,7 +96,7 @@ func makeFrame() Frame {
|
|||||||
f[i] = byte(2)
|
f[i] = byte(2)
|
||||||
}
|
}
|
||||||
for i := (8 + 32 + 16); i < (8 + 32 + 16 + 4); i++ {
|
for i := (8 + 32 + 16); i < (8 + 32 + 16 + 4); i++ {
|
||||||
f[i] = tgMagicByte
|
f[i] = 0xee
|
||||||
}
|
}
|
||||||
for i := (8 + 32 + 16 + 4); i < (8 + 32 + 16 + 4 + 2); i++ {
|
for i := (8 + 32 + 16 + 4); i < (8 + 32 + 16 + 4 + 2); i++ {
|
||||||
f[i] = byte(3)
|
f[i] = byte(3)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Obfuscated2 contains AES CTR encryption and decryption streams
|
// Obfuscated2 contains AES CTR encryption and decryption streams
|
||||||
@@ -19,7 +21,7 @@ type Obfuscated2 struct {
|
|||||||
// details: http://telegra.ph/telegram-blocks-wtf-05-26
|
// details: http://telegra.ph/telegram-blocks-wtf-05-26
|
||||||
//
|
//
|
||||||
// Beware, link above is in russian.
|
// Beware, link above is in russian.
|
||||||
func ParseObfuscated2ClientFrame(secret []byte, frame *Frame) (*Obfuscated2, int16, error) {
|
func ParseObfuscated2ClientFrame(secret []byte, frame *Frame) (*Obfuscated2, *mtproto.ConnectionOpts, error) {
|
||||||
decHasher := sha256.New()
|
decHasher := sha256.New()
|
||||||
decHasher.Write(frame.Key()) // nolint: errcheck
|
decHasher.Write(frame.Key()) // nolint: errcheck
|
||||||
decHasher.Write(secret) // nolint: errcheck
|
decHasher.Write(secret) // nolint: errcheck
|
||||||
@@ -34,23 +36,28 @@ func ParseObfuscated2ClientFrame(secret []byte, frame *Frame) (*Obfuscated2, int
|
|||||||
decryptedFrame := MakeFrame()
|
decryptedFrame := MakeFrame()
|
||||||
defer ReturnFrame(decryptedFrame)
|
defer ReturnFrame(decryptedFrame)
|
||||||
decryptor.XORKeyStream(*decryptedFrame, *frame)
|
decryptor.XORKeyStream(*decryptedFrame, *frame)
|
||||||
if !decryptedFrame.Valid() {
|
connType, err := decryptedFrame.ConnectionType()
|
||||||
return nil, 0, errors.New("Unknown protocol")
|
if err != nil {
|
||||||
|
return nil, nil, errors.Annotate(err, "Unknown protocol")
|
||||||
}
|
}
|
||||||
|
|
||||||
obfs := &Obfuscated2{
|
obfs := &Obfuscated2{
|
||||||
Decryptor: decryptor,
|
Decryptor: decryptor,
|
||||||
Encryptor: encryptor,
|
Encryptor: encryptor,
|
||||||
}
|
}
|
||||||
|
connOpts := &mtproto.ConnectionOpts{
|
||||||
|
DC: decryptedFrame.DC(),
|
||||||
|
ConnectionType: connType,
|
||||||
|
}
|
||||||
|
|
||||||
return obfs, decryptedFrame.DC(), nil
|
return obfs, connOpts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeTelegramObfuscated2Frame creates new handshake frame to send to
|
// MakeTelegramObfuscated2Frame creates new handshake frame to send to
|
||||||
// Telegram.
|
// Telegram.
|
||||||
// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
|
// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
|
||||||
func MakeTelegramObfuscated2Frame() (*Obfuscated2, *Frame) {
|
func MakeTelegramObfuscated2Frame(opts *mtproto.ConnectionOpts) (*Obfuscated2, *Frame) {
|
||||||
frame := generateFrame()
|
frame := generateFrame(opts.ConnectionType)
|
||||||
|
|
||||||
encryptor := makeStreamCipher(frame.Key(), frame.IV())
|
encryptor := makeStreamCipher(frame.Key(), frame.IV())
|
||||||
decryptorFrame := frame.Invert()
|
decryptorFrame := frame.Invert()
|
||||||
|
|||||||
@@ -5,20 +5,31 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestObfs2TelegramFrameDecrypt(t *testing.T) {
|
func TestObfs2TelegramFrameDecrypt(t *testing.T) {
|
||||||
_, frame := MakeTelegramObfuscated2Frame()
|
connOpts := &mtproto.ConnectionOpts{
|
||||||
|
DC: 1,
|
||||||
|
ConnectionType: mtproto.ConnectionTypeIntermediate,
|
||||||
|
}
|
||||||
|
_, frame := MakeTelegramObfuscated2Frame(connOpts)
|
||||||
decryptor := makeStreamCipher(frame.Key(), frame.IV())
|
decryptor := makeStreamCipher(frame.Key(), frame.IV())
|
||||||
|
|
||||||
decrypted := make(Frame, FrameLen)
|
decrypted := make(Frame, FrameLen)
|
||||||
decryptor.XORKeyStream(decrypted, *frame)
|
decryptor.XORKeyStream(decrypted, *frame)
|
||||||
|
|
||||||
assert.True(t, decrypted.Valid())
|
_, err := decrypted.ConnectionType()
|
||||||
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObfs2TelegramDecryptEncryptDecrypt(t *testing.T) {
|
func TestObfs2TelegramDecryptEncryptDecrypt(t *testing.T) {
|
||||||
obfs2, frame := MakeTelegramObfuscated2Frame()
|
connOpts := &mtproto.ConnectionOpts{
|
||||||
|
DC: 1,
|
||||||
|
ConnectionType: mtproto.ConnectionTypeIntermediate,
|
||||||
|
}
|
||||||
|
obfs2, frame := MakeTelegramObfuscated2Frame(connOpts)
|
||||||
inverted := frame.Invert()
|
inverted := frame.Invert()
|
||||||
encryptor := makeStreamCipher(inverted.Key(), inverted.IV())
|
encryptor := makeStreamCipher(inverted.Key(), inverted.IV())
|
||||||
|
|
||||||
@@ -34,7 +45,7 @@ func TestObfs2TelegramDecryptEncryptDecrypt(t *testing.T) {
|
|||||||
func TestObfs2Full(t *testing.T) {
|
func TestObfs2Full(t *testing.T) {
|
||||||
secret := []byte{1, 2, 3, 4, 5}
|
secret := []byte{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
clientFrame := generateFrame()
|
clientFrame := generateFrame(mtproto.ConnectionTypeIntermediate)
|
||||||
clientHasher := sha256.New()
|
clientHasher := sha256.New()
|
||||||
clientHasher.Write(clientFrame.Key())
|
clientHasher.Write(clientFrame.Key())
|
||||||
clientHasher.Write(secret)
|
clientHasher.Write(secret)
|
||||||
@@ -55,11 +66,16 @@ func TestObfs2Full(t *testing.T) {
|
|||||||
clientObfs, _, err := ParseObfuscated2ClientFrame(secret, &encrypted)
|
clientObfs, _, err := ParseObfuscated2ClientFrame(secret, &encrypted)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
tgObfs, tgFrame := MakeTelegramObfuscated2Frame()
|
connOpts := &mtproto.ConnectionOpts{
|
||||||
|
DC: 1,
|
||||||
|
ConnectionType: mtproto.ConnectionTypeIntermediate,
|
||||||
|
}
|
||||||
|
tgObfs, tgFrame := MakeTelegramObfuscated2Frame(connOpts)
|
||||||
tgDecryptor := makeStreamCipher(tgFrame.Key(), tgFrame.IV())
|
tgDecryptor := makeStreamCipher(tgFrame.Key(), tgFrame.IV())
|
||||||
decrypted := make(Frame, FrameLen)
|
decrypted := make(Frame, FrameLen)
|
||||||
tgDecryptor.XORKeyStream(decrypted, *tgFrame)
|
tgDecryptor.XORKeyStream(decrypted, *tgFrame)
|
||||||
assert.True(t, decrypted.Valid())
|
_, err = decrypted.ConnectionType()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
tgInvertedFrame := tgFrame.Invert()
|
tgInvertedFrame := tgFrame.Invert()
|
||||||
tgEncryptor := makeStreamCipher(tgInvertedFrame.Key(), tgInvertedFrame.IV())
|
tgEncryptor := makeStreamCipher(tgInvertedFrame.Key(), tgInvertedFrame.IV())
|
||||||
|
|||||||
+10
-9
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/9seconds/mtg/client"
|
"github.com/9seconds/mtg/client"
|
||||||
"github.com/9seconds/mtg/config"
|
"github.com/9seconds/mtg/config"
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
"github.com/9seconds/mtg/telegram"
|
"github.com/9seconds/mtg/telegram"
|
||||||
"github.com/9seconds/mtg/wrappers"
|
"github.com/9seconds/mtg/wrappers"
|
||||||
)
|
)
|
||||||
@@ -60,7 +61,7 @@ func (s *Server) accept(conn net.Conn) {
|
|||||||
"socketid", socketID,
|
"socketid", socketID,
|
||||||
)
|
)
|
||||||
|
|
||||||
dc, clientConn, err := s.getClientStream(ctx, cancel, conn, socketID)
|
connOpts, clientConn, err := s.getClientStream(ctx, cancel, conn, socketID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warnw("Cannot initialize client connection",
|
s.logger.Warnw("Cannot initialize client connection",
|
||||||
"addr", conn.RemoteAddr().String(),
|
"addr", conn.RemoteAddr().String(),
|
||||||
@@ -71,7 +72,7 @@ func (s *Server) accept(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
defer clientConn.Close() // nolint: errcheck
|
defer clientConn.Close() // nolint: errcheck
|
||||||
|
|
||||||
tgConn, err := s.getTelegramStream(ctx, cancel, dc, socketID)
|
tgConn, err := s.getTelegramStream(ctx, cancel, connOpts, socketID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warnw("Cannot initialize Telegram connection",
|
s.logger.Warnw("Cannot initialize Telegram connection",
|
||||||
"socketid", socketID,
|
"socketid", socketID,
|
||||||
@@ -96,27 +97,27 @@ func (s *Server) accept(conn net.Conn) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getClientStream(ctx context.Context, cancel context.CancelFunc, conn net.Conn, socketID string) (int16, io.ReadWriteCloser, error) {
|
func (s *Server) getClientStream(ctx context.Context, cancel context.CancelFunc, conn net.Conn, socketID string) (*mtproto.ConnectionOpts, io.ReadWriteCloser, error) {
|
||||||
dc, socket, err := s.clientInit(conn, s.conf)
|
connOpts, socket, err := s.clientInit(conn, s.conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, errors.Annotate(err, "Cannot init client connection")
|
return nil, nil, errors.Annotate(err, "Cannot init client connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
socket = wrappers.NewTrafficRWC(socket, s.stats.addIncomingTraffic, s.stats.addOutgoingTraffic)
|
socket = wrappers.NewTrafficRWC(socket, s.stats.addIncomingTraffic, s.stats.addOutgoingTraffic)
|
||||||
socket = wrappers.NewLogRWC(socket, s.logger, socketID, "client")
|
socket = wrappers.NewLogRWC(socket, s.logger, socketID, "client")
|
||||||
socket = wrappers.NewCtxRWC(ctx, cancel, socket)
|
socket = wrappers.NewCtxRWC(ctx, cancel, socket)
|
||||||
|
|
||||||
return dc, socket, nil
|
return connOpts, socket, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getTelegramStream(ctx context.Context, cancel context.CancelFunc, dc int16, socketID string) (io.ReadWriteCloser, error) {
|
func (s *Server) getTelegramStream(ctx context.Context, cancel context.CancelFunc, connOpts *mtproto.ConnectionOpts, socketID string) (io.ReadWriteCloser, error) {
|
||||||
conn, err := s.tg.Dial(dc)
|
conn, err := s.tg.Dial(connOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotate(err, "Cannot connect to Telegram")
|
return nil, errors.Annotate(err, "Cannot connect to Telegram")
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = wrappers.NewTrafficRWC(conn, s.stats.addIncomingTraffic, s.stats.addOutgoingTraffic)
|
conn = wrappers.NewTrafficRWC(conn, s.stats.addIncomingTraffic, s.stats.addOutgoingTraffic)
|
||||||
conn, err = s.tg.Init(conn)
|
conn, err = s.tg.Init(connOpts, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotate(err, "Cannot handshake Telegram")
|
return nil, errors.Annotate(err, "Cannot handshake Telegram")
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -45,8 +45,8 @@ func (t *tgDialer) dialRWC(addr string) (io.ReadWriteCloser, error) {
|
|||||||
return wrappers.NewTimeoutRWC(conn, t.conf.TimeoutRead, t.conf.TimeoutWrite), nil
|
return wrappers.NewTimeoutRWC(conn, t.conf.TimeoutRead, t.conf.TimeoutWrite), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDialer(conf *config.Config) *tgDialer {
|
func newDialer(conf *config.Config) tgDialer {
|
||||||
return &tgDialer{
|
return tgDialer{
|
||||||
Dialer: net.Dialer{Timeout: conf.TimeoutRead},
|
Dialer: net.Dialer{Timeout: conf.TimeoutRead},
|
||||||
conf: conf,
|
conf: conf,
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/config"
|
"github.com/9seconds/mtg/config"
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
"github.com/9seconds/mtg/obfuscated2"
|
"github.com/9seconds/mtg/obfuscated2"
|
||||||
"github.com/9seconds/mtg/wrappers"
|
"github.com/9seconds/mtg/wrappers"
|
||||||
)
|
)
|
||||||
@@ -31,18 +32,19 @@ type directTelegram struct {
|
|||||||
baseTelegram
|
baseTelegram
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *directTelegram) Dial(dcIdx int16) (io.ReadWriteCloser, error) {
|
func (t *directTelegram) Dial(connOpts *mtproto.ConnectionOpts) (io.ReadWriteCloser, error) {
|
||||||
if dcIdx < 0 {
|
dc := connOpts.DC
|
||||||
dcIdx = -dcIdx
|
if dc < 0 {
|
||||||
} else if dcIdx == 0 {
|
dc = -dc
|
||||||
dcIdx = 1
|
} else if dc == 0 {
|
||||||
|
dc = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.baseTelegram.Dial(dcIdx - 1)
|
return t.baseTelegram.dial(dc - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *directTelegram) Init(conn io.ReadWriteCloser) (io.ReadWriteCloser, error) {
|
func (t *directTelegram) Init(connOpts *mtproto.ConnectionOpts, conn io.ReadWriteCloser) (io.ReadWriteCloser, error) {
|
||||||
obfs2, frame := obfuscated2.MakeTelegramObfuscated2Frame()
|
obfs2, frame := obfuscated2.MakeTelegramObfuscated2Frame(connOpts)
|
||||||
defer obfuscated2.ReturnFrame(frame)
|
defer obfuscated2.ReturnFrame(frame)
|
||||||
|
|
||||||
if n, err := conn.Write(*frame); err != nil || n != len(*frame) {
|
if n, err := conn.Write(*frame); err != nil || n != len(*frame) {
|
||||||
|
|||||||
@@ -5,24 +5,26 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/mtproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Telegram defines an interface to connect to Telegram. This
|
// Telegram defines an interface to connect to Telegram. This
|
||||||
// encapsulates logic of working with middleproxies or direct
|
// encapsulates logic of working with middleproxies or direct
|
||||||
// connections.
|
// connections.
|
||||||
type Telegram interface {
|
type Telegram interface {
|
||||||
Dial(int16) (io.ReadWriteCloser, error)
|
Dial(*mtproto.ConnectionOpts) (io.ReadWriteCloser, error)
|
||||||
Init(io.ReadWriteCloser) (io.ReadWriteCloser, error)
|
Init(*mtproto.ConnectionOpts, io.ReadWriteCloser) (io.ReadWriteCloser, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type baseTelegram struct {
|
type baseTelegram struct {
|
||||||
dialer *tgDialer
|
dialer tgDialer
|
||||||
|
|
||||||
v4Addresses map[int16][]string
|
v4Addresses map[int16][]string
|
||||||
v6Addresses map[int16][]string
|
v6Addresses map[int16][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *baseTelegram) Dial(dcIdx int16) (io.ReadWriteCloser, error) {
|
func (b *baseTelegram) dial(dcIdx int16) (io.ReadWriteCloser, error) {
|
||||||
addrs := make([]string, 2)
|
addrs := make([]string, 2)
|
||||||
if addr, ok := b.v6Addresses[dcIdx]; ok && len(addr) > 0 {
|
if addr, ok := b.v6Addresses[dcIdx]; ok && len(addr) > 0 {
|
||||||
addrs = append(addrs, addr[rand.Intn(len(addr))])
|
addrs = append(addrs, addr[rand.Intn(len(addr))])
|
||||||
|
|||||||
Reference in New Issue
Block a user