Skip to content

Commit d8db3be

Browse files
authored
VLESS protocol: Add Reverse Proxy (4) Command and extremely simple config (XTLS#5101)
XTLS#5088 (comment)
1 parent 7c651c1 commit d8db3be

14 files changed

Lines changed: 420 additions & 81 deletions

File tree

app/dispatcher/default.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
483483
handler = h
484484
} else {
485485
errors.LogWarning(ctx, "non existing outTag: ", outTag)
486+
return
486487
}
487488
} else {
488489
errors.LogInfo(ctx, "default route for ", destination)

app/proxyman/outbound/handler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
108108
}
109109
h.proxyConfig = proxyConfig
110110

111+
ctx = session.ContextWithHandler(ctx, h)
112+
111113
rawProxyHandler, err := common.CreateObject(ctx, proxyConfig)
112114
if err != nil {
113115
return nil, err

app/reverse/bridge.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ func (b *Bridge) Close() error {
9494
}
9595

9696
type BridgeWorker struct {
97-
tag string
98-
worker *mux.ServerWorker
99-
dispatcher routing.Dispatcher
100-
state Control_State
97+
Tag string
98+
Worker *mux.ServerWorker
99+
Dispatcher routing.Dispatcher
100+
State Control_State
101101
}
102102

103103
func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) {
@@ -115,15 +115,15 @@ func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWo
115115
}
116116

117117
w := &BridgeWorker{
118-
dispatcher: d,
119-
tag: tag,
118+
Dispatcher: d,
119+
Tag: tag,
120120
}
121121

122122
worker, err := mux.NewServerWorker(context.Background(), w, link)
123123
if err != nil {
124124
return nil, err
125125
}
126-
w.worker = worker
126+
w.Worker = worker
127127

128128
return w, nil
129129
}
@@ -141,11 +141,11 @@ func (w *BridgeWorker) Close() error {
141141
}
142142

143143
func (w *BridgeWorker) IsActive() bool {
144-
return w.state == Control_ACTIVE && !w.worker.Closed()
144+
return w.State == Control_ACTIVE && !w.Worker.Closed()
145145
}
146146

147147
func (w *BridgeWorker) Connections() uint32 {
148-
return w.worker.ActiveConnections()
148+
return w.Worker.ActiveConnections()
149149
}
150150

151151
func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
@@ -161,8 +161,8 @@ func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
161161
errors.LogInfoInner(context.Background(), err, "failed to parse proto message")
162162
break
163163
}
164-
if ctl.State != w.state {
165-
w.state = ctl.State
164+
if ctl.State != w.State {
165+
w.State = ctl.State
166166
}
167167
}
168168
}
@@ -171,9 +171,9 @@ func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
171171
func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
172172
if !isInternalDomain(dest) {
173173
ctx = session.ContextWithInbound(ctx, &session.Inbound{
174-
Tag: w.tag,
174+
Tag: w.Tag,
175175
})
176-
return w.dispatcher.Dispatch(ctx, dest)
176+
return w.Dispatcher.Dispatch(ctx, dest)
177177
}
178178

179179
opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)}
@@ -194,12 +194,12 @@ func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*tra
194194
func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, link *transport.Link) error {
195195
if !isInternalDomain(dest) {
196196
ctx = session.ContextWithInbound(ctx, &session.Inbound{
197-
Tag: w.tag,
197+
Tag: w.Tag,
198198
})
199-
return w.dispatcher.DispatchLink(ctx, dest, link)
199+
return w.Dispatcher.DispatchLink(ctx, dest, link)
200200
}
201201

202-
link = w.dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
202+
link = w.Dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
203203
w.handleInternalConn(link)
204204

205205
return nil

app/reverse/portal.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) err
9797
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
9898
}
9999

100-
101100
return p.client.Dispatch(ctx, link)
102101
}
103102

app/reverse/reverse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
)
1313

