mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 17:14:02 +03:00
Working version
This commit is contained in:
Generated
+56
-1
@@ -16,6 +16,61 @@
|
|||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
|
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]]
|
[[projects]]
|
||||||
name = "gopkg.in/alecthomas/kingpin.v2"
|
name = "gopkg.in/alecthomas/kingpin.v2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@@ -25,6 +80,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "9b3ace12d2b928915476e7d30a29e7e37e0a9d569b37ad070fb0b2d0e06fc088"
|
inputs-digest = "36bcaf7701d70b8c4648e01a7fc94e0cda9c2f9b48c42d453c87567f6f058036"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|||||||
+12
@@ -32,3 +32,15 @@
|
|||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "gopkg.in/alecthomas/kingpin.v2"
|
name = "gopkg.in/alecthomas/kingpin.v2"
|
||||||
version = "2.2.6"
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -43,7 +47,8 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
kingpin.MustParse(app.Parse(os.Args[1:]))
|
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.")
|
usage("Secret has to be hexadecimal string.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,8 +66,23 @@ func main() {
|
|||||||
*serverName = strings.TrimSpace(string(myIPBytes))
|
*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()
|
printURLs()
|
||||||
serve()
|
if err := server.NewServer(*bindIP, int(*bindPort), secretBytes, logger).Serve(); err != nil {
|
||||||
|
logger.Fatal(err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func usage(msg string) {
|
func usage(msg string) {
|
||||||
@@ -88,7 +108,3 @@ func printURLs() {
|
|||||||
tgURL.Path = "proxy"
|
tgURL.Path = "proxy"
|
||||||
fmt.Println(tgURL.String())
|
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