mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 18:34:02 +03:00
Direct proxy works
This commit is contained in:
@@ -1,87 +0,0 @@
|
||||
package mtproto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
// ConnectionType is a type of obfuscated2/mtproto connection requested
|
||||
// by the user.
|
||||
type ConnectionType uint8
|
||||
|
||||
// ConnectionProtocol is a type of IP protocol to use.
|
||||
type ConnectionProtocol uint8
|
||||
|
||||
// Hacks is a simple structure to store flags for packet transmission.
|
||||
type Hacks struct {
|
||||
SimpleAck bool
|
||||
QuickAck bool
|
||||
}
|
||||
|
||||
// ConnectionOpts presents an options, metadata on connection requested
|
||||
// by the user on handshake.
|
||||
type ConnectionOpts struct {
|
||||
DC int16
|
||||
ConnectionType ConnectionType
|
||||
ConnectionProto ConnectionProtocol
|
||||
// Read and Write means direction related to the client.
|
||||
// ReadHacks are meant to be flushed on client read
|
||||
// WriteHacks are meant to be flushed on client write.
|
||||
ReadHacks Hacks
|
||||
WriteHacks Hacks
|
||||
ClientAddr *net.TCPAddr
|
||||
}
|
||||
|
||||
// Different connection types which user requests from Telegram.
|
||||
const (
|
||||
ConnectionTypeUnknown ConnectionType = iota
|
||||
ConnectionTypeAbridged
|
||||
ConnectionTypeIntermediate
|
||||
ConnectionTypeSecure
|
||||
)
|
||||
|
||||
// ConnectionProtocol* define which connection protocols to use.
|
||||
// ConnectionProtocolAny means that any is suitable.
|
||||
const (
|
||||
ConnectionProtocolIPv4 ConnectionProtocol = 1
|
||||
ConnectionProtocolIPv6 = ConnectionProtocolIPv4 << 1
|
||||
ConnectionProtocolAny = ConnectionProtocolIPv4 | ConnectionProtocolIPv6
|
||||
)
|
||||
|
||||
// Connection tags for mtproto handshakes.
|
||||
var (
|
||||
ConnectionTagAbridged = []byte{0xef, 0xef, 0xef, 0xef}
|
||||
ConnectionTagIntermediate = []byte{0xee, 0xee, 0xee, 0xee}
|
||||
ConnectionTagSecure = []byte{0xdd, 0xdd, 0xdd, 0xdd}
|
||||
)
|
||||
|
||||
// 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
|
||||
case ConnectionTypeSecure:
|
||||
return ConnectionTagSecure, 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
|
||||
}
|
||||
if bytes.Equal(magic, ConnectionTagSecure) {
|
||||
return ConnectionTypeSecure, nil
|
||||
}
|
||||
|
||||
return ConnectionTypeUnknown, errors.New("Unknown handshake protocol")
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import "bytes"
|
||||
|
||||
// HandshakeRequest is the data type which is responsible for
|
||||
// constructing of correct handshake request.
|
||||
type HandshakeRequest struct {
|
||||
}
|
||||
|
||||
// Bytes returns serialized handshake request.
|
||||
func (r *HandshakeRequest) Bytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
buf.Grow(len(TagHandshake) + len(HandshakeFlags) + len(HandshakeSenderPID) + len(HandshakePeerPID))
|
||||
|
||||
buf.Write(TagHandshake) // nolint: gosec
|
||||
buf.Write(HandshakeFlags) // nolint: gosec
|
||||
buf.Write(HandshakeSenderPID) // nolint: gosec
|
||||
buf.Write(HandshakePeerPID) // nolint: gosec
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// NewHandshakeRequest creates new HandshakeRequest instance.
|
||||
func NewHandshakeRequest() *HandshakeRequest {
|
||||
return &HandshakeRequest{}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
// HandshakeResponse defines data structure which is used for storage of
|
||||
// handshake response.
|
||||
type HandshakeResponse struct {
|
||||
Type []byte
|
||||
Flags []byte
|
||||
SenderPID []byte
|
||||
PeerPID []byte
|
||||
}
|
||||
|
||||
// Bytes returns a serialized handshake response.
|
||||
func (r *HandshakeResponse) Bytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.Write(r.Type) // nolint: gosec
|
||||
buf.Write(r.Flags) // nolint: gosec
|
||||
buf.Write(r.SenderPID) // nolint: gosec
|
||||
buf.Write(r.PeerPID) // nolint: gosec
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Valid checks that handshake response compliments request.
|
||||
func (r *HandshakeResponse) Valid(req *HandshakeRequest) error {
|
||||
if !bytes.Equal(r.Type, TagHandshake) {
|
||||
return errors.New("Unexpected handshake tag")
|
||||
}
|
||||
if !bytes.Equal(r.PeerPID, HandshakeSenderPID) {
|
||||
return errors.New("Incorrect sender PID")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewHandshakeResponse constructs new handshake response from the given
|
||||
// data.
|
||||
func NewHandshakeResponse(data []byte) (*HandshakeResponse, error) {
|
||||
if len(data) != 32 {
|
||||
return nil, errors.New("Incorrect handshake response length")
|
||||
}
|
||||
|
||||
return &HandshakeResponse{
|
||||
Type: data[:4],
|
||||
Flags: data[4:8],
|
||||
SenderPID: data[8:20],
|
||||
PeerPID: data[20:],
|
||||
}, nil
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
// NonceRequest is the data type which contains all the data for correct
|
||||
// nonce request.
|
||||
type NonceRequest struct {
|
||||
KeySelector []byte
|
||||
CryptoTS []byte
|
||||
Nonce []byte
|
||||
}
|
||||
|
||||
// Bytes returns serialized nonce request.
|
||||
func (r *NonceRequest) Bytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.Write(TagNonce) // nolint: gosec
|
||||
buf.Write(r.KeySelector) // nolint: gosec
|
||||
buf.Write(NonceCryptoAES) // nolint: gosec
|
||||
buf.Write(r.CryptoTS) // nolint: gosec
|
||||
buf.Write(r.Nonce) // nolint: gosec
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// NewNonceRequest builds new none request based on proxy secret.
|
||||
func NewNonceRequest(proxySecret []byte) (*NonceRequest, error) {
|
||||
nonce := make([]byte, 16)
|
||||
keySelector := make([]byte, 4)
|
||||
cryptoTS := make([]byte, 4)
|
||||
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, errors.Annotate(err, "Cannot generate nonce")
|
||||
}
|
||||
copy(keySelector, proxySecret)
|
||||
|
||||
timestamp := time.Now().Truncate(time.Second).Unix() % 4294967296 // 256 ^ 4 - do not know how to name
|
||||
binary.LittleEndian.PutUint32(cryptoTS, uint32(timestamp))
|
||||
|
||||
return &NonceRequest{
|
||||
KeySelector: keySelector,
|
||||
CryptoTS: cryptoTS,
|
||||
Nonce: nonce,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
// NonceResponse is the data type which contains data of nonce response.
|
||||
type NonceResponse struct {
|
||||
NonceRequest
|
||||
|
||||
Type []byte
|
||||
Crypto []byte
|
||||
}
|
||||
|
||||
// Bytes returns serialized form of the nonce response.
|
||||
func (r *NonceResponse) Bytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.Write(r.Type) // nolint: gosec
|
||||
buf.Write(r.KeySelector) // nolint: gosec
|
||||
buf.Write(r.Crypto) // nolint: gosec
|
||||
buf.Write(r.CryptoTS) // nolint: gosec
|
||||
buf.Write(r.Nonce) // nolint: gosec
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Valid checks that nonce response compliments nonce request.
|
||||
func (r *NonceResponse) Valid(req *NonceRequest) error {
|
||||
if !bytes.Equal(r.Type, TagNonce) {
|
||||
return errors.New("Unexpected RPC type")
|
||||
}
|
||||
if !bytes.Equal(r.Crypto, NonceCryptoAES) {
|
||||
return errors.New("Unexpected crypto type")
|
||||
}
|
||||
if !bytes.Equal(r.KeySelector, req.KeySelector) {
|
||||
return errors.New("Unexpected key selector")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewNonceResponse build new nonce response based on the given data.
|
||||
func NewNonceResponse(data []byte) (*NonceResponse, error) {
|
||||
if len(data) != 32 {
|
||||
return nil, errors.New("Unexpected message length")
|
||||
}
|
||||
|
||||
return &NonceResponse{
|
||||
NonceRequest: NonceRequest{
|
||||
KeySelector: data[4:8],
|
||||
CryptoTS: data[12:16],
|
||||
Nonce: data[16:],
|
||||
},
|
||||
Type: data[:4],
|
||||
Crypto: data[8:12],
|
||||
}, nil
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type proxyRequestFlags uint32
|
||||
|
||||
const (
|
||||
proxyRequestFlagsHasAdTag proxyRequestFlags = 0x8
|
||||
proxyRequestFlagsEncrypted proxyRequestFlags = 0x2
|
||||
proxyRequestFlagsMagic proxyRequestFlags = 0x1000
|
||||
proxyRequestFlagsExtMode2 proxyRequestFlags = 0x20000
|
||||
proxyRequestFlagsIntermediate proxyRequestFlags = 0x20000000
|
||||
proxyRequestFlagsAbdridged proxyRequestFlags = 0x40000000
|
||||
proxyRequestFlagsQuickAck proxyRequestFlags = 0x80000000
|
||||
proxyRequestFlagsPad proxyRequestFlags = 0x8000000
|
||||
)
|
||||
|
||||
var proxyRequestFlagsEncryptedPrefix [8]byte
|
||||
|
||||
func (r proxyRequestFlags) Bytes() []byte {
|
||||
converted := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(converted, uint32(r))
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
func (r proxyRequestFlags) String() string {
|
||||
flags := make([]string, 0, 7)
|
||||
|
||||
if r&proxyRequestFlagsHasAdTag != 0 {
|
||||
flags = append(flags, "HAS_AD_TAG")
|
||||
}
|
||||
if r&proxyRequestFlagsEncrypted != 0 {
|
||||
flags = append(flags, "ENCRYPTED")
|
||||
}
|
||||
if r&proxyRequestFlagsMagic != 0 {
|
||||
flags = append(flags, "MAGIC")
|
||||
}
|
||||
if r&proxyRequestFlagsExtMode2 != 0 {
|
||||
flags = append(flags, "EXT_MODE_2")
|
||||
}
|
||||
if r&proxyRequestFlagsIntermediate != 0 {
|
||||
flags = append(flags, "INTERMEDIATE")
|
||||
}
|
||||
if r&proxyRequestFlagsAbdridged != 0 {
|
||||
flags = append(flags, "ABRIDGED")
|
||||
}
|
||||
if r&proxyRequestFlagsQuickAck != 0 {
|
||||
flags = append(flags, "QUICK_ACK")
|
||||
}
|
||||
if r&proxyRequestFlagsPad != 0 {
|
||||
flags = append(flags, "PAD")
|
||||
}
|
||||
|
||||
return strings.Join(flags, " | ")
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/juju/errors"
|
||||
|
||||
"github.com/9seconds/mtg/mtproto"
|
||||
)
|
||||
|
||||
// ProxyRequest is the data type for storing data required to compose
|
||||
// RPC_PROXY_REQ request.
|
||||
type ProxyRequest struct {
|
||||
Flags proxyRequestFlags
|
||||
ConnectionID []byte
|
||||
OurIPPort []byte
|
||||
ClientIPPort []byte
|
||||
ADTag []byte
|
||||
Options *mtproto.ConnectionOpts
|
||||
}
|
||||
|
||||
// MakeHeader makes RPC_PROXY_REQ header. We need only to append the
|
||||
// data for it.
|
||||
func (r *ProxyRequest) MakeHeader(message []byte) (*bytes.Buffer, fmt.Stringer) {
|
||||
bufferLength := len(TagProxyRequest) +
|
||||
4 + // len(flags)
|
||||
len(r.ConnectionID) +
|
||||
len(r.ClientIPPort) +
|
||||
len(r.OurIPPort) +
|
||||
len(ProxyRequestExtraSize) +
|
||||
len(ProxyRequestProxyTag) +
|
||||
1 + // len(AdTag)
|
||||
len(r.ADTag)
|
||||
bufferLength += bufferLength % 4
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
buf.Grow(bufferLength + len(message))
|
||||
|
||||
flags := r.Flags
|
||||
if r.Options.ReadHacks.QuickAck {
|
||||
flags |= proxyRequestFlagsQuickAck
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(message, proxyRequestFlagsEncryptedPrefix[:]) {
|
||||
flags |= proxyRequestFlagsEncrypted
|
||||
}
|
||||
|
||||
buf.Write(TagProxyRequest) // nolint: gosec
|
||||
buf.Write(flags.Bytes()) // nolint: gosec
|
||||
buf.Write(r.ConnectionID) // nolint: gosec
|
||||
buf.Write(r.ClientIPPort) // nolint: gosec
|
||||
buf.Write(r.OurIPPort) // nolint: gosec
|
||||
buf.Write(ProxyRequestExtraSize) // nolint: gosec
|
||||
buf.Write(ProxyRequestProxyTag) // nolint: gosec
|
||||
buf.WriteByte(byte(len(r.ADTag))) // nolint: gosec
|
||||
buf.Write(r.ADTag) // nolint: gosec
|
||||
buf.Write(make([]byte, (4-buf.Len()%4)%4)) // nolint: gosec
|
||||
|
||||
return buf, flags
|
||||
}
|
||||
|
||||
// NewProxyRequest build new ProxyRequest data structure.
|
||||
func NewProxyRequest(clientAddr, ownAddr *net.TCPAddr,
|
||||
opts *mtproto.ConnectionOpts, adTag []byte) (*ProxyRequest, error) {
|
||||
flags := proxyRequestFlagsHasAdTag | proxyRequestFlagsMagic | proxyRequestFlagsExtMode2
|
||||
|
||||
switch opts.ConnectionType {
|
||||
case mtproto.ConnectionTypeAbridged:
|
||||
flags |= proxyRequestFlagsAbdridged
|
||||
case mtproto.ConnectionTypeIntermediate:
|
||||
flags |= proxyRequestFlagsIntermediate
|
||||
case mtproto.ConnectionTypeSecure:
|
||||
flags |= proxyRequestFlagsIntermediate | proxyRequestFlagsPad
|
||||
default:
|
||||
panic("Unknown connection type")
|
||||
}
|
||||
|
||||
request := &ProxyRequest{
|
||||
Flags: flags,
|
||||
ADTag: adTag,
|
||||
Options: opts,
|
||||
ConnectionID: make([]byte, 8),
|
||||
ClientIPPort: make([]byte, 16+4),
|
||||
OurIPPort: make([]byte, 16+4),
|
||||
}
|
||||
|
||||
if _, err := rand.Read(request.ConnectionID); err != nil {
|
||||
return nil, errors.Annotate(err, "Cannot generate connection ID")
|
||||
}
|
||||
|
||||
port := [4]byte{}
|
||||
copy(request.ClientIPPort[:16], clientAddr.IP.To16())
|
||||
binary.LittleEndian.PutUint32(port[:], uint32(clientAddr.Port))
|
||||
copy(request.ClientIPPort[16:], port[:])
|
||||
|
||||
copy(request.OurIPPort[:16], ownAddr.IP.To16())
|
||||
binary.LittleEndian.PutUint32(port[:], uint32(ownAddr.Port))
|
||||
copy(request.OurIPPort[16:], port[:])
|
||||
|
||||
return request, nil
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package rpc
|
||||
|
||||
// SeqNo* is the number of the sequence which have special meaning for
|
||||
// the Telegram.
|
||||
const (
|
||||
SeqNoNonce = -2
|
||||
SeqNoHandshake = -1
|
||||
)
|
||||
|
||||
// Different constants for RPC protocol
|
||||
var (
|
||||
TagCloseExt = []byte{0xa2, 0x34, 0xb6, 0x5e}
|
||||
TagProxyAns = []byte{0x0d, 0xda, 0x03, 0x44}
|
||||
TagSimpleAck = []byte{0x9b, 0x40, 0xac, 0x3b}
|
||||
TagHandshake = []byte{0xf5, 0xee, 0x82, 0x76}
|
||||
TagNonce = []byte{0xaa, 0x87, 0xcb, 0x7a}
|
||||
TagProxyRequest = []byte{0xee, 0xf1, 0xce, 0x36}
|
||||
|
||||
NonceCryptoAES = []byte{0x01, 0x00, 0x00, 0x00}
|
||||
|
||||
HandshakeFlags = []byte{0x00, 0x00, 0x00, 0x00}
|
||||
|
||||
ProxyRequestExtraSize = []byte{0x18, 0x00, 0x00, 0x00}
|
||||
ProxyRequestProxyTag = []byte{0xae, 0x26, 0x1e, 0xdb}
|
||||
|
||||
HandshakeSenderPID = []byte("IPIPPRPDTIME")
|
||||
HandshakePeerPID = []byte("IPIPPRPDTIME")
|
||||
)
|
||||
Reference in New Issue
Block a user