mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-09 19:56:44 -05:00
complete implementation of shadowsocks ota
This commit is contained in:
parent
7f5184e943
commit
57ff7ba923
@ -87,9 +87,9 @@ type AuthenticationReader struct {
|
||||
authBeforePayload bool
|
||||
}
|
||||
|
||||
func NewAuthenticationReader(reader io.Reader, auth crypto.Authenticator, authBeforePayload bool) *AuthenticationReader {
|
||||
func NewAuthenticationReader(reader Reader, auth crypto.Authenticator, authBeforePayload bool) *AuthenticationReader {
|
||||
return &AuthenticationReader{
|
||||
reader: NewChunkReader(reader),
|
||||
reader: reader,
|
||||
authenticator: auth,
|
||||
authBeforePayload: authBeforePayload,
|
||||
}
|
||||
|
@ -36,6 +36,16 @@ func (this IntLiteral) Value() int {
|
||||
return int(this)
|
||||
}
|
||||
|
||||
func (this IntLiteral) Bytes() []byte {
|
||||
value := this.Value()
|
||||
return []byte{
|
||||
byte(value >> 24),
|
||||
byte(value >> 16),
|
||||
byte(value >> 8),
|
||||
byte(value),
|
||||
}
|
||||
}
|
||||
|
||||
type Int64Literal int64
|
||||
|
||||
func (this Int64Literal) String() string {
|
||||
|
55
proxy/shadowsocks/ota.go
Normal file
55
proxy/shadowsocks/ota.go
Normal file
@ -0,0 +1,55 @@
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthSize = 10
|
||||
)
|
||||
|
||||
type KeyGenerator func() []byte
|
||||
|
||||
type Authenticator struct {
|
||||
key KeyGenerator
|
||||
}
|
||||
|
||||
func NewAuthenticator(keygen KeyGenerator) *Authenticator {
|
||||
return &Authenticator{
|
||||
key: keygen,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Authenticator) AuthSize() int {
|
||||
return AuthSize
|
||||
}
|
||||
|
||||
func (this *Authenticator) Authenticate(auth []byte, data []byte) []byte {
|
||||
hasher := hmac.New(sha1.New, this.key())
|
||||
hasher.Write(data)
|
||||
res := hasher.Sum(nil)
|
||||
return append(auth, res[:AuthSize]...)
|
||||
}
|
||||
|
||||
func HeaderKeyGenerator(key []byte, iv []byte) func() []byte {
|
||||
return func() []byte {
|
||||
newKey := make([]byte, 0, len(key)+len(iv))
|
||||
newKey = append(newKey, key...)
|
||||
newKey = append(newKey, iv...)
|
||||
return newKey
|
||||
}
|
||||
}
|
||||
|
||||
func ChunkKeyGenerator(iv []byte) func() []byte {
|
||||
chunkId := 0
|
||||
return func() []byte {
|
||||
newKey := make([]byte, 0, len(iv)+4)
|
||||
newKey = append(newKey, iv...)
|
||||
newKey = append(newKey, serial.IntLiteral(chunkId).Bytes()...)
|
||||
chunkId++
|
||||
return newKey
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import (
|
||||
"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/common/serial"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
)
|
||||
|
||||
@ -21,7 +22,7 @@ type Request struct {
|
||||
OTA bool
|
||||
}
|
||||
|
||||
func ReadRequest(reader io.Reader) (*Request, error) {
|
||||
func ReadRequest(reader io.Reader, auth *Authenticator) (*Request, error) {
|
||||
buffer := alloc.NewSmallBuffer()
|
||||
defer buffer.Release()
|
||||
|
||||
@ -30,6 +31,7 @@ func ReadRequest(reader io.Reader) (*Request, error) {
|
||||
log.Error("Shadowsocks: Failed to read address type: ", err)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
lenBuffer := 1
|
||||
|
||||
request := new(Request)
|
||||
|
||||
@ -39,43 +41,64 @@ func ReadRequest(reader io.Reader) (*Request, error) {
|
||||
}
|
||||
switch addrType {
|
||||
case AddrTypeIPv4:
|
||||
_, err := io.ReadFull(reader, buffer.Value[:4])
|
||||
_, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+4])
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks: Failed to read IPv4 address: ", err)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
request.Address = v2net.IPAddress(buffer.Value[:4])
|
||||
request.Address = v2net.IPAddress(buffer.Value[lenBuffer : lenBuffer+4])
|
||||
lenBuffer += 4
|
||||
case AddrTypeIPv6:
|
||||
_, err := io.ReadFull(reader, buffer.Value[:16])
|
||||
_, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+16])
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks: Failed to read IPv6 address: ", err)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
request.Address = v2net.IPAddress(buffer.Value[:16])
|
||||
request.Address = v2net.IPAddress(buffer.Value[lenBuffer : lenBuffer+16])
|
||||
lenBuffer += 16
|
||||
case AddrTypeDomain:
|
||||
_, err := io.ReadFull(reader, buffer.Value[:1])
|
||||
_, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+1])
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks: Failed to read domain lenth: ", err)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
domainLength := int(buffer.Value[0])
|
||||
_, err = io.ReadFull(reader, buffer.Value[:domainLength])
|
||||
domainLength := int(buffer.Value[lenBuffer])
|
||||
lenBuffer++
|
||||
_, err = io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+domainLength])
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks: Failed to read domain: ", err)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
request.Address = v2net.DomainAddress(string(buffer.Value[:domainLength]))
|
||||
request.Address = v2net.DomainAddress(string(buffer.Value[lenBuffer : lenBuffer+domainLength]))
|
||||
lenBuffer += domainLength
|
||||
default:
|
||||
log.Error("Shadowsocks: Unknown address type: ", addrType)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
|
||||
_, err = io.ReadFull(reader, buffer.Value[:2])
|
||||
_, err = io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+2])
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks: Failed to read port: ", err)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
|
||||
request.Port = v2net.PortFromBytes(buffer.Value[:2])
|
||||
request.Port = v2net.PortFromBytes(buffer.Value[lenBuffer : lenBuffer+2])
|
||||
lenBuffer += 2
|
||||
|
||||
if request.OTA {
|
||||
authBytes := buffer.Value[lenBuffer : lenBuffer+auth.AuthSize()]
|
||||
_, err = io.ReadFull(reader, authBytes)
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks: Failed to read OTA: ", err)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
|
||||
actualAuth := auth.Authenticate(nil, buffer.Value[0:lenBuffer])
|
||||
if !serial.BytesLiteral(actualAuth).Equals(serial.BytesLiteral(authBytes)) {
|
||||
log.Error("Shadowsocks: Invalid OTA: ", actualAuth)
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ func TestNormalRequestParsing(t *testing.T) {
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer.AppendBytes(1, 127, 0, 0, 1, 0, 80)
|
||||
|
||||
request, err := ReadRequest(buffer)
|
||||
request, err := ReadRequest(buffer, nil)
|
||||
assert.Error(err).IsNil()
|
||||
netassert.Address(request.Address).Equals(v2net.IPAddress([]byte{127, 0, 0, 1}))
|
||||
netassert.Port(request.Port).Equals(v2net.Port(80))
|
||||
@ -28,9 +28,12 @@ func TestOTARequest(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer.AppendBytes(0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0)
|
||||
buffer.AppendBytes(0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0, 239, 115, 52, 212, 178, 172, 26, 6, 168, 0)
|
||||
|
||||
request, err := ReadRequest(buffer)
|
||||
auth := NewAuthenticator(HeaderKeyGenerator(
|
||||
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5},
|
||||
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5}))
|
||||
request, err := ReadRequest(buffer, auth)
|
||||
assert.Error(err).IsNil()
|
||||
netassert.Address(request.Address).Equals(v2net.DomainAddress("www.v2ray.com"))
|
||||
assert.Bool(request.OTA).IsTrue()
|
||||
|
@ -32,11 +32,16 @@ func (this *Shadowsocks) Port() v2net.Port {
|
||||
|
||||
func (this *Shadowsocks) Close() {
|
||||
this.accepting = false
|
||||
this.tcpHub.Close()
|
||||
this.tcpHub = nil
|
||||
if this.tcpHub != nil {
|
||||
this.tcpHub.Close()
|
||||
this.tcpHub = nil
|
||||
}
|
||||
|
||||
if this.udpHub != nil {
|
||||
this.udpHub.Close()
|
||||
this.udpHub = nil
|
||||
}
|
||||
|
||||
this.udpHub.Close()
|
||||
this.udpHub = nil
|
||||
}
|
||||
|
||||
func (this *Shadowsocks) Listen(port v2net.Port) error {
|
||||
@ -80,7 +85,7 @@ func (this *Shadowsocks) handlerUDPPayload(payload *alloc.Buffer, dest v2net.Des
|
||||
return
|
||||
}
|
||||
|
||||
request, err := ReadRequest(reader)
|
||||
request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(key, iv)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -95,8 +100,9 @@ func (this *Shadowsocks) handlerUDPPayload(payload *alloc.Buffer, dest v2net.Des
|
||||
|
||||
response := alloc.NewBuffer().Slice(0, this.config.Cipher.IVSize())
|
||||
rand.Read(response.Value)
|
||||
respIv := response.Value
|
||||
|
||||
writer, err := this.config.Cipher.NewEncodingStream(key, response.Value, response)
|
||||
writer, err := this.config.Cipher.NewEncodingStream(key, respIv, response)
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks: Failed to create encoding stream: ", err)
|
||||
return
|
||||
@ -118,6 +124,11 @@ func (this *Shadowsocks) handlerUDPPayload(payload *alloc.Buffer, dest v2net.Des
|
||||
writer.Write(respChunk.Value)
|
||||
respChunk.Release()
|
||||
|
||||
if request.OTA {
|
||||
respAuth := NewAuthenticator(HeaderKeyGenerator(key, respIv))
|
||||
respAuth.Authenticate(buffer.Value, buffer.Value[this.config.Cipher.IVSize():])
|
||||
}
|
||||
|
||||
this.udpHub.WriteTo(response.Value, dest)
|
||||
response.Release()
|
||||
}
|
||||
@ -144,7 +155,7 @@ func (this *Shadowsocks) handleConnection(conn *hub.TCPConn) {
|
||||
return
|
||||
}
|
||||
|
||||
request, err := ReadRequest(reader)
|
||||
request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(key, iv)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -174,7 +185,15 @@ func (this *Shadowsocks) handleConnection(conn *hub.TCPConn) {
|
||||
writeFinish.Unlock()
|
||||
}()
|
||||
|
||||
v2io.RawReaderToChan(ray.InboundInput(), reader)
|
||||
var payloadReader v2io.Reader
|
||||
if request.OTA {
|
||||
payloadAuth := NewAuthenticator(ChunkKeyGenerator(iv))
|
||||
payloadReader = v2io.NewAuthenticationReader(v2io.NewChunkReader(reader), payloadAuth, true)
|
||||
} else {
|
||||
payloadReader = v2io.NewAdaptiveReader(reader)
|
||||
}
|
||||
|
||||
v2io.ReaderToChan(ray.InboundInput(), payloadReader)
|
||||
close(ray.InboundInput())
|
||||
|
||||
writeFinish.Lock()
|
||||
|
Loading…
Reference in New Issue
Block a user