Add background update for middle proxy

This commit is contained in:
9seconds
2019-09-09 17:07:59 +03:00
parent d431feb0ba
commit 7827d1255a
7 changed files with 206 additions and 5 deletions
-4
View File
@@ -25,8 +25,4 @@ var (
HandshakeSenderPID = []byte("IPIPPRPDTIME") HandshakeSenderPID = []byte("IPIPPRPDTIME")
HandshakePeerPID = []byte("IPIPPRPDTIME") HandshakePeerPID = []byte("IPIPPRPDTIME")
HandshakeRequest = append(TagHandshake,
append(HandshakeFlags,
append(HandshakeSenderPID, HandshakePeerPID...)...)...)
) )
+7
View File
@@ -0,0 +1,7 @@
package rpc
import "github.com/9seconds/mtg/mtproto"
var HandshakeRequest = append(mtproto.TagHandshake,
append(mtproto.HandshakeFlags,
append(mtproto.HandshakeSenderPID, mtproto.HandshakePeerPID...)...)...)
+55
View File
@@ -0,0 +1,55 @@
package rpc
import (
"bytes"
"errors"
"fmt"
"github.com/9seconds/mtg/mtproto"
)
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() error {
if !bytes.Equal(r.Type, mtproto.TagHandshake) {
return errors.New("Unexpected handshake tag")
}
if !bytes.Equal(r.PeerPID, mtproto.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, fmt.Errorf("Incorrect handshake response length %d", len(data))
}
return &HandshakeResponse{
Type: data[:4],
Flags: data[4:8],
SenderPID: data[8:20],
PeerPID: data[20:],
}, nil
}
+51
View File
@@ -0,0 +1,51 @@
package rpc
import (
"bytes"
"crypto/rand"
"encoding/binary"
"fmt"
"time"
"github.com/9seconds/mtg/mtproto"
)
type NonceRequest struct {
KeySelector []byte
CryptoTS []byte
Nonce []byte
}
// Bytes returns serialized nonce request.
func (r *NonceRequest) Bytes() []byte {
buf := &bytes.Buffer{}
buf.Write(mtproto.TagNonce) // nolint: gosec
buf.Write(r.KeySelector) // nolint: gosec
buf.Write(mtproto.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, fmt.Errorf("cannot generate nonce: %w", err)
}
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
}
+60
View File
@@ -0,0 +1,60 @@
package rpc
import (
"bytes"
"errors"
"fmt"
"github.com/9seconds/mtg/mtproto"
)
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()
}
func (r *NonceResponse) Valid(req *NonceRequest) error {
if !bytes.Equal(r.Type, mtproto.TagNonce) {
return errors.New("Unexpected RPC type")
}
if !bytes.Equal(r.Crypto, mtproto.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, fmt.Errorf("Unexpected message length %d", len(data))
}
return &NonceResponse{
NonceRequest: NonceRequest{
KeySelector: data[4:8],
CryptoTS: data[12:16],
Nonce: data[16:],
},
Type: data[:4],
Crypto: data[8:12],
}, nil
}
@@ -1,4 +1,4 @@
package mtproto package rpc
import ( import (
"encoding/binary" "encoding/binary"
+32
View File
@@ -3,13 +3,19 @@ package telegram
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"sync" "sync"
"time"
"go.uber.org/zap"
"github.com/9seconds/mtg/conntypes" "github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/telegram/api" "github.com/9seconds/mtg/telegram/api"
"github.com/9seconds/mtg/wrappers" "github.com/9seconds/mtg/wrappers"
) )
const middleTelegramBackgroundUpdateEvery = time.Hour
type middleTelegram struct { type middleTelegram struct {
baseTelegram baseTelegram
@@ -44,6 +50,15 @@ func (m *middleTelegram) update() error {
return nil return nil
} }
func (m *middleTelegram) backgroundUpdate() {
logger := zap.S().Named("telegram")
for range time.Tick(middleTelegramBackgroundUpdateEvery) {
if err := m.update(); err != nil {
logger.Warnw("Cannot update Telegram proxies", "error", err)
}
}
}
func (m *middleTelegram) Dial(ctx context.Context, func (m *middleTelegram) Dial(ctx context.Context,
cancel context.CancelFunc, cancel context.CancelFunc,
dc conntypes.DC, dc conntypes.DC,
@@ -52,5 +67,22 @@ func (m *middleTelegram) Dial(ctx context.Context,
dc = conntypes.DCDefaultIdx dc = conntypes.DCDefaultIdx
} }
m.mutex.RLock()
defer m.mutex.RUnlock()
return m.baseTelegram.dial(ctx, cancel, dc, protocol) return m.baseTelegram.dial(ctx, cancel, dc, protocol)
} }
func NewMiddleTelegram() Telegram {
tg := &middleTelegram{
baseTelegram: baseTelegram{
dialer: net.Dialer{Timeout: telegramDialTimeout},
},
}
if err := tg.update(); err != nil {
panic(err)
}
go tg.backgroundUpdate()
return tg
}