Compare commits

..

No commits in common. "d2764075300bd8579bd828086944233f30e6abbc" and "8e8649db838cd4f40ebc5b02ff7dbd83c7de8791" have entirely different histories.

7 changed files with 122 additions and 202 deletions

View File

@ -20,13 +20,12 @@
``` ```
## 功能特性 ## 功能特性
- ~~TLS连接多路复用,减少TLS握手次数~~(存在特征已经移除) - TLS连接多路复用,减少TLS握手次数
## 安全性特别说明 ## 安全性特别说明
- 包装的TCP流量没有加密,如有需求请加密后再转发 - 包装的TCP流量没有加密,如有需求请加密后再转发
- ~~多路复用特性使用了smux的框架,有协议特征,有更进一步需求需修改源码二次加密~~(已经移除多路复用,如有需求请在外层处理) - 多路复用特性使用了smux的框架,有协议特征,有更进一步需求需修改源码二次加密
- ~~TLS后续流量没有进行Application Data封装,深层次的协议分析可以发现此特征~~(已经封装,已与标准的TLS协议一致) - TLS后续流量没有进行Application Data封装,深层次的协议分析可以发现此特征
## 特别说明 ## 特别说明
- 感谢v2ex网友ihciah的思路灵感. - 感谢v2ex网友ihciah的思路灵感.

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.18
require ( require (
github.com/spf13/cobra v1.5.0 github.com/spf13/cobra v1.5.0
github.com/xtaci/smux v1.5.16
) )
require ( require (

View File

@ -1,11 +1,9 @@
package shadow package shadow
import ( import (
"crypto/tls"
"fmt" "fmt"
"io" "io"
"net" "net"
"time"
) )
type Client struct { type Client struct {
@ -24,6 +22,8 @@ func NewClient(listenAddress string, serverAddress string, fakeAddressSNI string
} }
func (c *Client) Start() { func (c *Client) Start() {
bridge := NewTLSBridge(c.ServerAddress, c.FakeAddressSNI)
listen, err := net.Listen("tcp", c.ListenAddress) listen, err := net.Listen("tcp", c.ListenAddress)
if err != nil { if err != nil {
fmt.Printf("[Client] Start client error: %v\n", err) fmt.Printf("[Client] Start client error: %v\n", err)
@ -37,61 +37,15 @@ func (c *Client) Start() {
fmt.Printf("[Client] accept error: %v\n", err) fmt.Printf("[Client] accept error: %v\n", err)
continue continue
} }
go handlerClient(conn, c.ServerAddress, c.FakeAddressSNI)
}
}
func handlerClient(conn net.Conn, serverAddress string, fakeAddressSNI string) { stream := bridge.GetStream()
dial, err := tls.DialWithDialer(&net.Dialer{ if stream == nil {
Timeout: time.Second * 5, conn.Close()
}, "tcp", serverAddress, &tls.Config{ fmt.Println("[Client] connect to server error")
ServerName: fakeAddressSNI, continue
})
if err != nil {
fmt.Printf("[Client] Dial server error: %v\n", err)
return
}
err = dial.Handshake()
if err != nil {
fmt.Printf("[Client] Handshake error: %v\n", err)
return
}
dial.NetConn().SetDeadline(time.Now())
dial.NetConn().SetDeadline(time.Time{})
p := &PackAppData{Conn: dial.NetConn()}
defer p.Close()
defer conn.Close()
exitCh := make(chan int, 1)
go MyCopy(conn, p, exitCh)
go MyCopy(p, conn, exitCh)
<-exitCh
}
func MyCopy(src io.ReadWriteCloser, dst io.ReadWriteCloser, ch chan int) {
buf := make([]byte, 32*1024)
for {
nr, er := src.Read(buf)
if er != nil {
if er == io.EOF {
break
} else {
fmt.Printf("Read err: %v\n", er)
break
}
} else {
nw, ew := dst.Write(buf[0:nr])
if ew != nil {
fmt.Printf("Write error:%v\n", ew)
break
}
if nr != nw {
fmt.Printf("Write less then buffered \n")
break
}
} }
fmt.Printf("[Client] New TCP connection: %v <-> %v \n", conn.LocalAddr().String(), conn.RemoteAddr().String())
go io.Copy(conn, stream)
go io.Copy(stream, conn)
} }
ch <- 1
} }

View File

@ -23,14 +23,15 @@ func TestName(t *testing.T) {
} }
func TestName2(t *testing.T) { func TestName2(t *testing.T) {
b := []byte("ABC") dial, err := tls.DialWithDialer(&net.Dialer{
encrypt, err := AesEncrypt(b, []byte("1234567812345678")) Timeout: time.Second * 5,
}, "tcp", "evan.run:443", &tls.Config{
ServerName: "evan.run",
})
err = dial.Handshake()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
decrypt, err := AesDecrypt(encrypt, []byte("1234567812345678")) time.Sleep(time.Minute)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(decrypt))
} }

