mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-22 10:08:15 -05:00
commit
6a21f93be0
@ -54,7 +54,7 @@ func IgnoreWriterError() CopyOption {
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateActivity(timer signal.ActivityTimer) CopyOption {
|
||||
func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
|
||||
return func(handler *copyHandler) {
|
||||
handler.onData = append(handler.onData, func(MultiBuffer) {
|
||||
timer.Update()
|
||||
|
@ -10,3 +10,9 @@ func Must(err error) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Must2(v interface{}, err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -35,25 +35,29 @@ const (
|
||||
AddressFamilyDomain = AddressFamily(2)
|
||||
)
|
||||
|
||||
func (v AddressFamily) Either(fs ...AddressFamily) bool {
|
||||
// Either returns true if current AddressFamily matches any of the AddressFamilys provided.
|
||||
func (af AddressFamily) Either(fs ...AddressFamily) bool {
|
||||
for _, f := range fs {
|
||||
if v == f {
|
||||
if af == f {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v AddressFamily) IsIPv4() bool {
|
||||
return v == AddressFamilyIPv4
|
||||
// IsIPv4 returns true if current AddressFamily is IPv4.
|
||||
func (af AddressFamily) IsIPv4() bool {
|
||||
return af == AddressFamilyIPv4
|
||||
}
|
||||
|
||||
func (v AddressFamily) IsIPv6() bool {
|
||||
return v == AddressFamilyIPv6
|
||||
// IsIPv6 returns true if current AddressFamily is IPv6.
|
||||
func (af AddressFamily) IsIPv6() bool {
|
||||
return af == AddressFamilyIPv6
|
||||
}
|
||||
|
||||
func (v AddressFamily) IsDomain() bool {
|
||||
return v == AddressFamilyDomain
|
||||
// IsDomain returns true if current AddressFamily is Domain.
|
||||
func (af AddressFamily) IsDomain() bool {
|
||||
return af == AddressFamilyDomain
|
||||
}
|
||||
|
||||
// Address represents a network address to be communicated with. It may be an IP address or domain
|
||||
@ -106,8 +110,8 @@ func DomainAddress(domain string) Address {
|
||||
|
||||
type ipv4Address [4]byte
|
||||
|
||||
func (v ipv4Address) IP() net.IP {
|
||||
return net.IP(v[:])
|
||||
func (a ipv4Address) IP() net.IP {
|
||||
return net.IP(a[:])
|
||||
}
|
||||
|
||||
func (ipv4Address) Domain() string {
|
||||
@ -118,14 +122,14 @@ func (ipv4Address) Family() AddressFamily {
|
||||
return AddressFamilyIPv4
|
||||
}
|
||||
|
||||
func (v ipv4Address) String() string {
|
||||
return v.IP().String()
|
||||
func (a ipv4Address) String() string {
|
||||
return a.IP().String()
|
||||
}
|
||||
|
||||
type ipv6Address [16]byte
|
||||
|
||||
func (v ipv6Address) IP() net.IP {
|
||||
return net.IP(v[:])
|
||||
func (a ipv6Address) IP() net.IP {
|
||||
return net.IP(a[:])
|
||||
}
|
||||
|
||||
func (ipv6Address) Domain() string {
|
||||
@ -136,8 +140,8 @@ func (ipv6Address) Family() AddressFamily {
|
||||
return AddressFamilyIPv6
|
||||
}
|
||||
|
||||
func (v ipv6Address) String() string {
|
||||
return "[" + v.IP().String() + "]"
|
||||
func (a ipv6Address) String() string {
|
||||
return "[" + a.IP().String() + "]"
|
||||
}
|
||||
|
||||
type domainAddress string
|
||||
@ -146,23 +150,24 @@ func (domainAddress) IP() net.IP {
|
||||
panic("Calling IP() on a DomainAddress.")
|
||||
}
|
||||
|
||||
func (v domainAddress) Domain() string {
|
||||
return string(v)
|
||||
func (a domainAddress) Domain() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
func (domainAddress) Family() AddressFamily {
|
||||
return AddressFamilyDomain
|
||||
}
|
||||
|
||||
func (v domainAddress) String() string {
|
||||
return v.Domain()
|
||||
func (a domainAddress) String() string {
|
||||
return a.Domain()
|
||||
}
|
||||
|
||||
func (v *IPOrDomain) AsAddress() Address {
|
||||
if v == nil {
|
||||
// AsAddress translates IPOrDomain to Address.
|
||||
func (d *IPOrDomain) AsAddress() Address {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
switch addr := v.Address.(type) {
|
||||
switch addr := d.Address.(type) {
|
||||
case *IPOrDomain_Ip:
|
||||
return IPAddress(addr.Ip)
|
||||
case *IPOrDomain_Domain:
|
||||
@ -171,6 +176,7 @@ func (v *IPOrDomain) AsAddress() Address {
|
||||
panic("Common|Net: Invalid address.")
|
||||
}
|
||||
|
||||
// NewIPOrDomain translates Address to IPOrDomain
|
||||
func NewIPOrDomain(addr Address) *IPOrDomain {
|
||||
switch addr.Family() {
|
||||
case AddressFamilyDomain:
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"crypto/md5"
|
||||
"hash"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/uuid"
|
||||
)
|
||||
|
||||
@ -25,31 +26,32 @@ type ID struct {
|
||||
}
|
||||
|
||||
// Equals returns true if this ID equals to the other one.
|
||||
func (v *ID) Equals(another *ID) bool {
|
||||
return v.uuid.Equals(another.uuid)
|
||||
func (id *ID) Equals(another *ID) bool {
|
||||
return id.uuid.Equals(another.uuid)
|
||||
}
|
||||
|
||||
func (v *ID) Bytes() []byte {
|
||||
return v.uuid.Bytes()
|
||||
func (id *ID) Bytes() []byte {
|
||||
return id.uuid.Bytes()
|
||||
}
|
||||
|
||||
func (v *ID) String() string {
|
||||
return v.uuid.String()
|
||||
func (id *ID) String() string {
|
||||
return id.uuid.String()
|
||||
}
|
||||
|
||||
func (v *ID) UUID() *uuid.UUID {
|
||||
return v.uuid
|
||||
func (id *ID) UUID() *uuid.UUID {
|
||||
return id.uuid
|
||||
}
|
||||
|
||||
func (v ID) CmdKey() []byte {
|
||||
return v.cmdKey[:]
|
||||
func (id ID) CmdKey() []byte {
|
||||
return id.cmdKey[:]
|
||||
}
|
||||
|
||||
// NewID returns an ID with given UUID.
|
||||
func NewID(uuid *uuid.UUID) *ID {
|
||||
id := &ID{uuid: uuid}
|
||||
md5hash := md5.New()
|
||||
md5hash.Write(uuid.Bytes())
|
||||
md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21"))
|
||||
common.Must2(md5hash.Write(uuid.Bytes()))
|
||||
common.Must2(md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21")))
|
||||
md5hash.Sum(id.cmdKey[:0])
|
||||
return id
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ var _ = math.Inf
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Serialized proto message along with its type name.
|
||||
// TypedMessage is a serialized proto message along with its type name.
|
||||
type TypedMessage struct {
|
||||
// The name of the message type, retrieved from protobuf API.
|
||||
Type string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"`
|
||||
|
@ -5,33 +5,42 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type ActivityTimer interface {
|
||||
type ActivityUpdater interface {
|
||||
Update()
|
||||
}
|
||||
|
||||
type realActivityTimer struct {
|
||||
type ActivityTimer struct {
|
||||
updated chan bool
|
||||
timeout time.Duration
|
||||
timeout chan time.Duration
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (t *realActivityTimer) Update() {
|
||||
func (t *ActivityTimer) Update() {
|
||||
select {
|
||||
case t.updated <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (t *realActivityTimer) run() {
|
||||
ticker := time.NewTicker(t.timeout)
|
||||
defer ticker.Stop()
|
||||
func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
|
||||
t.timeout <- timeout
|
||||
}
|
||||
|
||||
func (t *ActivityTimer) run() {
|
||||
ticker := time.NewTicker(<-t.timeout)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case <-t.ctx.Done():
|
||||
return
|
||||
case timeout := <-t.timeout:
|
||||
ticker.Stop()
|
||||
ticker = time.NewTicker(timeout)
|
||||
}
|
||||
|
||||
select {
|
||||
@ -44,14 +53,15 @@ func (t *realActivityTimer) run() {
|
||||
}
|
||||
}
|
||||
|
||||
func CancelAfterInactivity(ctx context.Context, timeout time.Duration) (context.Context, ActivityTimer) {
|
||||
func CancelAfterInactivity(ctx context.Context, timeout time.Duration) (context.Context, *ActivityTimer) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
timer := &realActivityTimer{
|
||||
timer := &ActivityTimer{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
timeout: timeout,
|
||||
timeout: make(chan time.Duration, 1),
|
||||
updated: make(chan bool, 1),
|
||||
}
|
||||
timer.timeout <- timeout
|
||||
go timer.run()
|
||||
return ctx, timer
|
||||
}
|
||||
|
32
common/signal/timer_test.go
Normal file
32
common/signal/timer_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package signal_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "v2ray.com/core/common/signal"
|
||||
"v2ray.com/core/testing/assert"
|
||||
)
|
||||
|
||||
func TestActivityTimer(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
ctx, timer := CancelAfterInactivity(context.Background(), time.Second*5)
|
||||
time.Sleep(time.Second * 6)
|
||||
assert.Error(ctx.Err()).IsNotNil()
|
||||
runtime.KeepAlive(timer)
|
||||
}
|
||||
|
||||
func TestActivityTimerUpdate(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
ctx, timer := CancelAfterInactivity(context.Background(), time.Second*10)
|
||||
time.Sleep(time.Second * 3)
|
||||
assert.Error(ctx.Err()).IsNil()
|
||||
timer.SetTimeout(time.Second * 1)
|
||||
time.Sleep(time.Second * 2)
|
||||
assert.Error(ctx.Err()).IsNotNil()
|
||||
runtime.KeepAlive(timer)
|
||||
}
|
2
core.go
2
core.go
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "2.38"
|
||||
version = "2.41"
|
||||
build = "Custom"
|
||||
codename = "One for all"
|
||||
intro = "An unified platform for anti-censorship."
|
||||
|
@ -62,7 +62,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
|
||||
|
||||
timeout := time.Second * time.Duration(d.config.Timeout)
|
||||
if timeout == 0 {
|
||||
timeout = time.Minute * 2
|
||||
timeout = time.Minute * 5
|
||||
}
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, timeout)
|
||||
|
||||
@ -94,6 +94,9 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
|
||||
if err := buf.Copy(inboundRay.InboundOutput(), writer, buf.UpdateActivity(timer)); err != nil {
|
||||
return newError("failed to transport response").Base(err)
|
||||
}
|
||||
|
||||
timer.SetTimeout(time.Second * 2)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -62,11 +62,10 @@ func (v *Handler) ResolveIP(destination net.Destination) net.Destination {
|
||||
}
|
||||
|
||||
ip := ips[dice.Roll(len(ips))]
|
||||
var newDest net.Destination
|
||||
if destination.Network == net.Network_TCP {
|
||||
newDest = net.TCPDestination(net.IPAddress(ip), destination.Port)
|
||||
} else {
|
||||
newDest = net.UDPDestination(net.IPAddress(ip), destination.Port)
|
||||
newDest := net.Destination{
|
||||
Network: destination.Network,
|
||||
Address: net.IPAddress(ip),
|
||||
Port: destination.Port,
|
||||
}
|
||||
log.Trace(newError("changing destination from ", destination, " to ", newDest))
|
||||
return newDest
|
||||
|
@ -125,7 +125,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
|
||||
|
||||
timeout := time.Second * time.Duration(s.config.Timeout)
|
||||
if timeout == 0 {
|
||||
timeout = time.Minute * 2
|
||||
timeout = time.Minute * 5
|
||||
}
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, timeout)
|
||||
ray, err := dispatcher.Dispatch(ctx, dest)
|
||||
@ -148,6 +148,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
|
||||
if err := buf.Copy(ray.InboundOutput(), v2writer, buf.UpdateActivity(timer)); err != nil {
|
||||
return err
|
||||
}
|
||||
timer.SetTimeout(time.Second * 2)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -91,7 +91,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
|
||||
request.Option |= RequestOptionOneTimeAuth
|
||||
}
|
||||
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, time.Minute*2)
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, time.Minute*5)
|
||||
|
||||
if request.Command == protocol.RequestCommandTCP {
|
||||
bufferedWriter := buf.NewBufferedWriter(conn)
|
||||
|
@ -70,7 +70,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
|
||||
func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
|
||||
udpServer := udp.NewDispatcher(dispatcher)
|
||||
|
||||
reader := buf.NewReader(conn)
|
||||
@ -81,7 +81,7 @@ func (v *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
}
|
||||
|
||||
for _, payload := range mpayload {
|
||||
request, data, err := DecodeUDPPacket(v.user, payload)
|
||||
request, data, err := DecodeUDPPacket(s.user, payload)
|
||||
if err != nil {
|
||||
if source, ok := proxy.SourceFromContext(ctx); ok {
|
||||
log.Trace(newError("dropping invalid UDP packet from: ", source).Base(err))
|
||||
@ -91,13 +91,13 @@ func (v *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
continue
|
||||
}
|
||||
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Disabled {
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) && s.account.OneTimeAuth == Account_Disabled {
|
||||
log.Trace(newError("client payload enables OTA but server doesn't allow it"))
|
||||
payload.Release()
|
||||
continue
|
||||
}
|
||||
|
||||
if !request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Enabled {
|
||||
if !request.Option.Has(RequestOptionOneTimeAuth) && s.account.OneTimeAuth == Account_Enabled {
|
||||
log.Trace(newError("client payload disables OTA but server forces it"))
|
||||
payload.Release()
|
||||
continue
|
||||
@ -177,6 +177,8 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
|
||||
return newError("failed to transport all TCP response").Base(err)
|
||||
}
|
||||
|
||||
timer.SetTimeout(time.Second * 2)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -84,7 +84,7 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy.
|
||||
return newError("failed to establish connection to server").AtWarning().Base(err)
|
||||
}
|
||||
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, time.Minute*2)
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, time.Minute*5)
|
||||
|
||||
var requestFunc func() error
|
||||
var responseFunc func() error
|
||||
|
@ -244,7 +244,7 @@ func writeSocks5AuthenticationResponse(writer io.Writer, version byte, auth byte
|
||||
return err
|
||||
}
|
||||
|
||||
func appendAddress(buffer *buf.Buffer, address net.Address, port net.Port) {
|
||||
func appendAddress(buffer *buf.Buffer, address net.Address, port net.Port) error {
|
||||
switch address.Family() {
|
||||
case net.AddressFamilyIPv4:
|
||||
buffer.AppendBytes(0x01)
|
||||
@ -253,16 +253,23 @@ func appendAddress(buffer *buf.Buffer, address net.Address, port net.Port) {
|
||||
buffer.AppendBytes(0x04)
|
||||
buffer.Append(address.IP())
|
||||
case net.AddressFamilyDomain:
|
||||
n := byte(len(address.Domain()))
|
||||
if int(n) != len(address.Domain()) {
|
||||
return newError("Super long domain is not supported in Socks protocol. ", address.Domain())
|
||||
}
|
||||
buffer.AppendBytes(0x03, byte(len(address.Domain())))
|
||||
buffer.AppendSupplier(serial.WriteString(address.Domain()))
|
||||
}
|
||||
buffer.AppendSupplier(serial.WriteUint16(port.Value()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeSocks5Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error {
|
||||
buffer := buf.NewLocal(64)
|
||||
buffer.AppendBytes(socks5Version, errCode, 0x00 /* reserved */)
|
||||
appendAddress(buffer, address, port)
|
||||
if err := appendAddress(buffer, address, port); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := writer.Write(buffer.Bytes())
|
||||
return err
|
||||
@ -327,12 +334,14 @@ func DecodeUDPPacket(packet []byte) (*protocol.RequestHeader, []byte, error) {
|
||||
return request, packet[dataBegin:], nil
|
||||
}
|
||||
|
||||
func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) *buf.Buffer {
|
||||
func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer, error) {
|
||||
b := buf.New()
|
||||
b.AppendBytes(0, 0, 0 /* Fragment */)
|
||||
appendAddress(b, request.Address, request.Port)
|
||||
if err := appendAddress(b, request.Address, request.Port); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Append(data)
|
||||
return b
|
||||
return b, nil
|
||||
}
|
||||
|
||||
type UDPReader struct {
|
||||
@ -371,7 +380,10 @@ func NewUDPWriter(request *protocol.RequestHeader, writer io.Writer) *UDPWriter
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (w *UDPWriter) Write(b []byte) (int, error) {
|
||||
eb := EncodeUDPPacket(w.request, b)
|
||||
eb, err := EncodeUDPPacket(w.request, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer eb.Release()
|
||||
if _, err := w.writer.Write(eb.Bytes()); err != nil {
|
||||
return 0, err
|
||||
|
@ -108,7 +108,7 @@ func (*Server) handleUDP() error {
|
||||
func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher dispatcher.Interface) error {
|
||||
timeout := time.Second * time.Duration(v.config.Timeout)
|
||||
if timeout == 0 {
|
||||
timeout = time.Minute * 2
|
||||
timeout = time.Minute * 5
|
||||
}
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, timeout)
|
||||
|
||||
@ -135,6 +135,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
|
||||
if err := buf.Copy(output, v2writer, buf.UpdateActivity(timer)); err != nil {
|
||||
return newError("failed to transport all TCP response").Base(err)
|
||||
}
|
||||
timer.SetTimeout(time.Second * 2)
|
||||
return nil
|
||||
})
|
||||
|
||||
@ -187,8 +188,11 @@ func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection,
|
||||
|
||||
log.Trace(newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug())
|
||||
|
||||
udpMessage := EncodeUDPPacket(request, payload.Bytes())
|
||||
udpMessage, err := EncodeUDPPacket(request, payload.Bytes())
|
||||
defer udpMessage.Release()
|
||||
if err != nil {
|
||||
log.Trace(newError("failed to write UDP response").AtWarning().Base(err))
|
||||
}
|
||||
|
||||
conn.Write(udpMessage.Bytes())
|
||||
})
|
||||
|
@ -13,24 +13,24 @@ type InternalAccount struct {
|
||||
Security protocol.Security
|
||||
}
|
||||
|
||||
func (v *InternalAccount) AnyValidID() *protocol.ID {
|
||||
if len(v.AlterIDs) == 0 {
|
||||
return v.ID
|
||||
func (a *InternalAccount) AnyValidID() *protocol.ID {
|
||||
if len(a.AlterIDs) == 0 {
|
||||
return a.ID
|
||||
}
|
||||
return v.AlterIDs[dice.Roll(len(v.AlterIDs))]
|
||||
return a.AlterIDs[dice.Roll(len(a.AlterIDs))]
|
||||
}
|
||||
|
||||
func (v *InternalAccount) Equals(account protocol.Account) bool {
|
||||
func (a *InternalAccount) Equals(account protocol.Account) bool {
|
||||
vmessAccount, ok := account.(*InternalAccount)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// TODO: handle AlterIds difference
|
||||
return v.ID.Equals(vmessAccount.ID)
|
||||
return a.ID.Equals(vmessAccount.ID)
|
||||
}
|
||||
|
||||
func (v *Account) AsAccount() (protocol.Account, error) {
|
||||
id, err := uuid.ParseString(v.Id)
|
||||
func (a *Account) AsAccount() (protocol.Account, error) {
|
||||
id, err := uuid.ParseString(a.Id)
|
||||
if err != nil {
|
||||
log.Trace(newError("failed to parse ID").Base(err).AtError())
|
||||
return nil, err
|
||||
@ -38,7 +38,7 @@ func (v *Account) AsAccount() (protocol.Account, error) {
|
||||
protoID := protocol.NewID(id)
|
||||
return &InternalAccount{
|
||||
ID: protoID,
|
||||
AlterIDs: protocol.NewAlterIDs(protoID, uint16(v.AlterId)),
|
||||
Security: v.SecuritySettings.AsSecurity(),
|
||||
AlterIDs: protocol.NewAlterIDs(protoID, uint16(a.AlterId)),
|
||||
Security: a.SecuritySettings.AsSecurity(),
|
||||
}, nil
|
||||
}
|
||||
|
@ -4,15 +4,16 @@ import (
|
||||
"crypto/md5"
|
||||
"hash/fnv"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/serial"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Authenticate authenticates a byte array using Fnv hash.
|
||||
func Authenticate(b []byte) uint32 {
|
||||
fnv1hash := fnv.New32a()
|
||||
fnv1hash.Write(b)
|
||||
common.Must2(fnv1hash.Write(b))
|
||||
return fnv1hash.Sum32()
|
||||
}
|
||||
|
||||
@ -81,7 +82,7 @@ type ShakeSizeParser struct {
|
||||
|
||||
func NewShakeSizeParser(nonce []byte) *ShakeSizeParser {
|
||||
shake := sha3.NewShake128()
|
||||
shake.Write(nonce)
|
||||
common.Must2(shake.Write(nonce))
|
||||
return &ShakeSizeParser{
|
||||
shake: shake,
|
||||
}
|
||||
@ -92,7 +93,7 @@ func (*ShakeSizeParser) SizeBytes() int {
|
||||
}
|
||||
|
||||
func (s *ShakeSizeParser) next() uint16 {
|
||||
s.shake.Read(s.buffer[:])
|
||||
common.Must2(s.shake.Read(s.buffer[:]))
|
||||
return serial.BytesToUint16(s.buffer[:])
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
|
||||
"v2ray.com/core/app/log"
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/crypto"
|
||||
"v2ray.com/core/common/dice"
|
||||
@ -43,7 +44,7 @@ type ClientSession struct {
|
||||
// NewClientSession creates a new ClientSession.
|
||||
func NewClientSession(idHash protocol.IDHash) *ClientSession {
|
||||
randomBytes := make([]byte, 33) // 16 + 16 + 1
|
||||
rand.Read(randomBytes)
|
||||
common.Must2(rand.Read(randomBytes))
|
||||
|
||||
session := &ClientSession{}
|
||||
session.requestBodyKey = randomBytes[:16]
|
||||
@ -58,22 +59,22 @@ func NewClientSession(idHash protocol.IDHash) *ClientSession {
|
||||
return session
|
||||
}
|
||||
|
||||
func (v *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) {
|
||||
func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) {
|
||||
timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)()
|
||||
account, err := header.User.GetTypedAccount()
|
||||
if err != nil {
|
||||
log.Trace(newError("failed to get user account: ", err).AtError())
|
||||
return
|
||||
}
|
||||
idHash := v.idHash(account.(*vmess.InternalAccount).AnyValidID().Bytes())
|
||||
idHash.Write(timestamp.Bytes(nil))
|
||||
writer.Write(idHash.Sum(nil))
|
||||
idHash := c.idHash(account.(*vmess.InternalAccount).AnyValidID().Bytes())
|
||||
common.Must2(idHash.Write(timestamp.Bytes(nil)))
|
||||
common.Must2(writer.Write(idHash.Sum(nil)))
|
||||
|
||||
buffer := make([]byte, 0, 512)
|
||||
buffer = append(buffer, Version)
|
||||
buffer = append(buffer, v.requestBodyIV...)
|
||||
buffer = append(buffer, v.requestBodyKey...)
|
||||
buffer = append(buffer, v.responseHeader, byte(header.Option))
|
||||
buffer = append(buffer, c.requestBodyIV...)
|
||||
buffer = append(buffer, c.requestBodyKey...)
|
||||
buffer = append(buffer, c.responseHeader, byte(header.Option))
|
||||
padingLen := dice.Roll(16)
|
||||
if header.Security.Is(protocol.SecurityType_LEGACY) {
|
||||
// Disable padding in legacy mode for a smooth transition.
|
||||
@ -100,29 +101,27 @@ func (v *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ
|
||||
|
||||
if padingLen > 0 {
|
||||
pading := make([]byte, padingLen)
|
||||
rand.Read(pading)
|
||||
common.Must2(rand.Read(pading))
|
||||
buffer = append(buffer, pading...)
|
||||
}
|
||||
|
||||
fnv1a := fnv.New32a()
|
||||
fnv1a.Write(buffer)
|
||||
common.Must2(fnv1a.Write(buffer))
|
||||
|
||||
buffer = fnv1a.Sum(buffer)
|
||||
|
||||
timestampHash := md5.New()
|
||||
timestampHash.Write(hashTimestamp(timestamp))
|
||||
common.Must2(timestampHash.Write(hashTimestamp(timestamp)))
|
||||
iv := timestampHash.Sum(nil)
|
||||
aesStream := crypto.NewAesEncryptionStream(account.(*vmess.InternalAccount).ID.CmdKey(), iv)
|
||||
aesStream.XORKeyStream(buffer, buffer)
|
||||
writer.Write(buffer)
|
||||
|
||||
return
|
||||
common.Must2(writer.Write(buffer))
|
||||
}
|
||||
|
||||
func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
|
||||
func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
|
||||
var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{}
|
||||
if request.Option.Has(protocol.RequestOptionChunkMasking) {
|
||||
sizeParser = NewShakeSizeParser(v.requestBodyIV)
|
||||
sizeParser = NewShakeSizeParser(c.requestBodyIV)
|
||||
}
|
||||
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
@ -141,7 +140,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
|
||||
}
|
||||
|
||||
if request.Security.Is(protocol.SecurityType_LEGACY) {
|
||||
aesStream := crypto.NewAesEncryptionStream(v.requestBodyKey, v.requestBodyIV)
|
||||
aesStream := crypto.NewAesEncryptionStream(c.requestBodyKey, c.requestBodyIV)
|
||||
cryptionWriter := crypto.NewCryptionWriter(aesStream, writer)
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
@ -156,13 +155,13 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
|
||||
}
|
||||
|
||||
if request.Security.Is(protocol.SecurityType_AES128_GCM) {
|
||||
block, _ := aes.NewCipher(v.requestBodyKey)
|
||||
block, _ := aes.NewCipher(c.requestBodyKey)
|
||||
aead, _ := cipher.NewGCM(block)
|
||||
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
AEAD: aead,
|
||||
NonceGenerator: &ChunkNonceGenerator{
|
||||
Nonce: append([]byte(nil), v.requestBodyIV...),
|
||||
Nonce: append([]byte(nil), c.requestBodyIV...),
|
||||
Size: aead.NonceSize(),
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
@ -171,12 +170,12 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
|
||||
}
|
||||
|
||||
if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.requestBodyKey))
|
||||
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey))
|
||||
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
AEAD: aead,
|
||||
NonceGenerator: &ChunkNonceGenerator{
|
||||
Nonce: append([]byte(nil), v.requestBodyIV...),
|
||||
Nonce: append([]byte(nil), c.requestBodyIV...),
|
||||
Size: aead.NonceSize(),
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
@ -299,8 +298,8 @@ type ChunkNonceGenerator struct {
|
||||
count uint16
|
||||
}
|
||||
|
||||
func (v *ChunkNonceGenerator) Next() []byte {
|
||||
serial.Uint16ToBytes(v.count, v.Nonce[:0])
|
||||
v.count++
|
||||
return v.Nonce[:v.Size]
|
||||
func (g *ChunkNonceGenerator) Next() []byte {
|
||||
serial.Uint16ToBytes(g.count, g.Nonce[:0])
|
||||
g.count++
|
||||
return g.Nonce[:g.Size]
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package encoding
|
||||
import (
|
||||
"io"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/net"
|
||||
"v2ray.com/core/common/protocol"
|
||||
@ -45,8 +46,8 @@ func MarshalCommand(command interface{}, writer io.Writer) error {
|
||||
return ErrCommandTooLarge
|
||||
}
|
||||
|
||||
writer.Write([]byte{cmdID, byte(len), byte(auth >> 24), byte(auth >> 16), byte(auth >> 8), byte(auth)})
|
||||
writer.Write(buffer.Bytes())
|
||||
common.Must2(writer.Write([]byte{cmdID, byte(len), byte(auth >> 24), byte(auth >> 16), byte(auth >> 8), byte(auth)}))
|
||||
common.Must2(writer.Write(buffer.Bytes()))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -78,7 +79,7 @@ type CommandFactory interface {
|
||||
type CommandSwitchAccountFactory struct {
|
||||
}
|
||||
|
||||
func (v *CommandSwitchAccountFactory) Marshal(command interface{}, writer io.Writer) error {
|
||||
func (f *CommandSwitchAccountFactory) Marshal(command interface{}, writer io.Writer) error {
|
||||
cmd, ok := command.(*protocol.CommandSwitchAccount)
|
||||
if !ok {
|
||||
return ErrCommandTypeMismatch
|
||||
@ -88,25 +89,25 @@ func (v *CommandSwitchAccountFactory) Marshal(command interface{}, writer io.Wri
|
||||
if cmd.Host != nil {
|
||||
hostStr = cmd.Host.String()
|
||||
}
|
||||
writer.Write([]byte{byte(len(hostStr))})
|
||||
common.Must2(writer.Write([]byte{byte(len(hostStr))}))
|
||||
|
||||
if len(hostStr) > 0 {
|
||||
writer.Write([]byte(hostStr))
|
||||
common.Must2(writer.Write([]byte(hostStr)))
|
||||
}
|
||||
|
||||
writer.Write(cmd.Port.Bytes(nil))
|
||||
common.Must2(writer.Write(cmd.Port.Bytes(nil)))
|
||||
|
||||
idBytes := cmd.ID.Bytes()
|
||||
writer.Write(idBytes)
|
||||
common.Must2(writer.Write(idBytes))
|
||||
|
||||
writer.Write(serial.Uint16ToBytes(cmd.AlterIds, nil))
|
||||
writer.Write([]byte{byte(cmd.Level)})
|
||||
common.Must2(writer.Write(serial.Uint16ToBytes(cmd.AlterIds, nil)))
|
||||
common.Must2(writer.Write([]byte{byte(cmd.Level)}))
|
||||
|
||||
writer.Write([]byte{cmd.ValidMin})
|
||||
common.Must2(writer.Write([]byte{cmd.ValidMin}))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *CommandSwitchAccountFactory) Unmarshal(data []byte) (interface{}, error) {
|
||||
func (f *CommandSwitchAccountFactory) Unmarshal(data []byte) (interface{}, error) {
|
||||
cmd := new(protocol.CommandSwitchAccount)
|
||||
if len(data) == 0 {
|
||||
return nil, newError("insufficient length.")
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/crypto"
|
||||
"v2ray.com/core/common/net"
|
||||
@ -126,7 +127,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
||||
}
|
||||
|
||||
timestampHash := md5.New()
|
||||
timestampHash.Write(hashTimestamp(timestamp))
|
||||
common.Must2(timestampHash.Write(hashTimestamp(timestamp)))
|
||||
iv := timestampHash.Sum(nil)
|
||||
account, err := user.GetTypedAccount()
|
||||
if err != nil {
|
||||
@ -220,7 +221,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
||||
}
|
||||
|
||||
fnv1a := fnv.New32a()
|
||||
fnv1a.Write(buffer[:bufferLen])
|
||||
common.Must2(fnv1a.Write(buffer[:bufferLen]))
|
||||
actualHash := fnv1a.Sum32()
|
||||
expectedHash := serial.BytesToUint32(buffer[bufferLen : bufferLen+4])
|
||||
|
||||
@ -314,10 +315,10 @@ func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr
|
||||
encryptionWriter := crypto.NewCryptionWriter(aesStream, writer)
|
||||
s.responseWriter = encryptionWriter
|
||||
|
||||
encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)})
|
||||
common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)}))
|
||||
err := MarshalCommand(header.Command, encryptionWriter)
|
||||
if err != nil {
|
||||
encryptionWriter.Write([]byte{0x00, 0x00})
|
||||
common.Must2(encryptionWriter.Write([]byte{0x00, 0x00}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,17 +127,17 @@ func (v *Handler) GetUser(email string) *protocol.User {
|
||||
return user
|
||||
}
|
||||
|
||||
func transferRequest(timer signal.ActivityTimer, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error {
|
||||
func transferRequest(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error {
|
||||
defer output.Close()
|
||||
|
||||
bodyReader := session.DecodeRequestBody(request, input)
|
||||
if err := buf.Copy(bodyReader, output, buf.UpdateActivity(timer)); err != nil {
|
||||
return err
|
||||
return newError("failed to transfer request").Base(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func transferResponse(timer signal.ActivityTimer, session *encoding.ServerSession, request *protocol.RequestHeader, response *protocol.ResponseHeader, input buf.Reader, output io.Writer) error {
|
||||
func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, response *protocol.ResponseHeader, input buf.Reader, output io.Writer) error {
|
||||
session.EncodeResponseHeader(response, output)
|
||||
|
||||
bodyWriter := session.EncodeResponseBody(request, output)
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"v2ray.com/core/proxy/vmess"
|
||||
)
|
||||
|
||||
func (v *Handler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) {
|
||||
func (h *Handler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) {
|
||||
account := &vmess.Account{
|
||||
Id: cmd.ID.String(),
|
||||
AlterId: uint32(cmd.AlterIds),
|
||||
@ -25,16 +25,16 @@ func (v *Handler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) {
|
||||
}
|
||||
dest := net.TCPDestination(cmd.Host, cmd.Port)
|
||||
until := time.Now().Add(time.Duration(cmd.ValidMin) * time.Minute)
|
||||
v.serverList.AddServer(protocol.NewServerSpec(dest, protocol.BeforeTime(until), user))
|
||||
h.serverList.AddServer(protocol.NewServerSpec(dest, protocol.BeforeTime(until), user))
|
||||
}
|
||||
|
||||
func (v *Handler) handleCommand(dest net.Destination, cmd protocol.ResponseCommand) {
|
||||
func (h *Handler) handleCommand(dest net.Destination, cmd protocol.ResponseCommand) {
|
||||
switch typedCommand := cmd.(type) {
|
||||
case *protocol.CommandSwitchAccount:
|
||||
if typedCommand.Host == nil {
|
||||
typedCommand.Host = dest.Address
|
||||
}
|
||||
v.handleSwitchAccount(typedCommand)
|
||||
h.handleSwitchAccount(typedCommand)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
|
||||
|
||||
session := encoding.NewClientSession(protocol.DefaultIDHash)
|
||||
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, time.Minute*2)
|
||||
ctx, timer := signal.CancelAfterInactivity(ctx, time.Minute*5)
|
||||
|
||||
requestDone := signal.ExecuteAsync(func() error {
|
||||
writer := buf.NewBufferedWriter(conn)
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/protocol"
|
||||
)
|
||||
|
||||
@ -60,11 +61,11 @@ func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, idx in
|
||||
var hashValueRemoval [16]byte
|
||||
idHash := v.hasher(entry.id.Bytes())
|
||||
for entry.lastSec <= nowSec {
|
||||
idHash.Write(entry.lastSec.Bytes(nil))
|
||||
common.Must2(idHash.Write(entry.lastSec.Bytes(nil)))
|
||||
idHash.Sum(hashValue[:0])
|
||||
idHash.Reset()
|
||||
|
||||
idHash.Write(entry.lastSecRemoval.Bytes(nil))
|
||||
common.Must2(idHash.Write(entry.lastSecRemoval.Bytes(nil)))
|
||||
idHash.Sum(hashValueRemoval[:0])
|
||||
idHash.Reset()
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -214,7 +214,7 @@ func NewConnection(conv uint16, sysConn SystemConnection, config *Config) *Conne
|
||||
dataInput: make(chan bool, 1),
|
||||
dataOutput: make(chan bool, 1),
|
||||
Config: config,
|
||||
output: NewSegmentWriter(sysConn),
|
||||
output: NewRetryableWriter(NewSegmentWriter(sysConn)),
|
||||
mss: config.GetMTUValue() - uint32(sysConn.Overhead()) - DataSegmentOverhead,
|
||||
roundTrip: &RoundTripInfo{
|
||||
rto: 100,
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"crypto/cipher"
|
||||
"hash/fnv"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/serial"
|
||||
)
|
||||
|
||||
@ -32,7 +33,7 @@ func (a *SimpleAuthenticator) Seal(dst, nonce, plain, extra []byte) []byte {
|
||||
dst = append(dst, plain...)
|
||||
|
||||
fnvHash := fnv.New32a()
|
||||
fnvHash.Write(dst[4:])
|
||||
common.Must2(fnvHash.Write(dst[4:]))
|
||||
fnvHash.Sum(dst[:0])
|
||||
|
||||
len := len(dst)
|
||||
@ -61,7 +62,7 @@ func (a *SimpleAuthenticator) Open(dst, nonce, cipherText, extra []byte) ([]byte
|
||||
}
|
||||
|
||||
fnvHash := fnv.New32a()
|
||||
fnvHash.Write(dst[4:])
|
||||
common.Must2(fnvHash.Write(dst[4:]))
|
||||
if serial.BytesToUint32(dst[:4]) != fnvHash.Sum32() {
|
||||
return nil, newError("invalid auth")
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"v2ray.com/core/common/retry"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
)
|
||||
|
||||
@ -24,11 +27,27 @@ func NewSegmentWriter(writer io.Writer) SegmentWriter {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *SimpleSegmentWriter) Write(seg Segment) error {
|
||||
v.Lock()
|
||||
defer v.Unlock()
|
||||
func (w *SimpleSegmentWriter) Write(seg Segment) error {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
|
||||
v.buffer.Reset(seg.Bytes())
|
||||
_, err := v.writer.Write(v.buffer.Bytes())
|
||||
common.Must(w.buffer.Reset(seg.Bytes()))
|
||||
_, err := w.writer.Write(w.buffer.Bytes())
|
||||
return err
|
||||
}
|
||||
|
||||
type RetryableWriter struct {
|
||||
writer SegmentWriter
|
||||
}
|
||||
|
||||
func NewRetryableWriter(writer SegmentWriter) SegmentWriter {
|
||||
return &RetryableWriter{
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *RetryableWriter) Write(seg Segment) error {
|
||||
return retry.Timed(5, 100).On(func() error {
|
||||
return w.writer.Write(seg)
|
||||
})
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ func Dial(ctx context.Context, dest net.Destination) (internet.Connection, error
|
||||
|
||||
conn, err := dialWebsocket(ctx, dest)
|
||||
if err != nil {
|
||||
return nil, newError("failed to dial WebSocket")
|
||||
return nil, newError("failed to dial WebSocket").Base(err)
|
||||
}
|
||||
return internet.Connection(conn), nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user