Skip to content

Commit

Permalink
add relay outbound
Browse files Browse the repository at this point in the history
  • Loading branch information
PuerNya committed Nov 3, 2023
1 parent fb8c45e commit 729dbe7
Show file tree
Hide file tree
Showing 22 changed files with 356 additions and 8 deletions.
5 changes: 5 additions & 0 deletions adapter/experimental.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ type URLTestGroup interface {
URLTest(ctx context.Context, url string) (map[string]uint16, error)
}

type RelayGroup interface {
OutboundGroup
IsRelay() bool
}

func OutboundTag(detour Outbound) string {
if group, isGroup := detour.(OutboundGroup); isGroup {
return group.Now()
Expand Down
4 changes: 4 additions & 0 deletions adapter/outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ type Outbound interface {
type OutboundUseIP interface {
UseIP() bool
}

type OutboundRelay interface {
SetRelay(detour N.Dialer) Outbound
}
6 changes: 6 additions & 0 deletions common/dialer/detour.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ func NewDetour(router adapter.Router, detour string) N.Dialer {
return &DetourDialer{router: router, detour: detour}
}

func NewDetourWithDialer(router adapter.Router, detour adapter.Outbound) N.Dialer {
d := DetourDialer{router: router, detour: detour.Tag()}
d.initOnce.Do(func() { d.dialer = detour })
return &d
}

func (d *DetourDialer) Start() error {
_, err := d.Dialer()
return err
Expand Down
3 changes: 3 additions & 0 deletions constant/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
const (
TypeSelector = "selector"
TypeURLTest = "urltest"
TypeRelay = "relay"
)

func ProxyDisplayName(proxyType string) string {
Expand Down Expand Up @@ -72,6 +73,8 @@ func ProxyDisplayName(proxyType string) string {
return "Selector"
case TypeURLTest:
return "URLTest"
case TypeRelay:
return "Relay"
default:
return "Unknown"
}
Expand Down
5 changes: 5 additions & 0 deletions option/clash.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ type URLTestOutboundOptions struct {
Tolerance uint16 `json:"tolerance,omitempty"`
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
}

type RelayOutboundOptions struct {
Outbounds []string `json:"outbounds"`
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
}
5 changes: 5 additions & 0 deletions option/outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type _Outbound struct {
Hysteria2Options Hysteria2OutboundOptions `json:"-"`
SelectorOptions SelectorOutboundOptions `json:"-"`
URLTestOptions URLTestOutboundOptions `json:"-"`
RelayOptions RelayOutboundOptions `json:"-"`
}

type Outbound _Outbound
Expand Down Expand Up @@ -70,6 +71,8 @@ func (h Outbound) MarshalJSON() ([]byte, error) {
v = h.SelectorOptions
case C.TypeURLTest:
v = h.URLTestOptions
case C.TypeRelay:
v = h.RelayOptions
default:
return nil, E.New("unknown outbound type: ", h.Type)
}
Expand Down Expand Up @@ -119,6 +122,8 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error {
v = &h.SelectorOptions
case C.TypeURLTest:
v = &h.URLTestOptions
case C.TypeRelay:
v = &h.RelayOptions
default:
return E.New("unknown outbound type: ", h.Type)
}
Expand Down
2 changes: 2 additions & 0 deletions outbound/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, t
return NewSelector(router, logger, tag, options.SelectorOptions)
case C.TypeURLTest:
return NewURLTest(ctx, router, logger, tag, options.URLTestOptions)
case C.TypeRelay:
return NewRelay(router, logger, tag, options.RelayOptions)
default:
return nil, E.New("unknown outbound type: ", options.Type)
}
Expand Down
18 changes: 17 additions & 1 deletion outbound/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"net"
"os"
"reflect"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
Expand All @@ -17,7 +18,10 @@ import (
sHTTP "github.com/sagernet/sing/protocol/http"
)

var _ adapter.Outbound = (*HTTP)(nil)
var (
_ adapter.Outbound = (*HTTP)(nil)
_ adapter.OutboundRelay = (*HTTP)(nil)
)

type HTTP struct {
myOutboundAdapter
Expand Down Expand Up @@ -73,3 +77,15 @@ func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapte
func (h *HTTP) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return os.ErrInvalid
}

func (h *HTTP) SetRelay(detour N.Dialer) adapter.Outbound {
c := *h.client
client := c
r := reflect.ValueOf(client)
r.FieldByName("dialer").Set(reflect.ValueOf(detour))
outbound := HTTP{
myOutboundAdapter: h.myOutboundAdapter,
client: &client,
}
return &outbound
}
14 changes: 14 additions & 0 deletions outbound/hysteria.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"net"
"os"
"reflect"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
Expand All @@ -24,6 +25,7 @@ import (

var (
_ adapter.Outbound = (*Hysteria)(nil)
_ adapter.OutboundRelay = (*Hysteria)(nil)
_ adapter.InterfaceUpdateListener = (*Hysteria)(nil)
)

Expand Down Expand Up @@ -138,3 +140,15 @@ func (h *Hysteria) InterfaceUpdated() {
func (h *Hysteria) Close() error {
return h.client.CloseWithError(os.ErrClosed)
}

func (h *Hysteria) SetRelay(detour N.Dialer) adapter.Outbound {
c := *h.client
client := c
r := reflect.ValueOf(client)
r.FieldByName("dialer").Set(reflect.ValueOf(detour))
outbound := Hysteria{
myOutboundAdapter: h.myOutboundAdapter,
client: &client,
}
return &outbound
}
14 changes: 14 additions & 0 deletions outbound/hysteria2.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"net"
"os"
"reflect"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
Expand All @@ -24,6 +25,7 @@ import (

var (
_ adapter.Outbound = (*Hysteria2)(nil)
_ adapter.OutboundRelay = (*Hysteria2)(nil)
_ adapter.InterfaceUpdateListener = (*Hysteria2)(nil)
)

Expand Down Expand Up @@ -124,3 +126,15 @@ func (h *Hysteria2) InterfaceUpdated() {
func (h *Hysteria2) Close() error {
return h.client.CloseWithError(os.ErrClosed)
}

func (h *Hysteria2) SetRelay(detour N.Dialer) adapter.Outbound {
c := *h.client
client := c
r := reflect.ValueOf(client)
r.FieldByName("dialer").Set(reflect.ValueOf(detour))
outbound := Hysteria2{
myOutboundAdapter: h.myOutboundAdapter,
client: &client,
}
return &outbound
}
133 changes: 133 additions & 0 deletions outbound/relay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package outbound

import (
"context"
"net"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/interrupt"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)

var (
_ adapter.Outbound = (*Relay)(nil)
_ adapter.OutboundGroup = (*Relay)(nil)
_ adapter.RelayGroup = (*Relay)(nil)
)

type Relay struct {
myOutboundAdapter
tags []string
interruptGroup *interrupt.Group
interruptExternalConnections bool
}

func NewRelay(router adapter.Router, logger log.ContextLogger, tag string, options option.RelayOutboundOptions) (*Relay, error) {
outbound := &Relay{
myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeRelay,
router: router,
logger: logger,
tag: tag,
dependencies: options.Outbounds,
},
tags: options.Outbounds,
interruptGroup: interrupt.NewGroup(),
interruptExternalConnections: options.InterruptExistConnections,
}
if len(outbound.tags) == 0 {
return nil, E.New("missing tags")
}
return outbound, nil
}

func (r *Relay) Network() []string {
detour, _ := r.router.Outbound(r.tags[0])
return detour.Network()
}

func (r *Relay) Start() error {
for i, tag := range r.tags {
outbound, loaded := r.router.Outbound(tag)
if !loaded {
return E.New("outbound ", i, " not found: ", tag)
}
if _, isRelay := outbound.(adapter.RelayGroup); isRelay {
return E.New("relay outbound invalid: ", tag)
}
}
return nil
}

func (r *Relay) Now() string {
return ""
}

func (r *Relay) All() []string {
return r.tags
}

func (s *Relay) UpdateOutbounds(tag string) error {
return nil
}

func (s *Relay) IsRelay() bool {
return true
}

func (r *Relay) SelectedOutbound(network string) adapter.Outbound {
detour, _ := r.router.Outbound(r.tags[len(r.tags)-1])
return detour
}

func (r *Relay) createRelayChain(network string) adapter.Outbound {
len := len(r.tags)
detour, _ := r.router.Outbound(r.tags[0])
tag := RealOutboundTag(detour, network)
detour, _ = r.router.OutboundWithProvider(tag)
for i := 1; i < len; i++ {
out, _ := r.router.Outbound(r.tags[i])
tag := RealOutboundTag(out, network)
out, _ = r.router.OutboundWithProvider(tag)
outbound := out.(adapter.OutboundRelay)
d := dialer.NewDetourWithDialer(r.router, detour)
detour = outbound.SetRelay(d)
}
return detour
}

func (r *Relay) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
detour := r.createRelayChain(network)
conn, err := detour.DialContext(ctx, network, destination)
if err != nil {
return nil, err
}
return r.interruptGroup.NewConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
}

func (r *Relay) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
detour := r.createRelayChain(N.NetworkUDP)
conn, err := detour.ListenPacket(ctx, destination)
if err != nil {
return nil, err
}
return r.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
}

func (r *Relay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
detour := r.createRelayChain(metadata.Network)
ctx = interrupt.ContextWithIsExternalConnection(ctx)
return detour.NewConnection(ctx, conn, metadata)
}

func (r *Relay) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
detour := r.createRelayChain(metadata.Network)
ctx = interrupt.ContextWithIsExternalConnection(ctx)
return detour.NewPacketConnection(ctx, conn, metadata)
}
12 changes: 11 additions & 1 deletion outbound/shadowsocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import (
"github.com/sagernet/sing/common/uot"
)

var _ adapter.Outbound = (*Shadowsocks)(nil)
var (
_ adapter.Outbound = (*Shadowsocks)(nil)
_ adapter.OutboundRelay = (*Shadowsocks)(nil)
)

type Shadowsocks struct {
myOutboundAdapter
Expand Down Expand Up @@ -187,3 +190,10 @@ func (h *shadowsocksDialer) ListenPacket(ctx context.Context, destination M.Sock
}
return h.method.DialPacketConn(outConn), nil
}

func (s *Shadowsocks) SetRelay(detour N.Dialer) adapter.Outbound {
ss := *s
outbound := ss
outbound.dialer = detour
return &outbound
}
12 changes: 11 additions & 1 deletion outbound/shadowsocksr.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import (
"github.com/Dreamacro/clash/transport/socks5"
)

var _ adapter.Outbound = (*ShadowsocksR)(nil)
var (
_ adapter.Outbound = (*ShadowsocksR)(nil)
_ adapter.OutboundRelay = (*ShadowsocksR)(nil)
)

type ShadowsocksR struct {
myOutboundAdapter
Expand Down Expand Up @@ -193,3 +196,10 @@ func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
copy(b, b[len(addr):])
return n - len(addr), udpAddr, e
}

func (h *ShadowsocksR) SetRelay(detour N.Dialer) adapter.Outbound {
ssr := *h
outbound := ssr
outbound.dialer = detour
return &outbound
}
Loading

0 comments on commit 729dbe7

Please sign in to comment.