mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 15:04:03 +03:00
Working version
This commit is contained in:
Generated
+56
-1
@@ -16,6 +16,61 @@
|
||||
packages = ["."]
|
||||
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/juju/errors"
|
||||
packages = ["."]
|
||||
revision = "c7d06af17c68cd34c835053720b21f6549d9b0ee"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/satori/go.uuid"
|
||||
packages = ["."]
|
||||
revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/atomic"
|
||||
packages = ["."]
|
||||
revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289"
|
||||
version = "v1.3.2"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/multierr"
|
||||
packages = ["."]
|
||||
revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/zap"
|
||||
packages = [
|
||||
".",
|
||||
"buffer",
|
||||
"internal/bufferpool",
|
||||
"internal/color",
|
||||
"internal/exit",
|
||||
"zapcore"
|
||||
]
|
||||
revision = "eeedf312bc6c57391d84767a4cd413f02a917974"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/alecthomas/kingpin.v2"
|
||||
packages = ["."]
|
||||
@@ -25,6 +80,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "9b3ace12d2b928915476e7d30a29e7e37e0a9d569b37ad070fb0b2d0e06fc088"
|
||||
inputs-digest = "36bcaf7701d70b8c4648e01a7fc94e0cda9c2f9b48c42d453c87567f6f058036"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
+12
@@ -32,3 +32,15 @@
|
||||
[[constraint]]
|
||||
name = "gopkg.in/alecthomas/kingpin.v2"
|
||||
version = "2.2.6"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/juju/errors"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/satori/go.uuid"
|
||||
version = "1.2.0"
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
||||
|
||||
"github.com/9seconds/mtg/server"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -43,7 +47,8 @@ var (
|
||||
func main() {
|
||||
kingpin.MustParse(app.Parse(os.Args[1:]))
|
||||
|
||||
if matched, err := regexp.MatchString("[a-fA-F0-9]+", *secret); !matched || err != nil {
|
||||
secretBytes, err := hex.DecodeString(*secret)
|
||||
if err != nil {
|
||||
usage("Secret has to be hexadecimal string.")
|
||||
}
|
||||
|
||||
@@ -61,8 +66,23 @@ func main() {
|
||||
*serverName = strings.TrimSpace(string(myIPBytes))
|
||||
}
|
||||
|
||||
atom := zap.NewAtomicLevel()
|
||||
if *debug {
|
||||
atom.SetLevel(zapcore.DebugLevel)
|
||||
} else {
|
||||
atom.SetLevel(zapcore.ErrorLevel)
|
||||
}
|
||||
encoderCfg := zap.NewProductionEncoderConfig()
|
||||
logger := zap.New(zapcore.NewCore(
|
||||
zapcore.NewJSONEncoder(encoderCfg),
|
||||
zapcore.Lock(os.Stderr),
|
||||
atom,
|
||||
)).Sugar()
|
||||
|
||||
printURLs()
|
||||
serve()
|
||||
if err := server.NewServer(*bindIP, int(*bindPort), secretBytes, logger).Serve(); err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func usage(msg string) {
|
||||
@@ -88,7 +108,3 @@ func printURLs() {
|
||||
tgURL.Path = "proxy"
|
||||
fmt.Println(tgURL.String())
|
||||
}
|
||||
|
||||
func serve() {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package obfuscated2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
|
||||
// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
|
||||
const (
|
||||
frameLenKey = 32
|
||||
frameLenIV = 16
|
||||
frameLenMagic = 4
|
||||
frameLenDC = 2
|
||||
|
||||
frameOffsetFirst = 8
|
||||
frameOffsetKey = frameOffsetFirst + frameLenKey
|
||||
frameOffsetIV = frameOffsetKey + frameLenIV
|
||||
frameOffsetMagic = frameOffsetIV + frameLenMagic
|
||||
frameOffsetDC = frameOffsetMagic + frameLenDC
|
||||
|
||||
tgMagicByte = byte(239)
|
||||
|
||||
FrameLen = 64
|
||||
)
|
||||
|
||||
var tgMagicBytes = []byte{tgMagicByte, tgMagicByte, tgMagicByte, tgMagicByte}
|
||||
|
||||
type Frame []byte
|
||||
|
||||
func (f Frame) Key() []byte {
|
||||
return f[frameOffsetFirst:frameOffsetKey]
|
||||
}
|
||||
|
||||
func (f Frame) IV() []byte {
|
||||
return f[frameOffsetKey:frameOffsetIV]
|
||||
}
|
||||
|
||||
func (f Frame) Magic() []byte {
|
||||
return f[frameOffsetIV:frameOffsetMagic]
|
||||
}
|
||||
|
||||
func (f Frame) DC() (n int16) {
|
||||
buf := bytes.NewReader(f[frameOffsetMagic:frameOffsetDC])
|
||||
binary.Read(buf, binary.LittleEndian, &n)
|
||||
|
||||
if n < 0 {
|
||||
n = -n
|
||||
} else if n == 0 {
|
||||
n = 1
|
||||
}
|
||||
|
||||
return n - 1
|
||||
}
|
||||
|
||||
func (f Frame) Valid() bool {
|
||||
return bytes.Equal(f.Magic(), tgMagicBytes)
|
||||
}
|
||||
|
||||
func (f Frame) Invert() Frame {
|
||||
reversed := make(Frame, FrameLen)
|
||||
copy(reversed, f)
|
||||
|
||||
for i := 0; i < frameLenKey+frameLenIV; i++ {
|
||||
reversed[frameOffsetFirst+i] = f[frameOffsetIV-1-i]
|
||||
}
|
||||
|
||||
return reversed
|
||||
}
|
||||
|
||||
func ExtractFrame(conn io.Reader) (Frame, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
if _, err := io.CopyN(buf, conn, FrameLen); err != nil {
|
||||
return nil, errors.Annotate(err, "Cannot extract obfuscated header")
|
||||
}
|
||||
|
||||
return Frame(buf.Bytes()), nil
|
||||
}
|
||||
|
||||
func generateFrame() Frame {
|
||||
data := make(Frame, FrameLen)
|
||||
|
||||
for {
|
||||
if _, err := rand.Read(data); err != nil {
|
||||
continue
|
||||
}
|
||||
if data[0] == 0xef {
|
||||
continue
|
||||
}
|
||||
|
||||
val := (uint32(data[3]) << 24) | (uint32(data[2]) << 16) | (uint32(data[1]) << 8) | uint32(data[0])
|
||||
if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee {
|
||||
continue
|
||||
}
|
||||
|
||||
val = (uint32(data[7]) << 24) | (uint32(data[6]) << 16) | (uint32(data[5]) << 8) | uint32(data[4])
|
||||
if val == 0x00000000 {
|
||||
continue
|
||||
}
|
||||
|
||||
copy(data.Magic(), tgMagicBytes)
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package obfuscated2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFrameKey(t *testing.T) {
|
||||
toCompare := make([]byte, 32)
|
||||
for i := 0; i < 32; i++ {
|
||||
toCompare[i] = byte(1)
|
||||
}
|
||||
|
||||
assert.Equal(t, toCompare, makeFrame().Key())
|
||||
}
|
||||
|
||||
func TestFrameIV(t *testing.T) {
|
||||
toCompare := make([]byte, 16)
|
||||
for i := 0; i < 16; i++ {
|
||||
toCompare[i] = byte(2)
|
||||
}
|
||||
|
||||
assert.Equal(t, toCompare, makeFrame().IV())
|
||||
}
|
||||
|
||||
func TestFrameMagic(t *testing.T) {
|
||||
toCompare := make([]byte, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
toCompare[i] = tgMagicByte
|
||||
}
|
||||
|
||||
assert.Equal(t, toCompare, makeFrame().Magic())
|
||||
}
|
||||
|
||||
func TestFrameDC(t *testing.T) {
|
||||
assert.Equal(t, int16(771), makeFrame().DC())
|
||||
}
|
||||
|
||||
func TestFrameValid(t *testing.T) {
|
||||
frame := makeFrame()
|
||||
assert.True(t, frame.Valid())
|
||||
|
||||
frame[8+32+16+2] = byte(3)
|
||||
assert.False(t, frame.Valid())
|
||||
}
|
||||
|
||||
func TestFrameInvert(t *testing.T) {
|
||||
frame := makeFrame()
|
||||
reversed := frame.Invert()
|
||||
|
||||
assert.Exactly(t, frame[:8], reversed[:8])
|
||||
assert.Exactly(t, frame[56:], reversed[56:])
|
||||
|
||||
toCompare := make([]byte, 48)
|
||||
for i := 0; i < 48; i++ {
|
||||
toCompare[i] = frame[55-i]
|
||||
}
|
||||
assert.Equal(t, []byte(reversed[8:56]), toCompare)
|
||||
}
|
||||
|
||||
func TestFrameGenerateValid(t *testing.T) {
|
||||
assert.True(t, generateFrame().Valid())
|
||||
}
|
||||
|
||||
func makeFrame() Frame {
|
||||
f := make(Frame, FrameLen)
|
||||
|
||||
for i := 8; i < (8 + 32); i++ {
|
||||
f[i] = byte(1)
|
||||
}
|
||||
for i := (8 + 32); i < (8 + 32 + 16); i++ {
|
||||
f[i] = byte(2)
|
||||
}
|
||||
for i := (8 + 32 + 16); i < (8 + 32 + 16 + 4); i++ {
|
||||
f[i] = tgMagicByte
|
||||
}
|
||||
for i := (8 + 32 + 16 + 4); i < (8 + 32 + 16 + 4 + 2); i++ {
|
||||
f[i] = byte(3)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package obfuscated2
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
type Obfuscated2 struct {
|
||||
decryptor cipher.Stream
|
||||
encryptor cipher.Stream
|
||||
}
|
||||
|
||||
func (o *Obfuscated2) Encrypt(data []byte) []byte {
|
||||
buf := make([]byte, len(data))
|
||||
o.encryptor.XORKeyStream(buf, data)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (o *Obfuscated2) Decrypt(data []byte) []byte {
|
||||
buf := make([]byte, len(data))
|
||||
o.decryptor.XORKeyStream(buf, data)
|
||||
return buf
|
||||
}
|
||||
|
||||
func ParseObfuscated2ClientFrame(secret, data []byte) (*Obfuscated2, int16, error) {
|
||||
frame := Frame(data)
|
||||
|
||||
decHasher := sha256.New()
|
||||
decHasher.Write(frame.Key())
|
||||
decHasher.Write(secret)
|
||||
decryptor := makeStreamCipher(decHasher.Sum(nil), frame.IV())
|
||||
|
||||
invertedFrame := frame.Invert()
|
||||
encHasher := sha256.New()
|
||||
encHasher.Write(invertedFrame.Key())
|
||||
encHasher.Write(secret)
|
||||
encryptor := makeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
|
||||
|
||||
decryptedFrame := make(Frame, FrameLen)
|
||||
decryptor.XORKeyStream(decryptedFrame, frame)
|
||||
if !decryptedFrame.Valid() {
|
||||
return nil, 0, errors.New("Unknown protocol")
|
||||
}
|
||||
|
||||
obfs := &Obfuscated2{
|
||||
decryptor: decryptor,
|
||||
encryptor: encryptor,
|
||||
}
|
||||
|
||||
return obfs, decryptedFrame.DC(), nil
|
||||
}
|
||||
|
||||
func MakeTelegramObfuscated2Frame() (*Obfuscated2, Frame) {
|
||||
frame := generateFrame()
|
||||
|
||||
encryptor := makeStreamCipher(frame.Key(), frame.IV())
|
||||
decryptorFrame := frame.Invert()
|
||||
decryptor := makeStreamCipher(decryptorFrame.Key(), decryptorFrame.IV())
|
||||
|
||||
copyFrame := make(Frame, frameOffsetIV)
|
||||
copy(copyFrame, frame)
|
||||
encryptor.XORKeyStream(frame, frame)
|
||||
copy(frame, copyFrame)
|
||||
|
||||
obfs := &Obfuscated2{
|
||||
decryptor: decryptor,
|
||||
encryptor: encryptor,
|
||||
}
|
||||
|
||||
return obfs, frame
|
||||
}
|
||||
|
||||
func makeStreamCipher(key, iv []byte) cipher.Stream {
|
||||
block, _ := aes.NewCipher(key)
|
||||
return cipher.NewCTR(block, iv)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package obfuscated2
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestObfs2TelegramFrameDecrypt(t *testing.T) {
|
||||
_, frame := MakeTelegramObfuscated2Frame()
|
||||
decryptor := makeStreamCipher(frame.Key(), frame.IV())
|
||||
|
||||
decrypted := make(Frame, FrameLen)
|
||||
decryptor.XORKeyStream(decrypted, frame)
|
||||
|
||||
assert.True(t, decrypted.Valid())
|
||||
}
|
||||
|
||||
func TestObfs2TelegramDecryptEncryptDecrypt(t *testing.T) {
|
||||
obfs2, frame := MakeTelegramObfuscated2Frame()
|
||||
inverted := frame.Invert()
|
||||
encryptor := makeStreamCipher(inverted.Key(), inverted.IV())
|
||||
|
||||
data := []byte{1, 2, 3}
|
||||
encrypted := make([]byte, 3)
|
||||
encryptor.XORKeyStream(encrypted, data)
|
||||
decrypted := obfs2.Decrypt(encrypted)
|
||||
|
||||
assert.Equal(t, data, decrypted)
|
||||
}
|
||||
|
||||
func TestObfs2Full(t *testing.T) {
|
||||
secret := []byte{1, 2, 3, 4, 5}
|
||||
|
||||
clientFrame := generateFrame()
|
||||
clientHasher := sha256.New()
|
||||
clientHasher.Write(clientFrame.Key())
|
||||
clientHasher.Write(secret)
|
||||
clientKey := clientHasher.Sum(nil)
|
||||
|
||||
encryptor := makeStreamCipher(clientKey, clientFrame.IV())
|
||||
encrypted := make(Frame, FrameLen)
|
||||
encryptor.XORKeyStream(encrypted, clientFrame)
|
||||
copy(encrypted[:56], clientFrame[:56])
|
||||
|
||||
invertedClientFrame := clientFrame.Invert()
|
||||
clientHasher = sha256.New()
|
||||
clientHasher.Write(invertedClientFrame.Key())
|
||||
clientHasher.Write(secret)
|
||||
invertedClientKey := clientHasher.Sum(nil)
|
||||
clientDecryptor := makeStreamCipher(invertedClientKey, invertedClientFrame.IV())
|
||||
|
||||
clientObfs, _, err := ParseObfuscated2ClientFrame(secret, encrypted)
|
||||
assert.Nil(t, err)
|
||||
|
||||
tgObfs, tgFrame := MakeTelegramObfuscated2Frame()
|
||||
tgDecryptor := makeStreamCipher(tgFrame.Key(), tgFrame.IV())
|
||||
decrypted := make(Frame, FrameLen)
|
||||
tgDecryptor.XORKeyStream(decrypted, tgFrame)
|
||||
assert.True(t, decrypted.Valid())
|
||||
|
||||
tgInvertedFrame := tgFrame.Invert()
|
||||
tgEncryptor := makeStreamCipher(tgInvertedFrame.Key(), tgInvertedFrame.IV())
|
||||
|
||||
message := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
tgEncryptedMessage := make([]byte, len(message))
|
||||
tgEncryptor.XORKeyStream(tgEncryptedMessage, message)
|
||||
|
||||
tgEncDecryptedMessage := tgObfs.Decrypt(tgEncryptedMessage)
|
||||
assert.Equal(t, message, tgEncDecryptedMessage)
|
||||
|
||||
clientEncryptedMessage := clientObfs.Encrypt(tgEncDecryptedMessage)
|
||||
finalMessage := make([]byte, len(clientEncryptedMessage))
|
||||
clientDecryptor.XORKeyStream(finalMessage, clientEncryptedMessage)
|
||||
|
||||
assert.Equal(t, finalMessage, message)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Cipher interface {
|
||||
Encrypt([]byte) []byte
|
||||
Decrypt([]byte) []byte
|
||||
}
|
||||
|
||||
type CipherReadWriteCloser struct {
|
||||
crypt Cipher
|
||||
conn io.ReadWriteCloser
|
||||
rest *bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *CipherReadWriteCloser) Read(p []byte) (n int, err error) {
|
||||
n, err = c.conn.Read(p)
|
||||
copy(p, c.crypt.Decrypt(p[:n]))
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CipherReadWriteCloser) Write(p []byte) (n int, err error) {
|
||||
c.rest.Write(c.crypt.Encrypt(p))
|
||||
newP := c.rest.Bytes()[:len(p)]
|
||||
n, err = c.conn.Write(newP)
|
||||
c.rest = bytes.NewBuffer(c.rest.Bytes()[n:])
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CipherReadWriteCloser) Close() error {
|
||||
var err1 error
|
||||
if c.rest.Len() > 0 {
|
||||
_, err1 = c.conn.Write(c.rest.Bytes())
|
||||
}
|
||||
err2 := c.conn.Close()
|
||||
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err1
|
||||
}
|
||||
|
||||
func newCipherReadWriteCloser(conn io.ReadWriteCloser, crypt Cipher) io.ReadWriteCloser {
|
||||
return &CipherReadWriteCloser{
|
||||
conn: conn,
|
||||
crypt: crypt,
|
||||
rest: &bytes.Buffer{},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
type CtxReadWriteCloser struct {
|
||||
ctx context.Context
|
||||
conn io.ReadWriteCloser
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (c *CtxReadWriteCloser) Read(p []byte) (int, error) {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return 0, errors.Annotate(c.ctx.Err(), "Read is failed because of closed context")
|
||||
default:
|
||||
n, err := c.conn.Read(p)
|
||||
if err != nil {
|
||||
c.cancel()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CtxReadWriteCloser) Write(p []byte) (int, error) {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return 0, errors.Annotate(c.ctx.Err(), "Write is failed because of closed context")
|
||||
default:
|
||||
n, err := c.conn.Write(p)
|
||||
if err != nil {
|
||||
c.cancel()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CtxReadWriteCloser) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func newCtxReadWriteCloser(conn io.ReadWriteCloser, ctx context.Context, cancel context.CancelFunc) io.ReadWriteCloser {
|
||||
return &CtxReadWriteCloser{
|
||||
conn: conn,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type LogReadWriteCloser struct {
|
||||
conn io.ReadWriteCloser
|
||||
logger *zap.SugaredLogger
|
||||
sockid string
|
||||
name string
|
||||
}
|
||||
|
||||
func (l *LogReadWriteCloser) Read(p []byte) (n int, err error) {
|
||||
n, err = l.conn.Read(p)
|
||||
l.logger.Debugw("Finish reading", "name", l.name, "socketid", l.sockid, "nbytes", n, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
func (l *LogReadWriteCloser) Write(p []byte) (n int, err error) {
|
||||
n, err = l.conn.Write(p)
|
||||
l.logger.Debugw("Finish writing", "name", l.name, "socketid", l.sockid, "nbytes", n, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
func (l *LogReadWriteCloser) Close() error {
|
||||
err := l.conn.Close()
|
||||
l.logger.Debugw("Finish closing socket", "name", l.name, "socketid", l.sockid, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
func newLogReadWriteCloser(conn io.ReadWriteCloser, logger *zap.SugaredLogger, sockid string, name string) io.ReadWriteCloser {
|
||||
return &LogReadWriteCloser{
|
||||
conn: conn,
|
||||
logger: logger,
|
||||
sockid: sockid,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/9seconds/mtg/obfuscated2"
|
||||
"github.com/juju/errors"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
ip net.IP
|
||||
port int
|
||||
secret []byte
|
||||
logger *zap.SugaredLogger
|
||||
lsock net.Listener
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (s *Server) Serve() error {
|
||||
lsock, err := net.Listen("tcp", s.Addr())
|
||||
if err != nil {
|
||||
return errors.Annotate(err, "Cannot create listen socket")
|
||||
}
|
||||
|
||||
for {
|
||||
if conn, err := lsock.Accept(); err != nil {
|
||||
s.logger.Warn("Cannot allocate incoming connection", "error", err)
|
||||
} else {
|
||||
go s.accept(conn)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Addr() string {
|
||||
return net.JoinHostPort(s.ip.String(), strconv.Itoa(s.port))
|
||||
}
|
||||
|
||||
func (s *Server) accept(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
socketID := s.makeSocketID()
|
||||
|
||||
s.logger.Debugw("Client connected",
|
||||
"secret", s.secret,
|
||||
"addr", conn.RemoteAddr().String(),
|
||||
"socketid", socketID,
|
||||
)
|
||||
|
||||
clientCipher, dc := s.getClientStream(conn, ctx, cancel, socketID)
|
||||
// if err != nil {
|
||||
// s.logger.Warnw("Cannot initialize client connection",
|
||||
// "secret", s.secret,
|
||||
// "addr", conn.RemoteAddr().String(),
|
||||
// "socketid", socketID,
|
||||
// "error", err,
|
||||
// )
|
||||
// return
|
||||
// }
|
||||
|
||||
tgConn, tgCipher := s.getTelegramStream(dc, ctx, cancel, socketID)
|
||||
// if err != nil {
|
||||
// s.logger.Warnw("Cannot initialize Telegram connection",
|
||||
// "socketid", socketID,
|
||||
// "error", err,
|
||||
// )
|
||||
// return
|
||||
// }
|
||||
defer tgConn.Close()
|
||||
|
||||
wait := &sync.WaitGroup{}
|
||||
wait.Add(2)
|
||||
go func() {
|
||||
buf := make([]byte, 128)
|
||||
for {
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
decrypted := clientCipher.Decrypt(buf[:n])
|
||||
encrypted := tgCipher.Encrypt(decrypted)
|
||||
tgConn.Write(encrypted)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
buf := make([]byte, 128)
|
||||
for {
|
||||
n, err := tgConn.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
decrypted := tgCipher.Decrypt(buf[:n])
|
||||
encrypted := clientCipher.Encrypt(decrypted)
|
||||
conn.Write(encrypted)
|
||||
}
|
||||
}()
|
||||
// go func() {
|
||||
// buf := make([]byte, 128)
|
||||
// for {
|
||||
// n, err := conn.
|
||||
// }
|
||||
// defer wait.Done()
|
||||
// io.Copy(tgConn, clientConn)
|
||||
// }()
|
||||
// go func() {
|
||||
// defer wait.Done()
|
||||
// io.Copy(clientConn, tgConn)
|
||||
// }()
|
||||
<-ctx.Done()
|
||||
wait.Wait()
|
||||
|
||||
s.logger.Debugw("Client disconnected",
|
||||
"secret", s.secret,
|
||||
"addr", conn.RemoteAddr().String(),
|
||||
"socketid", socketID,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Server) makeSocketID() string {
|
||||
return uuid.NewV4().String()
|
||||
}
|
||||
|
||||
func (s *Server) getClientStream(conn net.Conn, ctx context.Context, cancel context.CancelFunc, socketID string) (Cipher, int16) {
|
||||
frame, err := obfuscated2.ExtractFrame(conn)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
// return nil, 0, errors.Annotate(err, "Cannot create client stream")
|
||||
}
|
||||
|
||||
obfs2, dc, err := obfuscated2.ParseObfuscated2ClientFrame(s.secret, frame)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
// return nil, 0, errors.Annotate(err, "Cannot create client stream")
|
||||
}
|
||||
|
||||
return obfs2, dc
|
||||
|
||||
// cipherConn := newCipherReadWriteCloser(conn, obfs2)
|
||||
// ctxConn := newCtxReadWriteCloser(cipherConn, ctx, cancel)
|
||||
// // logConn := newLogReadWriteCloser(ctxConn, s.logger, socketID, "client")
|
||||
|
||||
// return ctxConn, dc, nil
|
||||
}
|
||||
|
||||
func (s *Server) getTelegramStream(dc int16, ctx context.Context, cancel context.CancelFunc, socketID string) (io.ReadWriteCloser, Cipher) {
|
||||
socket, err := dialToTelegram(dc)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
// return nil, errors.Annotate(err, "Cannot dial")
|
||||
}
|
||||
|
||||
obfs2, frame := obfuscated2.MakeTelegramObfuscated2Frame()
|
||||
if n, err := socket.Write(frame); err != nil || n != len(frame) {
|
||||
fmt.Println(err)
|
||||
// return nil, errors.Annotate(err, "Cannot write hadnshake frame")
|
||||
}
|
||||
|
||||
return socket, obfs2
|
||||
|
||||
// cipherConn := newCipherReadWriteCloser(socket, obfs2)
|
||||
// ctxConn := newCtxReadWriteCloser(cipherConn, ctx, cancel)
|
||||
// logConn := newLogReadWriteCloser(ctxConn, s.logger, socketID, "telegram")
|
||||
|
||||
// return logConn, nil
|
||||
}
|
||||
|
||||
func NewServer(ip net.IP, port int, secret []byte, logger *zap.SugaredLogger) *Server {
|
||||
return &Server{
|
||||
ip: ip,
|
||||
port: port,
|
||||
secret: secret,
|
||||
ctx: context.Background(),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
var telegramDCIPs = [5]string{
|
||||
"149.154.175.50:443",
|
||||
"149.154.167.51:443",
|
||||
"149.154.175.100:443",
|
||||
"149.154.167.91:443",
|
||||
"149.154.171.5:443",
|
||||
}
|
||||
|
||||
const telegramKeepAlive = 30 * time.Second
|
||||
|
||||
func dialToTelegram(dcIdx int16) (net.Conn, error) {
|
||||
if dcIdx < 0 || dcIdx >= 5 {
|
||||
return nil, errors.New("Incorrect DC IDX")
|
||||
}
|
||||
|
||||
conn, err := net.Dial("tcp", telegramDCIPs[dcIdx])
|
||||
if err != nil {
|
||||
return nil, errors.Annotate(err, "Cannot dial")
|
||||
}
|
||||
|
||||
// if err := conn.SetKeepAlive(true); err != nil {
|
||||
// return nil, errors.Annotate(err, "Cannot establish keepalive connection")
|
||||
// }
|
||||
// if err := conn.SetKeepAlivePeriod(telegramKeepAlive); err != nil {
|
||||
// return nil, errors.Annotate(err, "Cannot set keepalive timeout")
|
||||
// }
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
Reference in New Issue
Block a user