Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lo outbound, allow you to redirect connection to the dispatcher again #770

Merged
merged 7 commits into from
Mar 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions infra/conf/loopback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package conf

import (
"github.com/golang/protobuf/proto"
"github.com/v2fly/v2ray-core/v4/proxy/loopback"
)

type LoopbackConfig struct {
InboundTag string `json:"inboundTag"`
}

func (l LoopbackConfig) Build() (proto.Message, error) {
return &loopback.Config{InboundTag: l.InboundTag}, nil
}
1 change: 1 addition & 0 deletions infra/conf/v2ray.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var (
"trojan": func() interface{} { return new(TrojanClientConfig) },
"mtproto": func() interface{} { return new(MTProtoClientConfig) },
"dns": func() interface{} { return new(DNSOutboundConfig) },
"loopback": func() interface{} { return new(LoopbackConfig) },
}, "protocol", "settings")

ctllog = log.New(os.Stderr, "v2ctl> ", 0)
Expand Down
3 changes: 3 additions & 0 deletions proxy/loopback/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package loopback

//go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen
155 changes: 155 additions & 0 deletions proxy/loopback/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions proxy/loopback/config.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
syntax = "proto3";

package v2ray.core.proxy.loopback;
option csharp_namespace = "V2Ray.Core.Proxy.Loopback";
option go_package = "github.com/v2fly/v2ray-core/v4/proxy/loopback";
option java_package = "com.v2ray.core.proxy.loopback";
option java_multiple_files = true;

message Config {
string inbound_tag = 1;
}
9 changes: 9 additions & 0 deletions proxy/loopback/errors.generated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package loopback

import "github.com/v2fly/v2ray-core/v4/common/errors"

type errPathObjHolder struct{}

func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}
122 changes: 122 additions & 0 deletions proxy/loopback/lookback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// +build !confonly

package loopback

import (
"context"

core "github.com/v2fly/v2ray-core/v4"
"github.com/v2fly/v2ray-core/v4/common"
"github.com/v2fly/v2ray-core/v4/common/buf"
"github.com/v2fly/v2ray-core/v4/common/net"
"github.com/v2fly/v2ray-core/v4/common/retry"
"github.com/v2fly/v2ray-core/v4/common/session"
"github.com/v2fly/v2ray-core/v4/common/task"
"github.com/v2fly/v2ray-core/v4/features/routing"
"github.com/v2fly/v2ray-core/v4/transport"
"github.com/v2fly/v2ray-core/v4/transport/internet"
)

type Loopback struct {
config *Config
dispatcherInstance routing.Dispatcher
}

func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet.Dialer) error {
outbound := session.OutboundFromContext(ctx)
if outbound == nil || !outbound.Target.IsValid() {
return newError("target not specified.")
}
destination := outbound.Target

newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))

input := link.Reader
output := link.Writer

var conn internet.Connection
err := retry.ExponentialBackoff(5, 100).On(func() error {
dialDest := destination

content := new(session.Content)
content.SkipDNSResolve = true

ctx = session.ContextWithContent(ctx, content)

inbound := session.InboundFromContext(ctx)

inbound.Tag = l.config.InboundTag

ctx = session.ContextWithInbound(ctx, inbound)

rawConn, err := l.dispatcherInstance.Dispatch(ctx, dialDest)
if err != nil {
return err
}

var readerOpt net.ConnectionOption
if dialDest.Network == net.Network_TCP {
readerOpt = net.ConnectionOutputMulti(rawConn.Reader)
} else {
readerOpt = net.ConnectionOutputMultiUDP(rawConn.Reader)
}

conn = net.NewConnection(net.ConnectionInputMulti(rawConn.Writer), readerOpt)
return nil
})
if err != nil {
return newError("failed to open connection to ", destination).Base(err)
}
defer conn.Close()

requestDone := func() error {
var writer buf.Writer
if destination.Network == net.Network_TCP {
writer = buf.NewWriter(conn)
} else {
writer = &buf.SequentialWriter{Writer: conn}
}

if err := buf.Copy(input, writer); err != nil {
return newError("failed to process request").Base(err)
}

return nil
}

responseDone := func() error {
var reader buf.Reader
if destination.Network == net.Network_TCP {
reader = buf.NewReader(conn)
} else {
reader = buf.NewPacketReader(conn)
}
if err := buf.Copy(reader, output); err != nil {
return newError("failed to process response").Base(err)
}

return nil
}

if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
return newError("connection ends").Base(err)
}

return nil
}

func (l *Loopback) init(config *Config, dispatcherInstance routing.Dispatcher) error {
l.dispatcherInstance = dispatcherInstance
l.config = config
return nil
}

func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
l := new(Loopback)
err := core.RequireFeatures(ctx, func(dispatcherInstance routing.Dispatcher) error {
return l.init(config.(*Config), dispatcherInstance)
})
return l, err
}))
}