diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index f15b1dce..d9e1e5aa 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -1,6 +1,7 @@ package shadowsocks import ( + "crypto/md5" "io" "github.com/v2ray/v2ray-core/common/crypto" @@ -44,7 +45,25 @@ func (this *AesCfb) NewDecodingStream(key []byte, iv []byte, reader io.Reader) ( } type Config struct { - Cipher Cipher - Password string - UDP bool + Cipher Cipher + Key []byte + UDP bool +} + +func PasswordToCipherKey(password string, keySize int) []byte { + pwdBytes := []byte(password) + key := make([]byte, 0, keySize) + + md5Sum := md5.Sum(pwdBytes) + key = append(key, md5Sum[:]...) + + for len(key) < keySize { + md5Hash := md5.New() + md5Hash.Write(md5Sum[:]) + md5Hash.Write(pwdBytes) + md5Hash.Sum(md5Sum[:0]) + + key = append(key, md5Sum[:]...) + } + return key } diff --git a/proxy/shadowsocks/config_json.go b/proxy/shadowsocks/config_json.go index 4a2ed17c..71fe1d8c 100644 --- a/proxy/shadowsocks/config_json.go +++ b/proxy/shadowsocks/config_json.go @@ -8,6 +8,7 @@ import ( "github.com/v2ray/v2ray-core/common/log" "github.com/v2ray/v2ray-core/common/serial" "github.com/v2ray/v2ray-core/proxy/internal" + "github.com/v2ray/v2ray-core/proxy/internal/config" ) func (this *Config) UnmarshalJSON(data []byte) error { @@ -20,16 +21,8 @@ func (this *Config) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, jsonConfig); err != nil { return err } - if len(jsonConfig.Password) == 0 { - log.Error("Shadowsocks: Password is not specified.") - return internal.ErrorBadConfiguration - } + this.UDP = jsonConfig.UDP - this.Password = jsonConfig.Password.String() - if this.Cipher == nil { - log.Error("Shadowsocks: Cipher method is not specified.") - return internal.ErrorBadConfiguration - } jsonConfig.Cipher = jsonConfig.Cipher.ToLower() switch jsonConfig.Cipher.String() { case "aes-256-cfb": @@ -38,11 +31,26 @@ func (this *Config) UnmarshalJSON(data []byte) error { } case "aes-128-cfb": this.Cipher = &AesCfb{ - KeyBytes: 32, + KeyBytes: 16, } default: log.Error("Shadowsocks: Unknown cipher method: ", jsonConfig.Cipher) return internal.ErrorBadConfiguration } + + if len(jsonConfig.Password) == 0 { + log.Error("Shadowsocks: Password is not specified.") + return internal.ErrorBadConfiguration + } + this.Key = PasswordToCipherKey(jsonConfig.Password.String(), this.Cipher.KeySize()) + return nil } + +func init() { + config.RegisterInboundConfig("shadowsocks", func(data []byte) (interface{}, error) { + rawConfig := new(Config) + err := json.Unmarshal(data, rawConfig) + return rawConfig, err + }) +} diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go new file mode 100644 index 00000000..8df98b4b --- /dev/null +++ b/proxy/shadowsocks/protocol.go @@ -0,0 +1,77 @@ +package shadowsocks + +import ( + "io" + + "github.com/v2ray/v2ray-core/common/alloc" + "github.com/v2ray/v2ray-core/common/log" + v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/transport" +) + +const ( + AddrTypeIPv4 = 1 + AddrTypeIPv6 = 4 + AddrTypeDomain = 3 +) + +type Request struct { + Address v2net.Address + Port v2net.Port +} + +func ReadRequest(reader io.Reader) (*Request, error) { + buffer := alloc.NewSmallBuffer() + defer buffer.Release() + + _, err := v2net.ReadAllBytes(reader, buffer.Value[:1]) + if err != nil { + log.Error("Shadowsocks: Failed to read address type: ", err) + return nil, transport.CorruptedPacket + } + + request := new(Request) + + addrType := buffer.Value[0] + switch addrType { + case AddrTypeIPv4: + _, err := v2net.ReadAllBytes(reader, buffer.Value[:4]) + if err != nil { + log.Error("Shadowsocks: Failed to read IPv4 address: ", err) + return nil, transport.CorruptedPacket + } + request.Address = v2net.IPAddress(buffer.Value[:4]) + case AddrTypeIPv6: + _, err := v2net.ReadAllBytes(reader, buffer.Value[:16]) + if err != nil { + log.Error("Shadowsocks: Failed to read IPv6 address: ", err) + return nil, transport.CorruptedPacket + } + request.Address = v2net.IPAddress(buffer.Value[:16]) + case AddrTypeDomain: + _, err := v2net.ReadAllBytes(reader, buffer.Value[:1]) + if err != nil { + log.Error("Shadowsocks: Failed to read domain lenth: ", err) + return nil, transport.CorruptedPacket + } + domainLength := int(buffer.Value[0]) + _, err = v2net.ReadAllBytes(reader, buffer.Value[:domainLength]) + if err != nil { + log.Error("Shadowsocks: Failed to read domain: ", err) + return nil, transport.CorruptedPacket + } + request.Address = v2net.DomainAddress(string(buffer.Value[:domainLength])) + default: + log.Error("Shadowsocks: Unknown address type: ", addrType) + return nil, transport.CorruptedPacket + } + + _, err = v2net.ReadAllBytes(reader, buffer.Value[:2]) + if err != nil { + log.Error("Shadowsocks: Failed to read port: ", err) + return nil, transport.CorruptedPacket + } + + request.Port = v2net.PortFromBytes(buffer.Value[:2]) + return request, nil +} diff --git a/proxy/shadowsocks/shadowsocks.go b/proxy/shadowsocks/shadowsocks.go index e8eef6d7..168551eb 100644 --- a/proxy/shadowsocks/shadowsocks.go +++ b/proxy/shadowsocks/shadowsocks.go @@ -3,13 +3,19 @@ package shadowsocks import ( + "sync" + + "github.com/v2ray/v2ray-core/app" + "github.com/v2ray/v2ray-core/common/alloc" "github.com/v2ray/v2ray-core/common/log" v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/proxy" + "github.com/v2ray/v2ray-core/proxy/internal" "github.com/v2ray/v2ray-core/transport/listener" ) type Shadowsocks struct { + space app.Space config *Config port v2net.Port accepting bool @@ -48,4 +54,58 @@ func (this *Shadowsocks) Listen(port v2net.Port) error { func (this *Shadowsocks) handleConnection(conn *listener.TCPConn) { defer conn.Close() + buffer := alloc.NewSmallBuffer() + defer buffer.Release() + + _, err := v2net.ReadAllBytes(conn, buffer.Value[:this.config.Cipher.IVSize()]) + if err != nil { + log.Error("Shadowsocks: Failed to read IV: ", err) + return + } + + iv := buffer.Value[:this.config.Cipher.IVSize()] + key := this.config.Key + + reader, err := this.config.Cipher.NewDecodingStream(key, iv, conn) + if err != nil { + log.Error("Shadowsocks: Failed to create decoding stream: ", err) + return + } + + request, err := ReadRequest(reader) + if err != nil { + return + } + + packet := v2net.NewPacket(v2net.TCPDestination(request.Address, request.Port), nil, true) + ray := this.space.PacketDispatcher().DispatchToOutbound(packet) + + writer, err := this.config.Cipher.NewEncodingStream(key, iv, conn) + if err != nil { + log.Error("Shadowsocks: Failed to create encoding stream: ", err) + return + } + + var writeFinish sync.Mutex + writeFinish.Lock() + go func() { + v2net.ChanToWriter(writer, ray.InboundOutput()) + writeFinish.Unlock() + }() + + v2net.ReaderToChan(ray.InboundInput(), reader) + close(ray.InboundInput()) + + writeFinish.Lock() +} + +func init() { + internal.MustRegisterInboundHandlerCreator("shadowsocks", + func(space app.Space, rawConfig interface{}) (proxy.InboundHandler, error) { + config := rawConfig.(*Config) + return &Shadowsocks{ + space: space, + config: config, + }, nil + }) }