1414
const (
15-
internalDomain = "reverse.internal.v2fly.org" // make reverse proxy compatible with v2fly
15+
internalDomain = "reverse"
1616
)
1717

1818
func isDomain(dest net.Destination, domain string) bool {

common/protocol/headers.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ const (
1616
RequestCommandTCP = RequestCommand(0x01)
1717
RequestCommandUDP = RequestCommand(0x02)
1818
RequestCommandMux = RequestCommand(0x03)
19+
RequestCommandRvs = RequestCommand(0x04)
1920
)
2021

2122
func (c RequestCommand) TransferType() TransferType {
2223
switch c {
23-
case RequestCommandTCP, RequestCommandMux:
24+
case RequestCommandTCP, RequestCommandMux, RequestCommandRvs:
2425
return TransferTypeStream
2526
case RequestCommandUDP:
2627
return TransferTypePacket

common/session/context.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/xtls/xray-core/common/ctx"
88
"github.com/xtls/xray-core/common/net"
9+
"github.com/xtls/xray-core/features/outbound"
910
"github.com/xtls/xray-core/features/routing"
1011
)
1112

@@ -16,13 +17,13 @@ const (
1617
inboundSessionKey ctx.SessionKey = 1
1718
outboundSessionKey ctx.SessionKey = 2
1819
contentSessionKey ctx.SessionKey = 3
19-
muxPreferredSessionKey ctx.SessionKey = 4 // unused
20-
sockoptSessionKey ctx.SessionKey = 5 // used by dokodemo to only receive sockopt.Mark
21-
trackedConnectionErrorKey ctx.SessionKey = 6 // used by observer to get outbound error
22-
dispatcherKey ctx.SessionKey = 7 // used by ss2022 inbounds to get dispatcher
23-
timeoutOnlyKey ctx.SessionKey = 8 // mux context's child contexts to only cancel when its own traffic times out
24-
allowedNetworkKey ctx.SessionKey = 9 // muxcool server control incoming request tcp/udp
25-
handlerSessionKey ctx.SessionKey = 10 // unused
20+
muxPreferredSessionKey ctx.SessionKey = 4 // unused
21+
sockoptSessionKey ctx.SessionKey = 5 // used by dokodemo to only receive sockopt.Mark
22+
trackedConnectionErrorKey ctx.SessionKey = 6 // used by observer to get outbound error
23+
dispatcherKey ctx.SessionKey = 7 // used by ss2022 inbounds to get dispatcher
24+
timeoutOnlyKey ctx.SessionKey = 8 // mux context's child contexts to only cancel when its own traffic times out
25+
allowedNetworkKey ctx.SessionKey = 9 // muxcool server control incoming request tcp/udp
26+
handlerSessionKey ctx.SessionKey = 10 // outbound gets full handler
2627
mitmAlpn11Key ctx.SessionKey = 11 // used by TLS dialer
2728
mitmServerNameKey ctx.SessionKey = 12 // used by TLS dialer
2829
)
@@ -163,6 +164,17 @@ func AllowedNetworkFromContext(ctx context.Context) net.Network {
163164
return net.Network_Unknown
164165
}
165166

167+
func ContextWithHandler(ctx context.Context, handler outbound.Handler) context.Context {
168+
return context.WithValue(ctx, handlerSessionKey, handler)
169+
}
170+
171+
func HandlerFromContext(ctx context.Context) outbound.Handler {
172+
if val, ok := ctx.Value(handlerSessionKey).(outbound.Handler); ok {
173+
return val
174+
}
175+
return nil
176+
}
177+
166178
func ContextWithMitmAlpn11(ctx context.Context, alpn11 bool) context.Context {
167179
return context.WithValue(ctx, mitmAlpn11Key, alpn11)
168180
}

infra/conf/vless.go

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
7777
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
7878
}
7979

80+
if account.Reverse != nil && account.Reverse.Tag == "" {
81+
return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
82+
}
83+
8084
user.Account = serial.ToTypedMessage(account)
8185
config.Clients[idx] = user
8286
}
@@ -199,13 +203,30 @@ type VLessOutboundVnext struct {
199203
}
200204

