1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-06-03 06:30:42 +00:00

socks client

This commit is contained in:
Darien Raymond 2017-01-08 01:06:35 +01:00
parent b00ee67369
commit f2e9d8a4e0
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
7 changed files with 583 additions and 29 deletions

View File

@ -97,7 +97,14 @@ func (v *ServerSpec) AddUser(user *User) {
func (v *ServerSpec) PickUser() *User {
userCount := len(v.users)
return v.users[dice.Roll(userCount)]
switch userCount {
case 0:
return nil
case 1:
return v.users[0]
default:
return v.users[dice.Roll(userCount)]
}
}
func (v *ServerSpec) IsValid() bool {

View File

@ -166,13 +166,13 @@ func (v *Client) Dispatch(destination v2net.Destination, ray ray.OutboundRay) {
type ClientFactory struct{}
// StreamCapability implements OutboundHandlerFactory.StreamCapability().
func (v *ClientFactory) StreamCapability() v2net.NetworkList {
func (ClientFactory) StreamCapability() v2net.NetworkList {
return v2net.NetworkList{
Network: []v2net.Network{v2net.Network_TCP},
}
}
// Create implements OutboundHandlerFactory.Create().
func (v *ClientFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
func (ClientFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
return NewClient(rawConfig.(*ClientConfig), space, meta)
}

125
proxy/socks/client.go Normal file
View File

@ -0,0 +1,125 @@
package socks
import (
"v2ray.com/core/app"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/log"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/retry"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
)
type Client struct {
serverPicker protocol.ServerPicker
meta *proxy.OutboundHandlerMeta
}
func NewClient(config *ClientConfig, space app.Space, meta *proxy.OutboundHandlerMeta) (*Client, error) {
serverList := protocol.NewServerList()
for _, rec := range config.Server {
serverList.AddServer(protocol.NewServerSpecFromPB(*rec))
}
client := &Client{
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
meta: meta,
}
return client, nil
}
func (c *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) {
var server *protocol.ServerSpec
var conn internet.Connection
err := retry.ExponentialBackoff(5, 100).On(func() error {
server = c.serverPicker.PickServer()
dest := server.Destination()
rawConn, err := internet.Dial(c.meta.Address, dest, c.meta.GetDialerOptions())
if err != nil {
return err
}
conn = rawConn
return nil
})
if err != nil {
log.Warning("Socks|Client: Failed to find an available destination.")
return
}
defer conn.Close()
conn.SetReusable(false)
request := &protocol.RequestHeader{
Version: socks5Version,
Command: protocol.RequestCommandTCP,
Address: destination.Address,
Port: destination.Port,
}
if destination.Network == net.Network_UDP {
request.Command = protocol.RequestCommandUDP
}
user := server.PickUser()
if user != nil {
request.User = user
}
udpRequest, err := ClientHandshake(request, conn, conn)
if err != nil {
log.Warning("Socks|Client: Failed to establish connection to server: ", err)
return
}
var requestFunc func() error
var responseFunc func() error
if request.Command == protocol.RequestCommandTCP {
requestFunc = func() error {
defer ray.OutboundInput().ForceClose()
return buf.PipeUntilEOF(ray.OutboundInput(), buf.NewWriter(conn))
}
responseFunc = func() error {
defer ray.OutboundOutput().Close()
return buf.PipeUntilEOF(buf.NewReader(conn), ray.OutboundOutput())
}
} else if request.Command == protocol.RequestCommandUDP {
udpConn, err := internet.Dial(c.meta.Address, udpRequest.Destination(), c.meta.GetDialerOptions())
if err != nil {
log.Info("Socks|Client: Failed to create UDP connection: ", err)
return
}
defer udpConn.Close()
requestFunc = func() error {
defer ray.OutboundInput().ForceClose()
return buf.PipeUntilEOF(ray.OutboundInput(), &UDPWriter{request: request, writer: udpConn})
}
responseFunc = func() error {
defer ray.OutboundOutput().Close()
reader := &UDPReader{reader: net.NewTimeOutReader(16, udpConn)}
return buf.PipeUntilEOF(reader, ray.OutboundOutput())
}
}
requestDone := signal.ExecuteAsync(requestFunc)
responseDone := signal.ExecuteAsync(responseFunc)
if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
log.Info("Socks|Client: Connection ends with ", err)
}
}
type ClientFactory struct{}
func (ClientFactory) StreamCapability() net.NetworkList {
return net.NetworkList{
Network: []net.Network{net.Network_TCP},
}
}
func (ClientFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
return NewClient(rawConfig.(*ClientConfig), space, meta)
}

View File

@ -3,6 +3,8 @@ package socks
import (
"io"
"fmt"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/errors"
v2net "v2ray.com/core/common/net"
@ -90,7 +92,7 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol
}
var expectedAuth byte = authNotRequired
if len(s.config.Accounts) > 0 {
if s.config.AuthType == AuthType_PASSWORD {
expectedAuth = authPassword
}
@ -99,21 +101,27 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol
return nil, errors.New("Socks|Server: No matching auth method.")
}
if err := writeSocks5AuthenticationResponse(writer, expectedAuth); err != nil {
return nil, err
}
fmt.Println("s a")
if expectedAuth == authPassword {
username, password, err := readUsernamePassword(reader)
if err != nil {
return nil, errors.Base(err).Message("Socks|Server: Failed to read username or password.")
}
fmt.Println("s b")
if !s.config.HasAccount(username, password) {
writeSocks5AuthenticationResponse(writer, 0xFF)
return nil, errors.Base(err).Message("Socks|Server: Invalid username or password.")
}
}
if err := writeSocks5AuthenticationResponse(writer, 0x00); err != nil {
return nil, err
if err := writeSocks5AuthenticationResponse(writer, 0x00); err != nil {
return nil, err
}
}
fmt.Println("s c")
buffer.Clear()
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil {
return nil, err
@ -194,21 +202,27 @@ func readUsernamePassword(reader io.Reader) (string, string, error) {
return "", "", err
}
nUsername := int(buffer.Byte(1))
fmt.Println("s username", nUsername)
buffer.Clear()
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, nUsername)); err != nil {
return "", "", err
}
username := buffer.String()
fmt.Println("s username", username)
buffer.Clear()
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil {
return "", "", err
}
nPassword := int(buffer.Byte(0))
fmt.Println("s pwd", nPassword)
buffer.Clear()
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, nPassword)); err != nil {
return "", "", err
}
password := buffer.String()
fmt.Println("s pwd", password)
return username, password, nil
}
@ -244,9 +258,7 @@ func writeSocks5AuthenticationResponse(writer io.Writer, auth byte) error {
return err
}
func writeSocks5Response(writer io.Writer, errCode byte, address v2net.Address, port v2net.Port) error {
buffer := buf.NewLocal(64)
buffer.AppendBytes(socks5Version, errCode, 0x00 /* reserved */)
func appendAddress(buffer *buf.Buffer, address v2net.Address, port v2net.Port) {
switch address.Family() {
case v2net.AddressFamilyIPv4:
buffer.AppendBytes(0x01)
@ -259,6 +271,12 @@ func writeSocks5Response(writer io.Writer, errCode byte, address v2net.Address,
buffer.AppendSupplier(serial.WriteString(address.Domain()))
}
buffer.AppendSupplier(serial.WriteUint16(port.Value()))
}
func writeSocks5Response(writer io.Writer, errCode byte, address v2net.Address, port v2net.Port) error {
buffer := buf.NewLocal(64)
buffer.AppendBytes(socks5Version, errCode, 0x00 /* reserved */)
appendAddress(buffer, address, port)
_, err := writer.Write(buffer.Bytes())
return err
@ -326,18 +344,162 @@ func DecodeUDPPacket(packet []byte) (*protocol.RequestHeader, []byte, error) {
func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) *buf.Buffer {
b := buf.NewSmall()
b.AppendBytes(0, 0, 0 /* Fragment */)
switch request.Address.Family() {
case v2net.AddressFamilyIPv4:
b.AppendBytes(addrTypeIPv4)
b.Append(request.Address.IP())
case v2net.AddressFamilyIPv6:
b.AppendBytes(addrTypeIPv6)
b.Append(request.Address.IP())
case v2net.AddressFamilyDomain:
b.AppendBytes(addrTypeDomain, byte(len(request.Address.Domain())))
b.AppendSupplier(serial.WriteString(request.Address.Domain()))
}
b.AppendSupplier(serial.WriteUint16(request.Port.Value()))
appendAddress(b, request.Address, request.Port)
b.Append(data)
return b
}
type UDPReader struct {
reader io.Reader
}
func (r *UDPReader) Read() (*buf.Buffer, error) {
b := buf.NewSmall()
if err := b.AppendSupplier(buf.ReadFrom(r.reader)); err != nil {
return nil, err
}
_, data, err := DecodeUDPPacket(b.Bytes())
if err != nil {
return nil, err
}
b.Clear()
b.Append(data)
return b, nil
}
type UDPWriter struct {
request *protocol.RequestHeader
writer io.Writer
}
func (w *UDPWriter) Write(b *buf.Buffer) error {
eb := EncodeUDPPacket(w.request, b.Bytes())
b.Release()
defer eb.Release()
if _, err := w.writer.Write(eb.Bytes()); err != nil {
return err
}
return nil
}
func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
authByte := byte(authNotRequired)
if request.User != nil {
authByte = byte(authPassword)
}
authRequest := []byte{socks5Version, 0x01, authByte}
if _, err := writer.Write(authRequest); err != nil {
return nil, err
}
b := buf.NewLocal(64)
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil {
return nil, err
}
if b.Byte(0) != socks5Version {
return nil, errors.New("Socks|Client: Unexpected server version: ", b.Byte(0))
}
if b.Byte(1) != authByte {
return nil, errors.New("Socks|Client: auth method not supported.")
}
if authByte == authPassword {
rawAccount, err := request.User.GetTypedAccount()
if err != nil {
return nil, err
}
account := rawAccount.(*Account)
fmt.Println("c username", account.Username)
fmt.Println("c pwd", account.Password)
b.Clear()
b.AppendBytes(socks5Version, byte(len(account.Username)))
b.Append([]byte(account.Username))
b.AppendBytes(byte(len(account.Password)))
b.Append([]byte(account.Password))
fmt.Println("c a")
if _, err := writer.Write(b.Bytes()); err != nil {
return nil, err
}
fmt.Println("c b")
b.Clear()
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil {
return nil, err
}
fmt.Println("c c")
if b.Byte(1) != 0x00 {
return nil, errors.New("Socks|Client: Server rejects account: ", b.Byte(1))
}
}
b.Clear()
command := byte(cmdTCPConnect)
if request.Command == protocol.RequestCommandUDP {
command = byte(cmdUDPPort)
}
b.AppendBytes(socks5Version, command, 0x00 /* reserved */)
appendAddress(b, request.Address, request.Port)
fmt.Println("c e")
if _, err := writer.Write(b.Bytes()); err != nil {
return nil, err
}
b.Clear()
fmt.Println("c f")
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil {
return nil, err
}
resp := b.Byte(1)
if resp != 0x00 {
return nil, errors.New("Socks|Client: Server rejects request: ", resp)
}
addrType := b.Byte(3)
b.Clear()
var address v2net.Address
switch addrType {
case addrTypeIPv4:
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil {
return nil, err
}
address = v2net.IPAddress(b.Bytes())
case addrTypeIPv6:
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil {
return nil, err
}
address = v2net.IPAddress(b.Bytes())
case addrTypeDomain:
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil {
return nil, err
}
domainLength := int(b.Byte(0))
if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil {
return nil, err
}
address = v2net.DomainAddress(string(b.BytesFrom(-domainLength)))
default:
return nil, errors.New("Socks|Server: Unknown address type: ", addrType)
}
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil {
return nil, err
}
port := v2net.PortFromBytes(b.BytesFrom(-2))
if request.Command == protocol.RequestCommandUDP {
udpRequest := &protocol.RequestHeader{
Version: socks5Version,
Command: protocol.RequestCommandUDP,
Address: address,
Port: port,
}
return udpRequest, nil
}
return nil, nil
}

View File

@ -7,14 +7,12 @@ import (
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/bufio"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/serial"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/internet"
@ -193,7 +191,3 @@ func (v *ServerFactory) StreamCapability() v2net.NetworkList {
func (v *ServerFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {
return NewServer(rawConfig.(*ServerConfig), space, meta), nil
}
func init() {
common.Must(proxy.RegisterInboundHandlerCreator(serial.GetMessageType(new(ServerConfig)), new(ServerFactory)))
}

12
proxy/socks/socks.go Normal file
View File

@ -0,0 +1,12 @@
package socks
import (
"v2ray.com/core/common"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy"
)
func init() {
common.Must(proxy.RegisterOutboundHandlerCreator(serial.GetMessageType((*ClientConfig)(nil)), new(ClientFactory)))
common.Must(proxy.RegisterInboundHandlerCreator(serial.GetMessageType((*ServerConfig)(nil)), new(ServerFactory)))
}

View File

@ -0,0 +1,254 @@
package scenarios
import (
"net"
"testing"
socks1 "h12.me/socks"
"v2ray.com/core"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy/dokodemo"
"v2ray.com/core/proxy/freedom"
"v2ray.com/core/proxy/socks"
"v2ray.com/core/testing/assert"
"v2ray.com/core/testing/servers/tcp"
"v2ray.com/core/testing/servers/udp"
)
func TestSocksBridgeTCP(t *testing.T) {
assert := assert.On(t)
tcpServer := tcp.Server{
MsgProcessor: xor,
}
dest, err := tcpServer.Start()
assert.Error(err).IsNil()
defer tcpServer.Close()
serverPort := pickPort()
serverConfig := &core.Config{
Inbound: []*core.InboundConnectionConfig{
{
PortRange: v2net.SinglePortRange(serverPort),
ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP),
Settings: serial.ToTypedMessage(&socks.ServerConfig{
AuthType: socks.AuthType_PASSWORD,
Accounts: map[string]string{
"Test Account": "Test Password",
},
Address: v2net.NewIPOrDomain(v2net.LocalHostIP),
UdpEnabled: false,
}),
},
},
Outbound: []*core.OutboundConnectionConfig{
{
Settings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
clientPort := pickPort()
clientConfig := &core.Config{
Inbound: []*core.InboundConnectionConfig{
{
PortRange: v2net.SinglePortRange(clientPort),
ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP),
Settings: serial.ToTypedMessage(&dokodemo.Config{
Address: v2net.NewIPOrDomain(dest.Address),
Port: uint32(dest.Port),
NetworkList: &v2net.NetworkList{
Network: []v2net.Network{v2net.Network_TCP},
},
}),
},
},
Outbound: []*core.OutboundConnectionConfig{
{
Settings: serial.ToTypedMessage(&socks.ClientConfig{
Server: []*protocol.ServerEndpoint{
{
Address: v2net.NewIPOrDomain(v2net.LocalHostIP),
Port: uint32(serverPort),
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&socks.Account{
Username: "Test Account",
Password: "Test Password",
}),
},
},
},
},
}),
},
},
}
assert.Error(InitializeServerConfig(serverConfig)).IsNil()
assert.Error(InitializeServerConfig(clientConfig)).IsNil()
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(clientPort),
})
assert.Error(err).IsNil()
payload := "test payload"
nBytes, err := conn.Write([]byte(payload))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(payload))
response := make([]byte, 1024)
nBytes, err = conn.Read(response)
assert.Error(err).IsNil()
assert.Bytes(response[:nBytes]).Equals(xor([]byte(payload)))
assert.Error(conn.Close()).IsNil()
CloseAllServers()
}
func TestSocks4(t *testing.T) {
assert := assert.On(t)
tcpServer := tcp.Server{
MsgProcessor: xor,
}
dest, err := tcpServer.Start()
assert.Error(err).IsNil()
defer tcpServer.Close()
serverPort := pickPort()
serverConfig := &core.Config{
Inbound: []*core.InboundConnectionConfig{
{
PortRange: v2net.SinglePortRange(serverPort),
ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP),
Settings: serial.ToTypedMessage(&socks.ServerConfig{
AuthType: socks.AuthType_NO_AUTH,
Address: v2net.NewIPOrDomain(v2net.LocalHostIP),
UdpEnabled: false,
}),
},
},
Outbound: []*core.OutboundConnectionConfig{
{
Settings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
assert.Error(InitializeServerConfig(serverConfig)).IsNil()
dialer := socks1.DialSocksProxy(socks1.SOCKS4, v2net.TCPDestination(v2net.LocalHostIP, serverPort).NetAddr())
conn, err := dialer("tcp", dest.NetAddr())
assert.Error(err).IsNil()
payload := "test payload"
nBytes, err := conn.Write([]byte(payload))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(payload))
response := make([]byte, 1024)
nBytes, err = conn.Read(response)
assert.Error(err).IsNil()
assert.Bytes(response[:nBytes]).Equals(xor([]byte(payload)))
assert.Error(conn.Close()).IsNil()
CloseAllServers()
}
func TestSocksBridageUDP(t *testing.T) {
assert := assert.On(t)
udpServer := udp.Server{
MsgProcessor: xor,
}
dest, err := udpServer.Start()
assert.Error(err).IsNil()
defer udpServer.Close()
serverPort := pickPort()
serverConfig := &core.Config{
Inbound: []*core.InboundConnectionConfig{
{
PortRange: v2net.SinglePortRange(serverPort),
ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP),
Settings: serial.ToTypedMessage(&socks.ServerConfig{
AuthType: socks.AuthType_PASSWORD,
Accounts: map[string]string{
"Test Account": "Test Password",
},
Address: v2net.NewIPOrDomain(v2net.LocalHostIP),
UdpEnabled: true,
}),
},
},
Outbound: []*core.OutboundConnectionConfig{
{
Settings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
clientPort := pickPort()
clientConfig := &core.Config{
Inbound: []*core.InboundConnectionConfig{
{
PortRange: v2net.SinglePortRange(clientPort),
ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP),
Settings: serial.ToTypedMessage(&dokodemo.Config{
Address: v2net.NewIPOrDomain(dest.Address),
Port: uint32(dest.Port),
NetworkList: &v2net.NetworkList{
Network: []v2net.Network{v2net.Network_TCP, v2net.Network_UDP},
},
}),
},
},
Outbound: []*core.OutboundConnectionConfig{
{
Settings: serial.ToTypedMessage(&socks.ClientConfig{
Server: []*protocol.ServerEndpoint{
{
Address: v2net.NewIPOrDomain(v2net.LocalHostIP),
Port: uint32(serverPort),
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&socks.Account{
Username: "Test Account",
Password: "Test Password",
}),
},
},
},
},
}),
},
},
}
assert.Error(InitializeServerConfig(serverConfig)).IsNil()
assert.Error(InitializeServerConfig(clientConfig)).IsNil()
conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(clientPort),
})
assert.Error(err).IsNil()
payload := "dokodemo request."
nBytes, err := conn.Write([]byte(payload))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(payload))
response := make([]byte, 1024)
nBytes, err = conn.Read(response)
assert.Error(err).IsNil()
assert.Bytes(response[:nBytes]).Equals(xor([]byte(payload)))
assert.Error(conn.Close()).IsNil()
CloseAllServers()
}