View File

@ -1,70 +0,0 @@
package shadow
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
)
var (
AppDataHeader = []byte{0x17, 0x3, 0x3}
HeaderLength = len(AppDataHeader)
)
type PackAppData struct {
Conn net.Conn
}
func (m PackAppData) Read(p []byte) (n int, err error) {
buf := make([]byte, 32*1024+HeaderLength+2)
headRead, err := io.ReadAtLeast(m.Conn, buf[0:HeaderLength+2], HeaderLength+2)
if err != nil {
fmt.Printf("Read header error: %v\n", err)
return 0, err
}
if headRead < HeaderLength+2 {
return 0, errors.New("Read header failed")
}
if bytes.Equal(buf[0:HeaderLength], AppDataHeader) {
payLoadLength := int(binary.BigEndian.Uint16(buf[HeaderLength : HeaderLength+2]))
sum := 0
for sum < payLoadLength {
r, e := m.Conn.Read(buf[HeaderLength+2+sum : HeaderLength+2+payLoadLength])
if e != nil {
if e == io.EOF {
break
} else {
return 0, e
}
}
copy(p[sum:], buf[HeaderLength+2+sum:HeaderLength+2+sum+r])
sum += r
}
return sum, err
} else {
fmt.Printf("Invalid header")
return 0, errors.New("invalid header")
}
}
func (m PackAppData) Write(p []byte) (n int, err error) {
lenNum := make([]byte, 2)
binary.BigEndian.PutUint16(lenNum, uint16(len(p)))
packetBuf := bytes.NewBuffer(AppDataHeader)
packetBuf.Write(lenNum)
packetBuf.Write(p)
write, err := m.Conn.Write(packetBuf.Bytes())
write = write - HeaderLength - 2
return write, err
}
func (m PackAppData) Close() error {
return m.Conn.Close()
}

View File