201205
type VLessOutboundConfig struct {
202-
Vnext []*VLessOutboundVnext `json:"vnext"`
206+
Address *Address `json:"address"`
207+
Port uint16 `json:"port"`
208+
Level uint32 `json:"level"`
209+
Email string `json:"email"`
210+
Id string `json:"id"`
211+
Flow string `json:"flow"`
212+
Seed string `json:"seed"`
213+
Encryption string `json:"encryption"`
214+
Reverse *vless.Reverse `json:"reverse"`
215+
Vnext []*VLessOutboundVnext `json:"vnext"`
203216
}
204217

205218
// Build implements Buildable
206219
func (c *VLessOutboundConfig) Build() (proto.Message, error) {
207220
config := new(outbound.Config)
208-
221+
if c.Address != nil {
222+
c.Vnext = []*VLessOutboundVnext{
223+
{
224+
Address: c.Address,
225+
Port: c.Port,
226+
Users: []json.RawMessage{{}},
227+
},
228+
}
229+
}
209230
if len(c.Vnext) != 1 {
210231
return nil, errors.New(`VLESS settings: "vnext" should have one and only one member`)
211232
}
@@ -224,12 +245,25 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
224245
}
225246
for idx, rawUser := range rec.Users {
226247
user := new(protocol.User)
227-
if err := json.Unmarshal(rawUser, user); err != nil {
228-
return nil, errors.New(`VLESS users: invalid user`).Base(err)
248+
if c.Address != nil {
249+
user.Level = c.Level
250+
user.Email = c.Email
251+
} else {
252+
if err := json.Unmarshal(rawUser, user); err != nil {
253+
return nil, errors.New(`VLESS users: invalid user`).Base(err)
254+
}
229255
}
230256
account := new(vless.Account)
231-
if err := json.Unmarshal(rawUser, account); err != nil {
232-
return nil, errors.New(`VLESS users: invalid user`).Base(err)
257+
if c.Address != nil {
258+
account.Id = c.Id
259+
account.Flow = c.Flow
260+
//account.Seed = c.Seed
261+
account.Encryption = c.Encryption
262+
account.Reverse = c.Reverse
263+
} else {
264+
if err := json.Unmarshal(rawUser, account); err != nil {
265+
return nil, errors.New(`VLESS users: invalid user`).Base(err)
266+
}
233267
}
234268

235269
u, err := uuid.ParseString(account.Id)
@@ -288,6 +322,10 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
288322
return nil, errors.New(`VLESS users: unsupported "encryption": ` + account.Encryption)
289323
}
290324

325+
if account.Reverse != nil && account.Reverse.Tag == "" {
326+
return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
327+
}
328+
291329
user.Account = serial.ToTypedMessage(account)
292330
spec.User[idx] = user
293331
}

proxy/vless/account.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ func (a *Account) AsAccount() (protocol.Account, error) {
2121
XorMode: a.XorMode,
2222
Seconds: a.Seconds,
2323
Padding: a.Padding,
24+
Reverse: a.Reverse,
2425
}, nil
2526
}
2627

@@ -35,6 +36,8 @@ type MemoryAccount struct {
3536
XorMode uint32
3637
Seconds uint32
3738
Padding string
39+
40+
Reverse *Reverse
3841
}
3942

4043
// Equals implements protocol.Account.Equals().
@@ -54,5 +57,6 @@ func (a *MemoryAccount) ToProto() proto.Message {
5457
XorMode: a.XorMode,
5558
Seconds: a.Seconds,
5659
Padding: a.Padding,
60+
Reverse: a.Reverse,
5761
}
5862
}

0 commit comments

Comments
 (0)