Skip to content

Instantly share code, notes, and snippets.

@liberal-boy
Last active July 9, 2022 06:59
Show Gist options
  • Save liberal-boy/04f875b86a5e54cb4e1752d24077f2be to your computer and use it in GitHub Desktop.
Save liberal-boy/04f875b86a5e54cb4e1752d24077f2be to your computer and use it in GitHub Desktop.
Vmess Fail Redirect 简单实现
{
"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"
}
}
]
}
{
"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"
}
]
}
}
}
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