@ -2,6 +2,7 @@ package shadow
import ( import (
"fmt" "fmt"
"github.com/xtaci/smux"
"io" "io"
"net" "net"
"time" "time"
@ -60,76 +61,21 @@ func handler(conn net.Conn, targetAddress string, fakeAddress string) {
conn.SetDeadline(time.Now()) conn.SetDeadline(time.Now())
conn.SetDeadline(time.Time{}) conn.SetDeadline(time.Time{})
realConnection, err := net.Dial("tcp", targetAddress) //Process real tcp connection
session, err := smux.Server(conn, nil)
if err != nil { if err != nil {
fmt.Printf("[Server] Dial target error : %v\n", err) fmt.Printf("[Server] smux error: %v\n", err)
return return
} }
if err != nil { for {
return stream, err := session.AcceptStream()
if err != nil {
fmt.Printf("[Server] AcceptStream error: %v\n", err)
break
}
go handlerMux(stream, targetAddress)
} }
p := &PackAppData{Conn: conn}
defer p.Close()
defer realConnection.Close()
exit := make(chan int, 1)
go MyCopy(p, realConnection, exit)
go MyCopy(realConnection, p, exit)
<-exit
//go func() {
// buf := make([]byte, 64*1024)
// for {
// nr, er := realConnection.Read(buf)
// if er != nil {
// if er == io.EOF {
// continue
// } else {
// fmt.Println("read err:", er)
// break
// }
// } else {
// lenNum := make([]byte, 2)
// binary.BigEndian.PutUint16(lenNum, uint16(nr))
//
// packetBuf := bytes.NewBuffer(AppDataHeader)
// packetBuf.Write(lenNum)
// packetBuf.Write(buf[0:nr])
//
// _, ew := conn.Write(packetBuf.Bytes())
// if ew != nil {
// fmt.Printf("err2:%v\n", ew)
// break
// }
// }
// }
//}()
//
//go func() {
// result := bytes.NewBuffer(nil)
// var buf [65542]byte // 由于 标识数据包长度 的只有两个字节 故数据包最大为 2^16+4(魔数)+2(长度标识)
// for {
// n, er := conn.Read(buf[0:])
// result.Write(buf[0:n])
// if er != nil {
// if er == io.EOF {
// continue
// } else {
// fmt.Println("read err:", er)
// break
// }
// } else {
// scanner := bufio.NewScanner(result)
// scanner.Split(packetSlitFunc)
// for scanner.Scan() {
// realConnection.Write(scanner.Bytes()[HeaderLength+2:])
// }
// }
// result.Reset()
// }
//}()
} }
func processHandshake(src net.Conn, dst net.Conn, waitCh chan int) { func processHandshake(src net.Conn, dst net.Conn, waitCh chan int) {
@ -177,3 +123,17 @@ func processHandshake(src net.Conn, dst net.Conn, waitCh chan int) {
} }
waitCh <- 1 waitCh <- 1
} }
func handlerMux(conn *smux.Stream, targetAddress string) {
realConnection, err := net.Dial("tcp", targetAddress)
if err != nil {
fmt.Printf("[Server] Dial target error : %v\n", err)
return
}
if err != nil {
return
}
go io.Copy(realConnection, conn)
go io.Copy(conn, realConnection)
}

75
shadow/tls_bridge.go Normal file
View File

@ -0,0 +1,75 @@
package shadow
import (
"crypto/tls"
"github.com/xtaci/smux"
"net"
"sync"
"time"
)
type TLSBridge struct {
session *smux.Session
locker sync.Mutex
serverAddress string
fakeAddressSNI string
}
func NewTLSBridge(serverAddress string, fakeAddressSNI string) *TLSBridge {
t := &TLSBridge{
session: nil,
locker: sync.Mutex{},
serverAddress: serverAddress,
fakeAddressSNI: fakeAddressSNI,
}
return t
}
func (t *TLSBridge) dial() error {
if t.session != nil {
t.session.Close()
}
dial, err := tls.DialWithDialer(&net.Dialer{
Timeout: time.Second * 5,
}, "tcp", t.serverAddress, &tls.Config{
ServerName: t.fakeAddressSNI,
})
if err != nil {
return err
}
err = dial.Handshake()
if err != nil {
return err
}
dial.NetConn().SetDeadline(time.Now())
dial.NetConn().SetDeadline(time.Time{})
time.Sleep(time.Millisecond * 100)
session, err := smux.Client(dial.NetConn(), nil)
if err != nil {
return err
}
//force openStream to prevent first connection problem
session.OpenStream()
t.session = session
return nil
}
func (t *TLSBridge) GetStream() *smux.Stream {
t.locker.Lock()
defer t.locker.Unlock()
if t.session == nil {
err := t.dial()
if err != nil {
return nil
}
}
openStream, err := t.session.OpenStream()
if err != nil {
t.session.Close()
t.session = nil
}
return openStream
}