add shadow, update api

This commit is contained in:
p4gefau1t
2020-04-25 05:02:14 -04:00
parent b5db489899
commit 4bb5b31e91
22 changed files with 385 additions and 486 deletions
+34 -15
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+7 -6
View File
@@ -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)
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+66
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
}