mirror of
https://github.com/p4gefau1t/trojan-go.git
synced 2024-04-21 12:21:34 +00:00
add shadow, update api
This commit is contained in:
+34
-15
@@ -64,8 +64,10 @@ func (m *StatsRequest) GetPassword() string {
|
||||
}
|
||||
|
||||
type StatsReply struct {
|
||||
Upload uint64 `protobuf:"varint,1,opt,name=upload,proto3" json:"upload,omitempty"`
|
||||
Download uint64 `protobuf:"varint,2,opt,name=download,proto3" json:"download,omitempty"`
|
||||
UploadTraffic uint64 `protobuf:"varint,1,opt,name=upload_traffic,json=uploadTraffic,proto3" json:"upload_traffic,omitempty"`
|
||||
DownloadTraffic uint64 `protobuf:"varint,2,opt,name=download_traffic,json=downloadTraffic,proto3" json:"download_traffic,omitempty"`
|
||||
UploadSpeed uint64 `protobuf:"varint,3,opt,name=upload_speed,json=uploadSpeed,proto3" json:"upload_speed,omitempty"`
|
||||
DownloadSpeed uint64 `protobuf:"varint,4,opt,name=download_speed,json=downloadSpeed,proto3" json:"download_speed,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -96,16 +98,30 @@ func (m *StatsReply) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_StatsReply proto.InternalMessageInfo
|
||||
|
||||
func (m *StatsReply) GetUpload() uint64 {
|
||||
func (m *StatsReply) GetUploadTraffic() uint64 {
|
||||
if m != nil {
|
||||
return m.Upload
|
||||
return m.UploadTraffic
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *StatsReply) GetDownload() uint64 {
|
||||
func (m *StatsReply) GetDownloadTraffic() uint64 {
|
||||
if m != nil {
|
||||
return m.Download
|
||||
return m.DownloadTraffic
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *StatsReply) GetUploadSpeed() uint64 {
|
||||
if m != nil {
|
||||
return m.UploadSpeed
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *StatsReply) GetDownloadSpeed() uint64 {
|
||||
if m != nil {
|
||||
return m.DownloadSpeed
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -120,18 +136,21 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_00212fb1f9d3bf1c = []byte{
|
||||
// 161 bytes of a gzipped FileDescriptorProto
|
||||
// 214 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x2c, 0xc8, 0xd4,
|
||||
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x06, 0x32, 0x95, 0xb4, 0xb8, 0x78, 0x82, 0x4b, 0x12,
|
||||
0x4b, 0x8a, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0xa4, 0xb8, 0x38, 0x0a, 0x12, 0x8b,
|
||||
0x8b, 0xcb, 0xf3, 0x8b, 0x52, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xe0, 0x7c, 0x25, 0x07,
|
||||
0x2e, 0x2e, 0xa8, 0xda, 0x82, 0x9c, 0x4a, 0x21, 0x31, 0x2e, 0xb6, 0xd2, 0x82, 0x9c, 0xfc, 0x44,
|
||||
0x88, 0x3a, 0x96, 0x20, 0x28, 0x0f, 0x64, 0x42, 0x4a, 0x7e, 0x79, 0x1e, 0x58, 0x86, 0x09, 0x2c,
|
||||
0x03, 0xe7, 0x1b, 0x39, 0x73, 0xf1, 0x86, 0x14, 0xe5, 0x67, 0x25, 0xe6, 0x05, 0xa7, 0x16, 0x95,
|
||||
0x65, 0x26, 0xa7, 0x0a, 0x19, 0x71, 0x71, 0x05, 0x96, 0xa6, 0x16, 0x55, 0x82, 0xcd, 0x15, 0x12,
|
||||
0xd4, 0x03, 0xb9, 0x0e, 0xd9, 0x3d, 0x52, 0xfc, 0xc8, 0x42, 0x40, 0x6b, 0x95, 0x18, 0x92, 0xd8,
|
||||
0xc0, 0xce, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xea, 0x6a, 0xb8, 0xe4, 0xcb, 0x00, 0x00,
|
||||
0x00,
|
||||
0x8b, 0xcb, 0xf3, 0x8b, 0x52, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xe0, 0x7c, 0xa5, 0x15,
|
||||
0x8c, 0x5c, 0x5c, 0x50, 0xc5, 0x05, 0x39, 0x95, 0x42, 0xaa, 0x5c, 0x7c, 0xa5, 0x05, 0x39, 0xf9,
|
||||
0x89, 0x29, 0xf1, 0x25, 0x45, 0x89, 0x69, 0x69, 0x99, 0xc9, 0x60, 0x0d, 0x2c, 0x41, 0xbc, 0x10,
|
||||
0xd1, 0x10, 0x88, 0xa0, 0x90, 0x26, 0x97, 0x40, 0x4a, 0x7e, 0x79, 0x1e, 0x8a, 0x42, 0x26, 0xb0,
|
||||
0x42, 0x7e, 0x98, 0x38, 0x4c, 0xa9, 0x22, 0x17, 0x0f, 0xd4, 0xc4, 0xe2, 0x82, 0xd4, 0xd4, 0x14,
|
||||
0x09, 0x66, 0xb0, 0x32, 0x6e, 0x88, 0x58, 0x30, 0x48, 0x08, 0x64, 0x29, 0xdc, 0x34, 0x88, 0x22,
|
||||
0x16, 0x88, 0xa5, 0x30, 0x51, 0xb0, 0x32, 0x23, 0x67, 0x2e, 0xde, 0x90, 0xa2, 0xfc, 0xac, 0xc4,
|
||||
0xbc, 0xe0, 0xd4, 0xa2, 0xb2, 0xcc, 0xe4, 0x54, 0x21, 0x23, 0x2e, 0xae, 0xc0, 0xd2, 0xd4, 0xa2,
|
||||
0x4a, 0xb0, 0xfb, 0x85, 0x04, 0xf5, 0x40, 0xc1, 0x80, 0xec, 0x71, 0x29, 0x7e, 0x64, 0x21, 0xa0,
|
||||
0xf7, 0x94, 0x18, 0x92, 0xd8, 0xc0, 0xe1, 0x64, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x89, 0xd5,
|
||||
0x8f, 0xd9, 0x34, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
||||
+4
-2
@@ -10,6 +10,8 @@ message StatsRequest{
|
||||
}
|
||||
|
||||
message StatsReply {
|
||||
uint64 upload = 1;
|
||||
uint64 download = 2;
|
||||
uint64 upload_traffic = 1;
|
||||
uint64 download_traffic = 2;
|
||||
uint64 upload_speed = 3;
|
||||
uint64 download_speed = 4;
|
||||
}
|
||||
+27
-7
@@ -2,8 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/p4gefau1t/trojan-go/common"
|
||||
"github.com/p4gefau1t/trojan-go/conf"
|
||||
"github.com/p4gefau1t/trojan-go/log"
|
||||
"github.com/p4gefau1t/trojan-go/stat"
|
||||
@@ -13,26 +13,46 @@ import (
|
||||
|
||||
type ClientAPIService struct {
|
||||
TrojanServiceServer
|
||||
meter stat.TrafficMeter
|
||||
meter stat.TrafficMeter
|
||||
uploadSpeed uint64
|
||||
downloadSpeed uint64
|
||||
lastSent uint64
|
||||
lastRecv uint64
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (s *ClientAPIService) QueryStats(ctx context.Context, req *StatsRequest) (*StatsReply, error) {
|
||||
log.Debug("query stats, password", req.Password)
|
||||
password := req.Password
|
||||
passwordHash := common.SHA224String(password)
|
||||
sent, recv := s.meter.Query(passwordHash)
|
||||
//password := req.Password
|
||||
//passwordHash := common.SHA224String(password)
|
||||
sent, recv := s.meter.Query("")
|
||||
reply := &StatsReply{
|
||||
Upload: sent,
|
||||
Download: recv,
|
||||
UploadTraffic: sent,
|
||||
DownloadTraffic: recv,
|
||||
UploadSpeed: s.uploadSpeed,
|
||||
DownloadSpeed: s.downloadSpeed,
|
||||
}
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
func (s *ClientAPIService) calcSpeed() {
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
sent, recv := s.meter.Query("")
|
||||
s.uploadSpeed = sent - s.lastSent
|
||||
s.downloadSpeed = recv - s.lastRecv
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func RunClientAPIService(ctx context.Context, config *conf.GlobalConfig, meter stat.TrafficMeter) error {
|
||||
server := grpc.NewServer()
|
||||
service := &ClientAPIService{
|
||||
meter: meter,
|
||||
ctx: ctx,
|
||||
}
|
||||
go service.calcSpeed()
|
||||
RegisterTrojanServiceServer(server, service)
|
||||
listener, err := net.Listen("tcp", config.API.APIAddress.String())
|
||||
if err != nil {
|
||||
|
||||
+5
-3
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/p4gefau1t/trojan-go/common"
|
||||
"github.com/p4gefau1t/trojan-go/conf"
|
||||
@@ -19,13 +20,14 @@ func TestClientAPI(t *testing.T) {
|
||||
},
|
||||
}, meter)
|
||||
meter.Count("test", 123, 456)
|
||||
time.Sleep(time.Second)
|
||||
conn, err := grpc.Dial("127.0.0.1:10000", grpc.WithInsecure())
|
||||
common.Must(err)
|
||||
client := NewTrojanServiceClient(conn)
|
||||
reply, err := client.QueryStats(context.Background(), &StatsRequest{})
|
||||
common.Must(err)
|
||||
fmt.Println(reply.Download, reply.Upload)
|
||||
if reply.Download != 456 || reply.Upload != 123 {
|
||||
fmt.Println(reply.DownloadTraffic, reply.UploadTraffic)
|
||||
if reply.DownloadTraffic != 456 || reply.UploadTraffic != 123 {
|
||||
t.Fatal("wrong result")
|
||||
}
|
||||
}
|
||||
@@ -36,5 +38,5 @@ func TestRealClientAPI(t *testing.T) {
|
||||
client := NewTrojanServiceClient(conn)
|
||||
reply, err := client.QueryStats(context.Background(), &StatsRequest{})
|
||||
common.Must(err)
|
||||
fmt.Println(reply.Download, reply.Upload)
|
||||
fmt.Println(reply.DownloadTraffic, reply.UploadTraffic)
|
||||
}
|
||||
|
||||
+24
-3
@@ -1,6 +1,11 @@
|
||||
package common
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/p4gefau1t/trojan-go/log"
|
||||
)
|
||||
|
||||
type RewindReader struct {
|
||||
io.Reader
|
||||
@@ -26,8 +31,8 @@ func (r *RewindReader) Read(p []byte) (int, error) {
|
||||
n, err := r.rawReader.Read(p)
|
||||
if r.buffered {
|
||||
r.buf = append(r.buf, p[:n]...)
|
||||
if len(r.buf) > r.bufferSize {
|
||||
//panic("too long")
|
||||
if len(r.buf) > r.bufferSize*2 {
|
||||
log.Debug("read buffer too long")
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
@@ -119,3 +124,19 @@ func ReadByte(r io.Reader) (byte, error) {
|
||||
_, err := r.Read(buf[:])
|
||||
return buf[0], err
|
||||
}
|
||||
|
||||
type RewindConn struct {
|
||||
R *RewindReader
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *RewindConn) Read(p []byte) (int, error) {
|
||||
return c.R.Read(p)
|
||||
}
|
||||
|
||||
func NewRewindConn(conn net.Conn) *RewindConn {
|
||||
return &RewindConn{
|
||||
Conn: conn,
|
||||
R: NewRewindReader(conn),
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@ type TLSConfig struct {
|
||||
PreferServerCipher bool `json:"prefer_server_cipher"`
|
||||
SNI string `json:"sni"`
|
||||
HTTPFile string `json:"plain_http_response"`
|
||||
FallbackHost string `json:"fallback_addr"`
|
||||
FallbackPort int `json:"fallback_port"`
|
||||
ReuseSession bool `json:"reuse_session"`
|
||||
|
||||
@@ -102,7 +103,7 @@ type WebsocketConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
HostName string `json:"hostname"`
|
||||
Path string `json:"path"`
|
||||
Obfsucation bool `json:"obfuscation"`
|
||||
Obfuscation bool `json:"obfuscation"`
|
||||
DoubleTLS bool `json:"double_tls"`
|
||||
DoubleTLSVerify bool `json:"double_tls_verify"`
|
||||
}
|
||||
|
||||
+1
-1
@@ -292,7 +292,7 @@ func ParseJSON(data []byte) (*GlobalConfig, error) {
|
||||
config.Router.GeoSiteFilename = common.GetProgramDir() + "/geosite.dat"
|
||||
config.Websocket.DoubleTLS = true
|
||||
config.Websocket.DoubleTLSVerify = true
|
||||
config.Websocket.Obfsucation = true
|
||||
config.Websocket.Obfuscation = true
|
||||
|
||||
err := json.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
|
||||
@@ -107,7 +107,7 @@ func (o *DirectOutboundPacketSession) WritePacket(req *protocol.Request, packet
|
||||
if err != nil {
|
||||
return 0, common.NewError("cannot dial udp").Base(err)
|
||||
}
|
||||
log.Debug("UDP directly dialing to", remote)
|
||||
log.Debug("udp directly dialing to", remote)
|
||||
go o.listenConn(req, conn)
|
||||
n, err := conn.Write(packet)
|
||||
return n, err
|
||||
|
||||
@@ -26,7 +26,7 @@ func (i *SocksConnInboundSession) checkVersion() error {
|
||||
return err
|
||||
}
|
||||
if version != 0x5 {
|
||||
return common.NewError("unsupported version")
|
||||
return common.NewError("unsupported socks version")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -194,7 +194,7 @@ func (i *SocksInboundPacketSession) ReadPacket() (*protocol.Request, []byte, err
|
||||
i.tableMutex.Lock()
|
||||
i.sessionTable[req.String()] = session
|
||||
i.tableMutex.Unlock()
|
||||
log.Debug("UDP read from", src, "req", req)
|
||||
log.Debug("udp read from", src, "req", req)
|
||||
return req, payload, err
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ func (i *SocksInboundPacketSession) WritePacket(req *protocol.Request, packet []
|
||||
return 0, common.NewError("session not found")
|
||||
}
|
||||
client.expire = time.Now().Add(protocol.UDPTimeout)
|
||||
log.Debug("UDP write to", client.src, "req", req)
|
||||
log.Debug("udp write to", client.src, "req", req)
|
||||
return i.conn.WriteToUDP(w.Bytes(), client.src)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build linux
|
||||
|
||||
package nat
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -15,29 +15,29 @@ import (
|
||||
"github.com/p4gefau1t/trojan-go/protocol"
|
||||
)
|
||||
|
||||
type NATInboundConnSession struct {
|
||||
type TProxyInboundConnSession struct {
|
||||
protocol.ConnSession
|
||||
reqeust *protocol.Request
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func (i *NATInboundConnSession) Read(p []byte) (int, error) {
|
||||
func (i *TProxyInboundConnSession) Read(p []byte) (int, error) {
|
||||
return i.conn.Read(p)
|
||||
}
|
||||
|
||||
func (i *NATInboundConnSession) Write(p []byte) (int, error) {
|
||||
func (i *TProxyInboundConnSession) Write(p []byte) (int, error) {
|
||||
return i.conn.Write(p)
|
||||
}
|
||||
|
||||
func (i *NATInboundConnSession) Close() error {
|
||||
func (i *TProxyInboundConnSession) Close() error {
|
||||
return i.conn.Close()
|
||||
}
|
||||
|
||||
func (i *NATInboundConnSession) GetRequest() *protocol.Request {
|
||||
func (i *TProxyInboundConnSession) GetRequest() *protocol.Request {
|
||||
return i.reqeust
|
||||
}
|
||||
|
||||
func (i *NATInboundConnSession) parseRequest() error {
|
||||
func (i *TProxyInboundConnSession) parseRequest() error {
|
||||
addr, err := getOriginalTCPDest(i.conn.(*net.TCPConn))
|
||||
if err != nil {
|
||||
return common.NewError("failed to get original dst").Base(err)
|
||||
@@ -59,7 +59,7 @@ func (i *NATInboundConnSession) parseRequest() error {
|
||||
}
|
||||
|
||||
func NewInboundConnSession(conn net.Conn) (protocol.ConnSession, *protocol.Request, error) {
|
||||
i := &NATInboundConnSession{
|
||||
i := &TProxyInboundConnSession{
|
||||
conn: conn,
|
||||
}
|
||||
if err := i.parseRequest(); err != nil {
|
||||
@@ -135,7 +135,7 @@ func (i *NATInboundPacketSession) ReadPacket() (*protocol.Request, []byte, error
|
||||
expire: time.Now().Add(protocol.UDPTimeout),
|
||||
}
|
||||
i.tableMutex.Unlock()
|
||||
log.Debug("tproxy UDP packet from", src, "to", dst)
|
||||
log.Debug("tproxy udp packet from", src, "to", dst)
|
||||
req := &protocol.Request{
|
||||
Address: &common.Address{
|
||||
IP: dst.IP,
|
||||
@@ -167,7 +167,7 @@ func NewInboundPacketSession(ctx context.Context, config *conf.GlobalConfig) (pr
|
||||
}
|
||||
conn, err := tproxy.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
return nil, common.NewError("failed to listen UDP addr").Base(err)
|
||||
return nil, common.NewError("failed to listen udp addr").Base(err)
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
i := &NATInboundPacketSession{
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build linux
|
||||
|
||||
package nat
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
+28
-33
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/p4gefau1t/trojan-go/conf"
|
||||
"github.com/p4gefau1t/trojan-go/log"
|
||||
"github.com/p4gefau1t/trojan-go/protocol"
|
||||
"github.com/p4gefau1t/trojan-go/shadow"
|
||||
"github.com/p4gefau1t/trojan-go/stat"
|
||||
)
|
||||
|
||||
@@ -89,67 +90,61 @@ func (i *TrojanInboundConnSession) SetMeter(meter stat.TrafficMeter) {
|
||||
i.meter = meter
|
||||
}
|
||||
|
||||
func NewInboundConnSession(ctx context.Context, conn net.Conn, config *conf.GlobalConfig, auth stat.Authenticator) (protocol.ConnSession, *protocol.Request, error) {
|
||||
func NewInboundConnSession(ctx context.Context, conn net.Conn, config *conf.GlobalConfig, auth stat.Authenticator, shadowMan *shadow.ShadowManager) (protocol.ConnSession, *protocol.Request, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
rwc := common.NewRewindReadWriteCloser(conn)
|
||||
//rwc := common.NewRewindReadWriteCloser(conn)
|
||||
rewindConn := common.NewRewindConn(conn)
|
||||
i := &TrojanInboundConnSession{
|
||||
config: config,
|
||||
auth: auth,
|
||||
passwordHash: "INVALID_HASH",
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
rwc: rwc,
|
||||
rwc: rewindConn,
|
||||
}
|
||||
//start buffering
|
||||
rwc.SetBufferSize(512)
|
||||
defer rwc.StopBuffering()
|
||||
rewindConn.R.SetBufferSize(512)
|
||||
defer rewindConn.R.StopBuffering()
|
||||
|
||||
if i.config.Websocket.Enabled {
|
||||
//try to treat it as a websocket connection first
|
||||
ws, err := NewInboundWebsocket(i.ctx, conn, rwc.RewindReader, config)
|
||||
ws, err := NewInboundWebsocket(i.ctx, rewindConn, config, shadowMan)
|
||||
if err != nil {
|
||||
//websocket with wrong url path/origin, no need to continue parsing
|
||||
rwc.Rewind()
|
||||
rwc.StopBuffering()
|
||||
i.request = &protocol.Request{
|
||||
Address: config.RemoteAddress,
|
||||
Command: protocol.Connect,
|
||||
}
|
||||
log.Warn("remote", conn.RemoteAddr(), "is a invalid websocket conn | ", err)
|
||||
return i, i.request, nil
|
||||
return nil, nil, common.NewError("invalid websocket request").Base(err)
|
||||
}
|
||||
if ws != nil {
|
||||
//a websocket conn, try to verify it
|
||||
log.Debug("websocket conn")
|
||||
//disable the read buffer, use ws as new transport layer
|
||||
rwc.SetBufferSize(0)
|
||||
rwc = common.NewRewindReadWriteCloser(ws)
|
||||
i.rwc = rwc
|
||||
//disable the current read buffer, use ws as the new transport layer
|
||||
rewindConn.R.SetBufferSize(0)
|
||||
newTrapsport := common.NewRewindReadWriteCloser(ws)
|
||||
i.rwc = newTrapsport
|
||||
//parse it with trojan protocol format
|
||||
if err := i.parseRequest(rwc.RewindReader); err != nil {
|
||||
//not valid, just simply close it
|
||||
if err := i.parseRequest(newTrapsport.RewindReader); err != nil {
|
||||
//invalid ws, just simply close it
|
||||
ws.Close()
|
||||
return nil, nil, common.NewError("invalid trojan over ws conn").Base(err)
|
||||
}
|
||||
return i, i.request, nil
|
||||
}
|
||||
//not a websocket conn, it might be a normal trojan conn
|
||||
rwc.Rewind()
|
||||
rewindConn.R.Rewind()
|
||||
}
|
||||
|
||||
//normal trojan conn
|
||||
if err := i.parseRequest(rwc.RewindReader); err != nil {
|
||||
if err := i.parseRequest(rewindConn.R); err != nil {
|
||||
//not a valid trojan request, proxy it to the remote_addr
|
||||
rwc.Rewind()
|
||||
rwc.StopBuffering()
|
||||
i.request = &protocol.Request{
|
||||
Address: i.config.RemoteAddress,
|
||||
Command: protocol.Connect,
|
||||
}
|
||||
log.Warn(common.NewError("invalid trojan protocol over websocket from " + conn.RemoteAddr().String()).Base(err))
|
||||
return i, i.request, nil
|
||||
rewindConn.R.Rewind()
|
||||
err := common.NewError("invalid trojan protocol over websocket from " + conn.RemoteAddr().String()).Base(err)
|
||||
shadowMan.CommitScapegoat(&shadow.Scapegoat{
|
||||
Conn: rewindConn,
|
||||
ShadowAddress: i.config.RemoteAddress,
|
||||
Info: err.Error(),
|
||||
})
|
||||
return nil, nil, err
|
||||
}
|
||||
rwc.SetBufferSize(0)
|
||||
rwc.StopBuffering()
|
||||
//release the buffer
|
||||
rewindConn.R.SetBufferSize(0)
|
||||
return i, i.request, nil
|
||||
}
|
||||
|
||||
+108
-16
@@ -17,13 +17,14 @@ import (
|
||||
"github.com/p4gefau1t/trojan-go/conf"
|
||||
"github.com/p4gefau1t/trojan-go/log"
|
||||
"github.com/p4gefau1t/trojan-go/protocol"
|
||||
"github.com/p4gefau1t/trojan-go/shadow"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
//this AES layer is used for obfuscation purpose only
|
||||
type obfReadWriteCloser struct {
|
||||
*websocket.Conn
|
||||
net.Conn
|
||||
r cipher.StreamReader
|
||||
w cipher.StreamWriter
|
||||
bufrw *bufio.ReadWriter
|
||||
@@ -70,7 +71,7 @@ func NewOutboundObfReadWriteCloser(password string, conn *websocket.Conn) *obfRe
|
||||
}
|
||||
}
|
||||
|
||||
func NewInboundObfReadWriteCloser(password string, conn *websocket.Conn) (*obfReadWriteCloser, error) {
|
||||
func NewInboundObfReadWriteCloser(password string, conn net.Conn) (*obfReadWriteCloser, error) {
|
||||
bufrw := common.NewBufioReadWriter(conn)
|
||||
randomBytes := [aes.BlockSize + 8]byte{}
|
||||
_, err := bufrw.Read(randomBytes[:])
|
||||
@@ -127,14 +128,14 @@ func NewOutboundWebosocket(conn net.Conn, config *conf.GlobalConfig) (io.ReadWri
|
||||
return nil, err
|
||||
}
|
||||
var transport net.Conn = wsConn
|
||||
if config.Websocket.Obfsucation {
|
||||
if config.Websocket.Obfuscation {
|
||||
log.Debug("ws obfs enabled")
|
||||
transport = NewOutboundObfReadWriteCloser(config.Passwords[0], wsConn)
|
||||
}
|
||||
if !config.Websocket.DoubleTLS {
|
||||
return transport, nil
|
||||
}
|
||||
log.Debug("ws double TLS enabled")
|
||||
log.Debug("ws double tls enabled")
|
||||
tlsConfig := &tls.Config{
|
||||
CipherSuites: config.TLS.CipherSuites,
|
||||
RootCAs: config.TLS.CertPool,
|
||||
@@ -150,7 +151,7 @@ func NewOutboundWebosocket(conn net.Conn, config *conf.GlobalConfig) (io.ReadWri
|
||||
if config.LogLevel == 0 {
|
||||
state := tlsConn.ConnectionState()
|
||||
chain := state.VerifiedChains
|
||||
log.Debug("websocket TLS handshaked", "cipher:", tls.CipherSuiteName(state.CipherSuite), "resume:", state.DidResume)
|
||||
log.Debug("websocket tls handshaked", "cipher:", tls.CipherSuiteName(state.CipherSuite), "resume:", state.DidResume)
|
||||
for i := range chain {
|
||||
for j := range chain[i] {
|
||||
log.Debug("subject:", chain[i][j].Subject, ", issuer:", chain[i][j].Issuer)
|
||||
@@ -160,22 +161,66 @@ func NewOutboundWebosocket(conn net.Conn, config *conf.GlobalConfig) (io.ReadWri
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
func NewInboundWebsocket(ctx context.Context, conn net.Conn, r *common.RewindReader, config *conf.GlobalConfig) (io.ReadWriteCloser, error) {
|
||||
bufrw := bufio.NewReadWriter(bufio.NewReader(r), bufio.NewWriter(conn))
|
||||
httpRequest, err := http.ReadRequest(bufrw.Reader)
|
||||
func dialToWebosocketServer(config *conf.GlobalConfig, url, origin string) (*websocket.Conn, error) {
|
||||
wsConfig, err := websocket.NewConfig(url, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.Dial("tcp", config.RemoteAddress.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newWsConn, err := websocket.NewClient(wsConfig, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newWsConn, nil
|
||||
}
|
||||
|
||||
func getWebsocketScapegoat(config *conf.GlobalConfig, url, origin, info string, conn net.Conn) (*shadow.Scapegoat, error) {
|
||||
shadowConn, err := dialToWebosocketServer(config, url, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &shadow.Scapegoat{
|
||||
Conn: conn,
|
||||
ShadowConn: shadowConn,
|
||||
Info: info,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewInboundWebsocket(ctx context.Context, conn net.Conn, config *conf.GlobalConfig, shadowMan *shadow.ShadowManager) (io.ReadWriteCloser, error) {
|
||||
rewindConn := common.NewRewindConn(conn)
|
||||
rewindConn.R.SetBufferSize(512)
|
||||
defer rewindConn.R.StopBuffering()
|
||||
|
||||
bufrw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
|
||||
httpRequest, obfErr := http.ReadRequest(bufrw.Reader)
|
||||
if obfErr != nil {
|
||||
log.Debug(common.NewError("not a http request:").Base(obfErr))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
//this is a http request
|
||||
if (config.Websocket.HostName != "" && httpRequest.Host != config.Websocket.HostName) || //check hostname
|
||||
httpRequest.URL.Path != config.Websocket.Path || //check url path
|
||||
httpRequest.Header.Get("Upgrade") != "websocket" { //check upgrade field
|
||||
//not a valid websocket conn
|
||||
rewindConn.R.Rewind()
|
||||
shadowMan.CommitScapegoat(&shadow.Scapegoat{
|
||||
Conn: rewindConn,
|
||||
ShadowAddress: config.RemoteAddress,
|
||||
Info: "not a valid http upgrade request from " + conn.RemoteAddr().String(),
|
||||
})
|
||||
return nil, common.NewError("invalid ws url or hostname")
|
||||
}
|
||||
|
||||
//this is a websocket upgrade request
|
||||
//no need to record the recv content for now
|
||||
rewindConn.R.SetBufferSize(0)
|
||||
url := "wss://" + config.Websocket.HostName + config.Websocket.Path
|
||||
origin := "https://" + config.Websocket.HostName
|
||||
wsConfig, err := websocket.NewConfig(url, origin)
|
||||
wsConfig, obfErr := websocket.NewConfig(url, origin)
|
||||
|
||||
handshaked := make(chan struct{})
|
||||
|
||||
@@ -209,17 +254,47 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, r *common.RewindRea
|
||||
}
|
||||
|
||||
if wsConn == nil {
|
||||
//conn has been closed at this point
|
||||
return nil, common.NewError("failed to perform websocket handshake")
|
||||
}
|
||||
|
||||
var transport net.Conn = wsConn
|
||||
if config.Websocket.Obfsucation {
|
||||
transport, err = NewInboundObfReadWriteCloser(config.Passwords[0], wsConn)
|
||||
if err != nil {
|
||||
return nil, common.NewError("failed to init obfus layer").Base(err)
|
||||
var transport net.Conn
|
||||
transport = common.NewRewindConn(wsConn)
|
||||
|
||||
//start buffering the websocket payload
|
||||
rewindConn.R.SetBufferSize(512)
|
||||
defer rewindConn.R.StopBuffering()
|
||||
|
||||
if config.Websocket.Obfuscation {
|
||||
log.Debug("ws obfs")
|
||||
|
||||
//deadline for sending the iv and hash
|
||||
rewindConn.SetDeadline(time.Now().Add(protocol.TCPTimeout))
|
||||
transport, obfErr = NewInboundObfReadWriteCloser(config.Passwords[0], rewindConn)
|
||||
rewindConn.SetDeadline(time.Time{})
|
||||
|
||||
if obfErr != nil {
|
||||
rewindConn.R.Rewind()
|
||||
//proxy this to our own ws server
|
||||
obfErr = common.NewError("remote websocket conn:" + conn.RemoteAddr().String() + "didn't send any valid iv/hash").Base(obfErr)
|
||||
goat, err := getWebsocketScapegoat(
|
||||
config,
|
||||
url,
|
||||
origin,
|
||||
obfErr.Error(),
|
||||
rewindConn,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error(common.NewError("failed to obtain websocket scapegoat").Base(err))
|
||||
wsConn.WriteClose(500)
|
||||
} else {
|
||||
shadowMan.CommitScapegoat(goat)
|
||||
}
|
||||
return nil, obfErr
|
||||
}
|
||||
}
|
||||
if !config.Websocket.DoubleTLS {
|
||||
rewindConn.R.SetBufferSize(0)
|
||||
return transport, nil
|
||||
}
|
||||
tlsConfig := &tls.Config{
|
||||
@@ -229,8 +304,25 @@ func NewInboundWebsocket(ctx context.Context, conn net.Conn, r *common.RewindRea
|
||||
SessionTicketsDisabled: !config.TLS.SessionTicket,
|
||||
}
|
||||
tlsConn := tls.Server(transport, tlsConfig)
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return nil, err
|
||||
if tlsErr := tlsConn.Handshake(); tlsErr != nil {
|
||||
rewindConn.R.Rewind()
|
||||
//proxy this to our own ws server
|
||||
tlsErr = common.NewError("invalid double tls handshake from" + conn.RemoteAddr().String()).Base(tlsErr)
|
||||
goat, err := getWebsocketScapegoat(
|
||||
config,
|
||||
url,
|
||||
origin,
|
||||
tlsErr.Error(),
|
||||
rewindConn,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error(common.NewError("failed to obtain websocket scapegoat").Base(err))
|
||||
wsConn.WriteClose(500)
|
||||
} else {
|
||||
shadowMan.CommitScapegoat(goat)
|
||||
}
|
||||
return nil, tlsErr
|
||||
}
|
||||
rewindConn.R.SetBufferSize(0)
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func (c *Client) handleSocksConn(conn io.ReadWriteCloser) {
|
||||
rwc := common.NewRewindReadWriteCloser(conn)
|
||||
inboundConn, req, err := socks.NewInboundConnSession(rwc)
|
||||
if err != nil {
|
||||
log.Error(common.NewError("failed to start new inbound session").Base(err))
|
||||
log.Error(common.NewError("failed to handle socks requests").Base(err))
|
||||
rwc.Close()
|
||||
return
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func (c *Client) handleSocksConn(conn io.ReadWriteCloser) {
|
||||
|
||||
//notify listenUDP to get ready for relaying udp packets
|
||||
c.associated.Signal()
|
||||
log.Debug("UDP associated, req", req)
|
||||
log.Debug("udp associated to", req)
|
||||
if err := inboundConn.(protocol.NeedRespond).Respond(); err != nil {
|
||||
log.Error("failed to repsond")
|
||||
return
|
||||
@@ -101,7 +101,7 @@ func (c *Client) handleSocksConn(conn io.ReadWriteCloser) {
|
||||
//stop relaying UDP once TCP connection is closed
|
||||
var buf [1]byte
|
||||
_, err = rwc.Read(buf[:])
|
||||
log.Debug(common.NewError("UDP conn ends").Base(err))
|
||||
log.Debug(common.NewError("udp conn ends").Base(err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -142,11 +142,12 @@ func (c *Client) handleHTTPConn(conn io.ReadWriteCloser) {
|
||||
rwc := common.NewRewindReadWriteCloser(conn)
|
||||
inboundConn, req, inboundPacket, err := http.NewHTTPInbound(rwc)
|
||||
if err != nil {
|
||||
log.Error(common.NewError("failed to start new inbound session:").Base(err))
|
||||
log.Error(common.NewError("failed to handle HTTP requests").Base(err))
|
||||
rwc.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if inboundConn != nil { //CONNECT request
|
||||
if inboundConn != nil { //CONNECT requests
|
||||
defer inboundConn.Close()
|
||||
|
||||
if err := inboundConn.(protocol.NeedRespond).Respond(); err != nil {
|
||||
@@ -182,7 +183,7 @@ func (c *Client) handleHTTPConn(conn io.ReadWriteCloser) {
|
||||
log.Info("conn tunneling to", req)
|
||||
outboundConn.(protocol.NeedMeter).SetMeter(c.meter)
|
||||
proxy.ProxyConn(c.ctx, inboundConn, outboundConn, c.config.BufferSize)
|
||||
} else { //GET/POST
|
||||
} else { //GET/POST requests
|
||||
defer inboundPacket.Close()
|
||||
packetChan := make(chan *packetInfo, 512)
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
@@ -149,7 +149,7 @@ func (f *Forward) listenUDP(errChan chan error) {
|
||||
}
|
||||
f.clientPackets <- &dispatchInfo{
|
||||
addr: addr,
|
||||
payload: buf[0:n],
|
||||
payload: buf[:n],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -105,10 +105,10 @@ func (m *MuxManager) DialToServer() (io.ReadWriteCloser, error) {
|
||||
defer m.Unlock()
|
||||
delete(m.muxPool, info.id)
|
||||
info.client.Close()
|
||||
log.Info("somthing wrong with mux", info.id, ", closing")
|
||||
log.Info("somthing wrong with mux client", info.id, ", closing")
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("new mux conn", info.id)
|
||||
log.Debug("new mux conn established, client", info.id)
|
||||
info.lastActiveTime = time.Now()
|
||||
return stream, nil
|
||||
}
|
||||
@@ -146,7 +146,7 @@ func (m *MuxManager) checkAndCloseIdleMuxClient() {
|
||||
m.Lock()
|
||||
for id, info := range m.muxPool {
|
||||
info.client.Close()
|
||||
log.Info("mux", id, "closed")
|
||||
log.Info("mux client", id, "closed")
|
||||
}
|
||||
m.Unlock()
|
||||
return
|
||||
|
||||
+5
-5
@@ -11,8 +11,8 @@ import (
|
||||
"github.com/p4gefau1t/trojan-go/conf"
|
||||
"github.com/p4gefau1t/trojan-go/log"
|
||||
"github.com/p4gefau1t/trojan-go/protocol"
|
||||
"github.com/p4gefau1t/trojan-go/protocol/nat"
|
||||
"github.com/p4gefau1t/trojan-go/protocol/simplesocks"
|
||||
"github.com/p4gefau1t/trojan-go/protocol/tproxy"
|
||||
"github.com/p4gefau1t/trojan-go/protocol/trojan"
|
||||
"github.com/p4gefau1t/trojan-go/proxy"
|
||||
)
|
||||
@@ -49,7 +49,7 @@ func (n *NAT) openOutboundConn(req *protocol.Request) (protocol.ConnSession, err
|
||||
}
|
||||
|
||||
func (n *NAT) handleConn(conn net.Conn) {
|
||||
inboundConn, req, err := nat.NewInboundConnSession(conn)
|
||||
inboundConn, req, err := tproxy.NewInboundConnSession(conn)
|
||||
if err != nil {
|
||||
log.Error(common.NewError("failed to start inbound session").Base(err))
|
||||
return
|
||||
@@ -66,7 +66,7 @@ func (n *NAT) handleConn(conn net.Conn) {
|
||||
}
|
||||
|
||||
func (n *NAT) listenUDP(errChan chan error) {
|
||||
inboundPacket, err := nat.NewInboundPacketSession(n.ctx, n.config)
|
||||
inboundPacket, err := tproxy.NewInboundPacketSession(n.ctx, n.config)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
@@ -125,7 +125,7 @@ func (n *NAT) listenTCP(errChan chan error) {
|
||||
}
|
||||
|
||||
func (n *NAT) Run() error {
|
||||
log.Info("nat running at", n.config.LocalAddress)
|
||||
log.Info("tproxy running at", n.config.LocalAddress)
|
||||
errChan := make(chan error, 2)
|
||||
go n.listenUDP(errChan)
|
||||
go n.listenTCP(errChan)
|
||||
@@ -138,7 +138,7 @@ func (n *NAT) Run() error {
|
||||
}
|
||||
|
||||
func (n *NAT) Close() error {
|
||||
log.Info("shutting down nat...")
|
||||
log.Info("shutting down tproxy...")
|
||||
n.cancel()
|
||||
if n.listener != nil {
|
||||
n.listener.Close()
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ func (m *TLSManager) DialToServer() (io.ReadWriteCloser, error) {
|
||||
if m.config.LogLevel == 0 {
|
||||
state := tlsConn.ConnectionState()
|
||||
chain := state.VerifiedChains
|
||||
log.Debug("TLS handshaked", "cipher:", tls.CipherSuiteName(state.CipherSuite), "resume:", state.DidResume)
|
||||
log.Debug("tls handshaked", "cipher:", tls.CipherSuiteName(state.CipherSuite), "resume:", state.DidResume)
|
||||
for i := range chain {
|
||||
for j := range chain[i] {
|
||||
log.Debug("subject:", chain[i][j].Subject, ", issuer:", chain[i][j].Issuer)
|
||||
|
||||
+32
-41
@@ -6,7 +6,6 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
|
||||
"github.com/p4gefau1t/trojan-go/common"
|
||||
"github.com/p4gefau1t/trojan-go/conf"
|
||||
@@ -16,6 +15,7 @@ import (
|
||||
"github.com/p4gefau1t/trojan-go/protocol/simplesocks"
|
||||
"github.com/p4gefau1t/trojan-go/protocol/trojan"
|
||||
"github.com/p4gefau1t/trojan-go/proxy"
|
||||
"github.com/p4gefau1t/trojan-go/shadow"
|
||||
"github.com/p4gefau1t/trojan-go/stat"
|
||||
"github.com/xtaci/smux"
|
||||
)
|
||||
@@ -28,6 +28,7 @@ type Server struct {
|
||||
auth stat.Authenticator
|
||||
meter stat.TrafficMeter
|
||||
config *conf.GlobalConfig
|
||||
shadow *shadow.ShadowManager
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
@@ -62,8 +63,9 @@ func (s *Server) handleMuxConn(stream *smux.Stream) {
|
||||
}
|
||||
|
||||
func (s *Server) handleConn(conn net.Conn) {
|
||||
inboundConn, req, err := trojan.NewInboundConnSession(s.ctx, conn, s.config, s.auth)
|
||||
inboundConn, req, err := trojan.NewInboundConnSession(s.ctx, conn, s.config, s.auth, s.shadow)
|
||||
if err != nil {
|
||||
//once the auth is failed, the conn will be took over by shadow manager. don't close it
|
||||
log.Error(common.NewError("failed to start inbound session, remote:" + conn.RemoteAddr().String()).Base(err))
|
||||
return
|
||||
}
|
||||
@@ -94,9 +96,9 @@ func (s *Server) handleConn(conn net.Conn) {
|
||||
return
|
||||
}
|
||||
defer outboundPacket.Close()
|
||||
log.Info("UDP tunnel established")
|
||||
log.Info("udp tunnel established")
|
||||
proxy.ProxyPacket(s.ctx, inboundPacket, outboundPacket)
|
||||
log.Debug("UDP tunnel closed")
|
||||
log.Debug("udp tunnel closed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -112,39 +114,6 @@ func (s *Server) handleConn(conn net.Conn) {
|
||||
proxy.ProxyConn(s.ctx, inboundConn, outboundConn, s.config.BufferSize)
|
||||
}
|
||||
|
||||
func (s *Server) handleInvalidConn(conn net.Conn, tlsConn *tls.Conn) {
|
||||
defer conn.Close()
|
||||
if len(s.config.TLS.HTTPResponse) > 0 {
|
||||
log.Warn("trying to response with a plain http response")
|
||||
conn.Write(s.config.TLS.HTTPResponse)
|
||||
return
|
||||
}
|
||||
|
||||
if s.config.TLS.FallbackAddress != nil {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Error("recovered", r)
|
||||
}
|
||||
}()
|
||||
//HACK
|
||||
//obtain the bytes buffered by the tls conn
|
||||
v := reflect.ValueOf(*tlsConn)
|
||||
buf := v.FieldByName("rawInput").FieldByName("buf").Bytes()
|
||||
log.Debug("payload:" + string(buf))
|
||||
|
||||
remote, err := net.Dial("tcp", s.config.TLS.FallbackAddress.String())
|
||||
if err != nil {
|
||||
log.Warn(common.NewError("failed to dial to tls fallback server").Base(err))
|
||||
return
|
||||
}
|
||||
log.Warn("proxying this invalid tls conn to the tls fallback server")
|
||||
remote.Write(buf)
|
||||
proxy.ProxyConn(s.ctx, conn, remote, s.config.BufferSize)
|
||||
} else {
|
||||
log.Warn("tls fallback port is unspecified, closing")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Run() error {
|
||||
var db *sql.DB
|
||||
var err error
|
||||
@@ -219,15 +188,36 @@ func (s *Server) Run() error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
log.Info("conn accepted from", conn.RemoteAddr())
|
||||
go func(conn net.Conn) {
|
||||
tlsConn := tls.Server(conn, tlsConfig)
|
||||
rewindConn := common.NewRewindConn(conn)
|
||||
rewindConn.R.SetBufferSize(512)
|
||||
|
||||
tlsConn := tls.Server(rewindConn, tlsConfig)
|
||||
err = tlsConn.Handshake()
|
||||
|
||||
rewindConn.R.StopBuffering()
|
||||
|
||||
if err != nil {
|
||||
log.Warn(common.NewError("failed to perform tls handshake, remote:" + conn.RemoteAddr().String()).Base(err))
|
||||
go s.handleInvalidConn(conn, tlsConn)
|
||||
rewindConn.R.Rewind()
|
||||
err = common.NewError("failed to perform tls handshake with " + conn.RemoteAddr().String()).Base(err)
|
||||
log.Warn(err)
|
||||
if s.config.TLS.FallbackAddress != nil {
|
||||
s.shadow.CommitScapegoat(&shadow.Scapegoat{
|
||||
Conn: rewindConn,
|
||||
ShadowAddress: s.config.TLS.FallbackAddress,
|
||||
Info: err.Error(),
|
||||
})
|
||||
} else if s.config.TLS.HTTPResponse != nil {
|
||||
rewindConn.Write(s.config.TLS.HTTPResponse)
|
||||
rewindConn.Close()
|
||||
} else {
|
||||
rewindConn.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
go s.handleConn(tlsConn)
|
||||
defer tlsConn.Close()
|
||||
s.handleConn(tlsConn)
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
@@ -242,6 +232,7 @@ func (s *Server) Close() error {
|
||||
func (s *Server) Build(config *conf.GlobalConfig) (common.Runnable, error) {
|
||||
s.config = config
|
||||
s.ctx, s.cancel = context.WithCancel(context.Background())
|
||||
s.shadow = shadow.NewShadowManager(s.ctx, s.config)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package shadow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/p4gefau1t/trojan-go/common"
|
||||
"github.com/p4gefau1t/trojan-go/conf"
|
||||
"github.com/p4gefau1t/trojan-go/log"
|
||||
"github.com/p4gefau1t/trojan-go/proxy"
|
||||
)
|
||||
|
||||
type Scapegoat struct {
|
||||
Conn io.ReadWriteCloser
|
||||
ShadowConn io.ReadWriteCloser
|
||||
ShadowAddress *common.Address
|
||||
Info string
|
||||
}
|
||||
|
||||
type ShadowManager struct {
|
||||
config *conf.GlobalConfig
|
||||
ctx context.Context
|
||||
scapegoatChan chan *Scapegoat
|
||||
}
|
||||
|
||||
func (m *ShadowManager) CommitScapegoat(goat *Scapegoat) {
|
||||
m.scapegoatChan <- goat
|
||||
log.Debug("scapegoat commited")
|
||||
}
|
||||
|
||||
func (m *ShadowManager) handleScapegoat() {
|
||||
for {
|
||||
select {
|
||||
case goat := <-m.scapegoatChan:
|
||||
if goat.Info != "" {
|
||||
log.Info("scapegoat: ", goat.Info)
|
||||
}
|
||||
if goat.ShadowConn == nil {
|
||||
if goat.ShadowAddress == nil {
|
||||
panic("incorrect shadow server")
|
||||
}
|
||||
var err error
|
||||
goat.ShadowConn, err = net.Dial("tcp", goat.ShadowAddress.String())
|
||||
if err != nil {
|
||||
log.Error(common.NewError("failed to dial to shadow server").Base(err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
go proxy.ProxyConn(m.ctx, goat.Conn, goat.ShadowConn, m.config.BufferSize)
|
||||
case <-m.ctx.Done():
|
||||
log.Debug("shadow manager exiting..")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewShadowManager(ctx context.Context, config *conf.GlobalConfig) *ShadowManager {
|
||||
m := &ShadowManager{
|
||||
config: config,
|
||||
ctx: ctx,
|
||||
scapegoatChan: make(chan *Scapegoat, 1024),
|
||||
}
|
||||
go m.handleScapegoat()
|
||||
return m
|
||||
}
|
||||
+22
-15
@@ -90,13 +90,14 @@ func getTLSConfig() conf.TLSConfig {
|
||||
panic("invalid cert")
|
||||
}
|
||||
c := conf.TLSConfig{
|
||||
SNI: "localhost",
|
||||
CertPool: pool,
|
||||
KeyPair: KeyPair,
|
||||
Verify: true,
|
||||
VerifyHostname: true,
|
||||
ReuseSession: true,
|
||||
SessionTicket: true,
|
||||
SNI: "localhost",
|
||||
CertPool: pool,
|
||||
KeyPair: KeyPair,
|
||||
Verify: true,
|
||||
VerifyHostname: true,
|
||||
ReuseSession: true,
|
||||
SessionTicket: true,
|
||||
FallbackAddress: common.NewAddress("127.0.0.1", 80, "tcp"),
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -141,7 +142,7 @@ func addWsConfig(config *conf.GlobalConfig) *conf.GlobalConfig {
|
||||
Enabled: true,
|
||||
HostName: "127.0.0.1",
|
||||
Path: "/websocket",
|
||||
Obfsucation: true,
|
||||
Obfuscation: false,
|
||||
DoubleTLS: true,
|
||||
}
|
||||
return config
|
||||
@@ -269,16 +270,16 @@ func SingleThreadSpeedTestClientServer(b *testing.B, clientConfig *conf.GlobalCo
|
||||
conn.Write(payload)
|
||||
t2 := time.Now()
|
||||
speed := float64(mbytes) / t2.Sub(t1).Seconds()
|
||||
b.Log("Single thread link speed:", speed, "MB/s")
|
||||
b.Log("Single thread link speed:", speed*8/1024, "Gbps")
|
||||
conn.Close()
|
||||
cancel()
|
||||
}
|
||||
|
||||
func TestIt(t *testing.T) {
|
||||
//clientConfig := getBasicClientConfig()
|
||||
//serverConfig := getBasicServerConfig()
|
||||
//go RunClient(context.Background(), clientConfig)
|
||||
//RunServer(context.Background(), serverConfig)
|
||||
clientConfig := getBasicClientConfig()
|
||||
serverConfig := getBasicServerConfig()
|
||||
go RunClient(context.Background(), clientConfig)
|
||||
RunServer(context.Background(), serverConfig)
|
||||
}
|
||||
|
||||
func TestNormal(t *testing.T) {
|
||||
@@ -357,9 +358,15 @@ func TestHTTPProxy(t *testing.T) {
|
||||
wsClient, err := websocket.NewClient(wsConfig, conn)
|
||||
common.Must(err)
|
||||
buf := [100]byte{}
|
||||
common.Must2(wsClient.Write([]byte("I'm GFW")))
|
||||
wsClient.Read(buf[:])
|
||||
common.Must2(wsClient.Write([]byte("I'm GFW1231231231231212391273871283719823791237912398721933123")))
|
||||
common.Must2(wsClient.Read(buf[:]))
|
||||
fmt.Println(buf)
|
||||
common.Must(err)
|
||||
conn.Close()
|
||||
|
||||
resp, err = http.Get("http://127.0.0.1:4445")
|
||||
common.Must(err)
|
||||
resp.Body.Read(buf[:])
|
||||
fmt.Println(buf)
|
||||
cancel()
|
||||
}
|
||||
|
||||
-318
@@ -1,318 +0,0 @@
|
||||
package test
|
||||
|
||||
// Copyright 2012, Hailiang Wang. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package socks implements a SOCKS (SOCKS4, SOCKS4A and SOCKS5) proxy client.
|
||||
|
||||
A complete example using this package:
|
||||
package main
|
||||
|
||||
import (
|
||||
"h12.io/socks"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dialSocksProxy := socks.Dial("socks5://127.0.0.1:1080?timeout=5s")
|
||||
tr := &http.Transport{Dial: dialSocksProxy}
|
||||
httpClient := &http.Client{Transport: tr}
|
||||
|
||||
bodyText, err := TestHttpsGet(httpClient, "https://h12.io/about")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
fmt.Print(bodyText)
|
||||
}
|
||||
|
||||
func TestHttpsGet(c *http.Client, url string) (bodyText string, err error) {
|
||||
resp, err := c.Get(url)
|
||||
if err != nil { return }
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil { return }
|
||||
bodyText = string(body)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Constants to choose which version of SOCKS protocol to use.
|
||||
const (
|
||||
SOCKS4 = iota
|
||||
SOCKS4A
|
||||
SOCKS5
|
||||
)
|
||||
|
||||
type (
|
||||
Config struct {
|
||||
Proto int
|
||||
Host string
|
||||
Auth Auth
|
||||
Timeout time.Duration
|
||||
}
|
||||
Auth struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
)
|
||||
|
||||
func parse(proxyURI string) (*Config, error) {
|
||||
uri, err := url.Parse(proxyURI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg := &Config{}
|
||||
switch uri.Scheme {
|
||||
case "socks4":
|
||||
cfg.Proto = SOCKS4
|
||||
case "socks4a":
|
||||
cfg.Proto = SOCKS4A
|
||||
case "socks5":
|
||||
cfg.Proto = SOCKS5
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown SOCKS protocol %s", uri.Scheme)
|
||||
}
|
||||
cfg.Host = uri.Host
|
||||
if uri.User != nil {
|
||||
cfg.Auth.Username = uri.User.Username()
|
||||
cfg.Auth.Password, _ = uri.User.Password()
|
||||
}
|
||||
query := uri.Query()
|
||||
timeout := query.Get("timeout")
|
||||
if timeout != "" {
|
||||
var err error
|
||||
cfg.Timeout, err = time.ParseDuration(timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// Dial returns the dial function to be used in http.Transport object.
|
||||
// Argument proxyURI should be in the format: "socks5://user:password@127.0.0.1:1080?timeout=5s".
|
||||
// The protocol could be socks5, socks4 and socks4a.
|
||||
func Dial(proxyURI string) func(string, string) (net.Conn, error) {
|
||||
cfg, err := parse(proxyURI)
|
||||
if err != nil {
|
||||
return dialError(err)
|
||||
}
|
||||
return cfg.dialFunc()
|
||||
}
|
||||
|
||||
// DialSocksProxy returns the dial function to be used in http.Transport object.
|
||||
// Argument socksType should be one of SOCKS4, SOCKS4A and SOCKS5.
|
||||
// Argument proxy should be in this format "127.0.0.1:1080".
|
||||
func DialSocksProxy(socksType int, proxy string) func(string, string) (net.Conn, error) {
|
||||
return (&Config{Proto: socksType, Host: proxy}).dialFunc()
|
||||
}
|
||||
|
||||
func (c *Config) dialFunc() func(string, string) (net.Conn, error) {
|
||||
switch c.Proto {
|
||||
case SOCKS5:
|
||||
return func(_, targetAddr string) (conn net.Conn, err error) {
|
||||
return c.dialSocks5(targetAddr)
|
||||
}
|
||||
case SOCKS4, SOCKS4A:
|
||||
return func(_, targetAddr string) (conn net.Conn, err error) {
|
||||
return c.dialSocks4(targetAddr)
|
||||
}
|
||||
}
|
||||
return dialError(fmt.Errorf("unknown SOCKS protocol %v", c.Proto))
|
||||
}
|
||||
|
||||
func (cfg *Config) dialSocks5(targetAddr string) (conn net.Conn, err error) {
|
||||
proxy := cfg.Host
|
||||
|
||||
// dial TCP
|
||||
conn, err = net.Dial("tcp", proxy)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// version identifier/method selection request
|
||||
req := []byte{
|
||||
5, // version number
|
||||
1, // number of methods
|
||||
0, // method 0: no authentication (only anonymous access supported for now)
|
||||
}
|
||||
resp, err := cfg.sendReceive(conn, req)
|
||||
if err != nil {
|
||||
return
|
||||
} else if len(resp) != 2 {
|
||||
err = errors.New("Server does not respond properly.")
|
||||
return
|
||||
} else if resp[0] != 5 {
|
||||
err = errors.New("Server does not support Socks 5.")
|
||||
return
|
||||
} else if resp[1] != 0 { // no auth
|
||||
err = errors.New("socks method negotiation failed.")
|
||||
return
|
||||
}
|
||||
|
||||
// detail request
|
||||
host, port, err := splitHostPort(targetAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = []byte{
|
||||
5, // version number
|
||||
//1, // connect command
|
||||
3, // associate command
|
||||
0, // reserved, must be zero
|
||||
3, // address type, 3 means domain name
|
||||
byte(len(host)), // address length
|
||||
}
|
||||
req = append(req, []byte(host)...)
|
||||
req = append(req, []byte{
|
||||
byte(port >> 8), // higher byte of destination port
|
||||
byte(port), // lower byte of destination port (big endian)
|
||||
}...)
|
||||
resp, err = cfg.sendReceive(conn, req)
|
||||
if err != nil {
|
||||
return
|
||||
} else if len(resp) != 10 {
|
||||
err = errors.New("Server does not respond properly.")
|
||||
} else if resp[1] != 0 {
|
||||
err = errors.New("Can't complete SOCKS5 connection.")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *Config) dialSocks4(targetAddr string) (conn net.Conn, err error) {
|
||||
socksType := cfg.Proto
|
||||
proxy := cfg.Host
|
||||
|
||||
// dial TCP
|
||||
conn, err = net.Dial("tcp", proxy)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// connection request
|
||||
host, port, err := splitHostPort(targetAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ip := net.IPv4(0, 0, 0, 1).To4()
|
||||
if socksType == SOCKS4 {
|
||||
ip, err = lookupIP(host)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
req := []byte{
|
||||
4, // version number
|
||||
1, // command CONNECT
|
||||
byte(port >> 8), // higher byte of destination port
|
||||
byte(port), // lower byte of destination port (big endian)
|
||||
ip[0], ip[1], ip[2], ip[3], // special invalid IP address to indicate the host name is provided
|
||||
0, // user id is empty, anonymous proxy only
|
||||
}
|
||||
if socksType == SOCKS4A {
|
||||
req = append(req, []byte(host+"\x00")...)
|
||||
}
|
||||
|
||||
resp, err := cfg.sendReceive(conn, req)
|
||||
if err != nil {
|
||||
return
|
||||
} else if len(resp) != 8 {
|
||||
err = errors.New("Server does not respond properly.")
|
||||
return
|
||||
}
|
||||
switch resp[1] {
|
||||
case 90:
|
||||
// request granted
|
||||
case 91:
|
||||
err = errors.New("Socks connection request rejected or failed.")
|
||||
case 92:
|
||||
err = errors.New("Socks connection request rejected becasue SOCKS server cannot connect to identd on the client.")
|
||||
case 93:
|
||||
err = errors.New("Socks connection request rejected because the client program and identd report different user-ids.")
|
||||
default:
|
||||
err = errors.New("Socks connection request failed, unknown error.")
|
||||
}
|
||||
// clear the deadline before returning
|
||||
if err := conn.SetDeadline(time.Time{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *Config) sendReceive(conn net.Conn, req []byte) (resp []byte, err error) {
|
||||
if cfg.Timeout > 0 {
|
||||
if err := conn.SetWriteDeadline(time.Now().Add(cfg.Timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err = conn.Write(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp, err = cfg.readAll(conn)
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *Config) readAll(conn net.Conn) (resp []byte, err error) {
|
||||
resp = make([]byte, 1024)
|
||||
if cfg.Timeout > 0 {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(cfg.Timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
n, err := conn.Read(resp)
|
||||
resp = resp[:n]
|
||||
return
|
||||
}
|
||||
|
||||
func lookupIP(host string) (ip net.IP, err error) {
|
||||
ips, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(ips) == 0 {
|
||||
err = fmt.Errorf("Cannot resolve host: %s.", host)
|
||||
return
|
||||
}
|
||||
ip = ips[0].To4()
|
||||
if len(ip) != net.IPv4len {
|
||||
fmt.Println(len(ip), ip)
|
||||
err = errors.New("IPv6 is not supported by SOCKS4.")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func splitHostPort(addr string) (host string, port uint16, err error) {
|
||||
host, portStr, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
portInt, err := strconv.ParseUint(portStr, 10, 16)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
port = uint16(portInt)
|
||||
return
|
||||
}
|
||||
|
||||
func dialError(err error) func(string, string) (net.Conn, error) {
|
||||
return func(_, _ string) (net.Conn, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user