Last active
July 9, 2022 06:59
-
-
Save liberal-boy/04f875b86a5e54cb4e1752d24077f2be to your computer and use it in GitHub Desktop.
Vmess Fail Redirect 简单实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"log": { | |
"access": "", | |
"error": "", | |
"loglevel": "warning" | |
}, | |
"inbounds": [ | |
{ | |
"port": 1080, | |
"listen": "127.0.0.1", | |
"protocol": "socks" | |
} | |
], | |
"outbounds": [ | |
{ | |
"protocol": "vmess", | |
"settings": { | |
"vnext": [ | |
{ | |
"address": "example.com", | |
"port": 443, | |
"users": [ | |
{ | |
"id": "f2435e5c-9ad9-4367-836a-8341117d0a5f", | |
"security": "none" | |
} | |
] | |
} | |
] | |
}, | |
"streamSettings": { | |
"network": "tcp", | |
"security": "tls" | |
} | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"log": { | |
"access": "", | |
"error": "", | |
"loglevel": "warning" | |
}, | |
"inbounds": [ | |
{ | |
"protocol": "vmess", | |
"port": 443, | |
"settings": { | |
"clients": [ | |
{ | |
"id": "f2435e5c-9ad9-4367-836a-8341117d0a5f" | |
} | |
] | |
}, | |
"streamSettings": { | |
"network": "tcp", | |
"security": "tls", | |
"tlsSettings": { | |
"certificates": [ | |
{ | |
"certificateFile": "/etc/ssl/example.com.pem", | |
"keyFile": "/etc/ssl/example.com.key" | |
} | |
], | |
"alpn": ["http/2"] | |
} | |
} | |
} | |
], | |
"outbounds": [ | |
{ | |
"protocol": "freedom" | |
}, | |
{ | |
"protocol": "freedom", | |
"settings": { | |
"redirect": "127.0.0.1:8080" | |
}, | |
"tag": "web" | |
} | |
], | |
"routing": { | |
"strategy": "rules", | |
"settings": { | |
"domainStrategy": "AsIs", | |
"rules": [ | |
{ | |
"type": "field", | |
"domain": [ | |
"full:failredirect.local" | |
], | |
"outboundTag": "web" | |
} | |
] | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: proxy/vmess/encoding/server.go | |
IDEA additional info: | |
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
<+>UTF-8 | |
=================================================================== | |
--- proxy/vmess/encoding/server.go (revision 4f059a1c521a6615de73805da13d03c1c5493c2e) | |
+++ proxy/vmess/encoding/server.go (revision 47ea9c4d96a2eb36f0bab65fec6eeb7562c2a959) | |
@@ -121,17 +121,17 @@ | |
} | |
// DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream. | |
-func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error) { | |
+func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error, []byte) { | |
buffer := buf.New() | |
defer buffer.Release() | |
if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil { | |
- return nil, newError("failed to read request header").Base(err) | |
+ return nil, newError("failed to read request header").Base(err), buffer.Bytes() | |
} | |
user, timestamp, valid := s.userValidator.Get(buffer.Bytes()) | |
if !valid { | |
- return nil, newError("invalid user") | |
+ return nil, newError("invalid user"), buffer.Bytes() | |
} | |
iv := hashTimestamp(md5.New(), timestamp) | |
@@ -142,7 +142,7 @@ | |
buffer.Clear() | |
if _, err := buffer.ReadFullFrom(decryptor, 38); err != nil { | |
- return nil, newError("failed to read request header").Base(err) | |
+ return nil, newError("failed to read request header").Base(err), nil | |
} | |
request := &protocol.RequestHeader{ | |
@@ -157,7 +157,7 @@ | |
sid.key = s.requestBodyKey | |
sid.nonce = s.requestBodyIV | |
if !s.sessionHistory.addIfNotExits(sid) { | |
- return nil, newError("duplicated session id, possibly under replay attack") | |
+ return nil, newError("duplicated session id, possibly under replay attack"), nil | |
} | |
s.responseHeader = buffer.Byte(33) // 1 byte | |
@@ -180,12 +180,12 @@ | |
if padingLen > 0 { | |
if _, err := buffer.ReadFullFrom(decryptor, int32(padingLen)); err != nil { | |
- return nil, newError("failed to read padding").Base(err) | |
+ return nil, newError("failed to read padding").Base(err), nil | |
} | |
} | |
if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil { | |
- return nil, newError("failed to read checksum").Base(err) | |
+ return nil, newError("failed to read checksum").Base(err), nil | |
} | |
fnv1a := fnv.New32a() | |
@@ -194,18 +194,18 @@ | |
expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4)) | |
if actualHash != expectedHash { | |
- return nil, newError("invalid auth") | |
+ return nil, newError("invalid auth"), nil | |
} | |
if request.Address == nil { | |
- return nil, newError("invalid remote address") | |
+ return nil, newError("invalid remote address"), nil | |
} | |
if request.Security == protocol.SecurityType_UNKNOWN || request.Security == protocol.SecurityType_AUTO { | |
- return nil, newError("unknown security type: ", request.Security) | |
+ return nil, newError("unknown security type: ", request.Security), nil | |
} | |
- return request, nil | |
+ return request, nil, nil | |
} | |
// DecodeRequestBody returns Reader from which caller can fetch decrypted body. | |
Index: proxy/vmess/inbound/inbound.go | |
IDEA additional info: | |
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
<+>UTF-8 | |
=================================================================== | |
--- proxy/vmess/inbound/inbound.go (revision 4f059a1c521a6615de73805da13d03c1c5493c2e) | |
+++ proxy/vmess/inbound/inbound.go (revision 47ea9c4d96a2eb36f0bab65fec6eeb7562c2a959) | |
@@ -10,7 +10,6 @@ | |
"strings" | |
"sync" | |
"time" | |
- | |
"v2ray.com/core" | |
"v2ray.com/core/common" | |
"v2ray.com/core/common/buf" | |
@@ -215,6 +214,79 @@ | |
return s == protocol.SecurityType_NONE || s == protocol.SecurityType_LEGACY || s == protocol.SecurityType_UNKNOWN | |
} | |
+func (h *Handler) transferBody( | |
+ ctx context.Context, | |
+ dispatcher routing.Dispatcher, | |
+ sessionPolicy policy.Session, | |
+ dest net.Destination, | |
+ bodyReader buf.Reader, | |
+ connection internet.Connection, | |
+ preLoad []byte, | |
+) error { | |
+ | |
+ ctx, cancel := context.WithCancel(ctx) | |
+ timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) | |
+ | |
+ ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) | |
+ link, err := dispatcher.Dispatch(ctx, dest) | |
+ if err != nil { | |
+ return newError("failed to dispatch request to ", dest).Base(err) | |
+ } | |
+ | |
+ requestDone := func() error { | |
+ defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) | |
+ | |
+ if preLoad != nil && len(preLoad) > 0 { | |
+ buffer := buf.New() | |
+ buffer.Write(preLoad) | |
+ if err := link.Writer.WriteMultiBuffer(buf.MultiBuffer{buffer}); err != nil { | |
+ return newError("failed to transfer request").Base(err) | |
+ } | |
+ } | |
+ | |
+ if err := buf.Copy(bodyReader, link.Writer, buf.UpdateActivity(timer)); err != nil { | |
+ return newError("failed to transfer request").Base(err) | |
+ } | |
+ return nil | |
+ } | |
+ | |
+ responseDone := func() error { | |
+ defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) | |
+ | |
+ writer := buf.NewBufferedWriter(buf.NewWriter(connection)) | |
+ defer writer.Flush() | |
+ | |
+ { | |
+ // Optimize for small response packet | |
+ data, err := link.Reader.ReadMultiBuffer() | |
+ if err != nil { | |
+ return err | |
+ } | |
+ | |
+ if err := writer.WriteMultiBuffer(data); err != nil { | |
+ return err | |
+ } | |
+ } | |
+ if err := writer.SetBuffered(false); err != nil { | |
+ return err | |
+ } | |
+ | |
+ if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil { | |
+ return newError("failed to transfer response").Base(err) | |
+ } | |
+ return nil | |
+ } | |
+ | |
+ var requestDonePost = task.OnSuccess(requestDone, task.Close(link.Writer)) | |
+ if err := task.Run(ctx, requestDonePost, responseDone); err != nil { | |
+ common.Interrupt(link.Reader) | |
+ common.Interrupt(link.Writer) | |
+ return newError("connection ends").Base(err) | |
+ } | |
+ | |
+ return nil | |
+} | |
+ | |
// Process implements proxy.Inbound.Process(). | |
func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher routing.Dispatcher) error { | |
sessionPolicy := h.policyManager.ForLevel(0) | |
@@ -224,8 +296,13 @@ | |
reader := &buf.BufferedReader{Reader: buf.NewReader(connection)} | |
svrSession := encoding.NewServerSession(h.clients, h.sessionHistory) | |
- request, err := svrSession.DecodeRequestHeader(reader) | |
+ request, err, preLoad := svrSession.DecodeRequestHeader(reader) | |
if err != nil { | |
+ if preLoad != nil { | |
+ dest := net.TCPDestination(net.DomainAddress("failredirect.local"), 0) | |
+ return h.transferBody(ctx, dispatcher, sessionPolicy, dest, reader, connection, preLoad) | |
+ } | |
+ | |
if errors.Cause(err) != io.EOF { | |
log.Record(&log.AccessMessage{ | |
From: connection.RemoteAddr(), |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment