Skip to content

Commit

Permalink
Add Outbound Providers
Browse files Browse the repository at this point in the history
  • Loading branch information
PuerNya committed Nov 3, 2023
1 parent 8ee5b44 commit fb8c45e
Show file tree
Hide file tree
Showing 56 changed files with 4,022 additions and 124 deletions.
1 change: 1 addition & 0 deletions adapter/experimental.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type OutboundGroup interface {
Outbound
Now() string
All() []string
UpdateOutbounds(tag string) error
SelectedOutbound(network string) Outbound
}

Expand Down
2 changes: 2 additions & 0 deletions adapter/outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import (
type Outbound interface {
Type() string
Tag() string
Port() int
Network() []string
Dependencies() []string
N.Dialer
NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
SetTag(tag string)
}

type OutboundUseIP interface {
Expand Down
24 changes: 24 additions & 0 deletions adapter/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package adapter

import (
"context"
"time"
)

type OutboundProvider interface {
Tag() string
Path() string
Type() string
Outbounds() []Outbound
Outbound(tag string) (Outbound, bool)
UpdateTime() time.Time

Start()
Close() error
PRLock()
PRUnlock()
Healthcheck(ctx context.Context, link string, force bool) (map[string]uint16, error)
SubscriptionInfo() map[string]uint64
UpdateProvider(ctx context.Context, router Router, force bool) error
UpdateOutboundByTag()
}
6 changes: 6 additions & 0 deletions adapter/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ type Router interface {

Outbounds() []Outbound
Outbound(tag string) (Outbound, bool)
OutboundsWithProvider() []Outbound
OutboundWithProvider(tag string) (Outbound, bool)
DefaultOutbound(network string) Outbound
DefaultOutboundForConnection() Outbound

OutboundProviders() []OutboundProvider
OutboundProvider(tag string) (OutboundProvider, bool)

FakeIPStore() FakeIPStore

Expand Down
61 changes: 54 additions & 7 deletions box.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/outbound"
P "github.com/sagernet/sing-box/provider"
"github.com/sagernet/sing-box/route"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
Expand All @@ -30,6 +31,7 @@ type Box struct {
router adapter.Router
inbounds []adapter.Inbound
outbounds []adapter.Outbound
providers []adapter.OutboundProvider
logFactory log.Factory
logger log.ContextLogger
preServices map[string]adapter.Service
Expand Down Expand Up @@ -89,7 +91,8 @@ func New(options Options) (*Box, error) {
return nil, E.Cause(err, "parse route options")
}
inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
outbounds := []adapter.Outbound{}
providers := make([]adapter.OutboundProvider, 0, len(options.OutboundProviders))
for i, inboundOptions := range options.Inbounds {
var in adapter.Inbound
var tag string
Expand All @@ -110,6 +113,32 @@ func New(options Options) (*Box, error) {
}
inbounds = append(inbounds, in)
}
for i, outboundProvderOptions := range options.OutboundProviders {
var provider adapter.OutboundProvider
var tag string
if outboundProvderOptions.Tag != "" {
tag = outboundProvderOptions.Tag
} else {
tag = F.ToString(i)
}
provider, err = P.New(
ctx,
router,
logFactory.NewLogger(F.ToString("provider", "[", tag, "]")),
outboundProvderOptions,
)
if err != nil {
return nil, E.Cause(err, "parse outbound provider[", i, "]")
}
providers = append(providers, provider)
}
OUTBOUNDLESS, _ := outbound.New(
ctx,
router,
logFactory.NewLogger(F.ToString("outbound/direct[OUTBOUNDLESS]")),
"OUTBOUNDLESS",
option.Outbound{Type: "direct", Tag: "OUTBOUNDLESS"})
outbounds = append(outbounds, OUTBOUNDLESS)
for i, outboundOptions := range options.Outbounds {
var out adapter.Outbound
var tag string
Expand All @@ -129,12 +158,7 @@ func New(options Options) (*Box, error) {
}
outbounds = append(outbounds, out)
}
err = router.Initialize(inbounds, outbounds, func() adapter.Outbound {
out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), "direct", option.Outbound{Type: "direct", Tag: "default"})
common.Must(oErr)
outbounds = append(outbounds, out)
return out
})
err = router.Initialize(inbounds, providers, outbounds)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -168,6 +192,7 @@ func New(options Options) (*Box, error) {
router: router,
inbounds: inbounds,
outbounds: outbounds,
providers: providers,
createdAt: createdAt,
logFactory: logFactory,
logger: logFactory.Logger(),
Expand Down Expand Up @@ -229,6 +254,16 @@ func (s *Box) preStart() error {
if err != nil {
return err
}
for i, p := range s.providers {
var tag string
if p.Tag() == "" {
tag = F.ToString(i)
} else {
tag = p.Tag()
}
p.Start()
s.logger.Trace("initializing outbound provider/", p.Type(), "[", tag, "]")
}
return s.router.Start()
}

Expand Down Expand Up @@ -306,6 +341,18 @@ func (s *Box) Close() error {
return E.Cause(err, "close outbound/", out.Type(), "[", i, "]")
})
}
for i, prov := range s.providers {
for j, out := range prov.Outbounds() {
s.logger.Trace("closing provider/", prov.Type(), "[", i, "]", " outbound/", out.Type(), "[", j, "]")
errors = E.Append(errors, common.Close(out), func(err error) error {
return E.Cause(err, "close provider/", prov.Type(), "[", i, "]", " outbound/", out.Type(), "[", j, "]")
})
}
s.logger.Trace("closing provider/", prov.Type(), "[", i, "]")
errors = E.Append(errors, common.Close(prov), func(err error) error {
return E.Cause(err, "close provider/", prov.Type(), "[", i, "]")
})
}
s.logger.Trace("closing router")
if err := common.Close(s.router); err != nil {
errors = E.Append(errors, err, func(err error) error {
Expand Down
51 changes: 48 additions & 3 deletions box_outbound.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package box

import (
"fmt"
"strings"

"github.com/sagernet/sing-box/adapter"
Expand All @@ -9,22 +10,66 @@ import (
F "github.com/sagernet/sing/common/format"
)

func (s *Box) startProviderOutbounds() error {
outboundTag := make(map[string]int)
for _, out := range s.outbounds {
tag := out.Tag()
outboundTag[tag] = 0
}
for i, p := range s.providers {
var pTag string
if p.Tag() == "" {
pTag = F.ToString(i)
} else {
pTag = p.Tag()
}
for j, out := range p.Outbounds() {
var tag string
if out.Tag() == "" {
out.SetTag(fmt.Sprint("[", pTag, "]", F.ToString(j)))
}
tag = out.Tag()
if _, exists := outboundTag[tag]; exists {
count := outboundTag[tag] + 1
tag = fmt.Sprint(tag, "[", count, "]")
out.SetTag(tag)
outboundTag[tag] = count
}
outboundTag[tag] = 0
if starter, isStarter := out.(common.Starter); isStarter {
s.logger.Trace("initializing outbound provider[", pTag, "]", " outbound/", out.Type(), "[", tag, "]")
err := starter.Start()
if err != nil {
return E.Cause(err, "initialize outbound provider[", pTag, "]", " outbound/", out.Type(), "[", tag, "]")
}
}
}
p.PRLock()
p.UpdateOutboundByTag()
p.PRUnlock()
}
return nil
}

func (s *Box) startOutbounds() error {
outboundTags := make(map[adapter.Outbound]string)
outbounds := make(map[string]adapter.Outbound)
for i, outboundToStart := range s.outbounds {
var outboundTag string
if outboundToStart.Tag() == "" {
outboundTag = F.ToString(i)
} else {
outboundTag = outboundToStart.Tag()
outboundToStart.SetTag(F.ToString(i))
}
outboundTag = outboundToStart.Tag()
if _, exists := outbounds[outboundTag]; exists {
return E.New("outbound tag ", outboundTag, " duplicated")
}
outboundTags[outboundToStart] = outboundTag
outbounds[outboundTag] = outboundToStart
}
err := s.startProviderOutbounds()
if err != nil {
return nil
}
started := make(map[string]bool)
for {
canContinue := false
Expand Down
2 changes: 1 addition & 1 deletion common/dialer/detour.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (d *DetourDialer) Start() error {
func (d *DetourDialer) Dialer() (N.Dialer, error) {
d.initOnce.Do(func() {
var loaded bool
d.dialer, loaded = d.router.Outbound(d.detour)
d.dialer, loaded = d.router.OutboundWithProvider(d.detour)
if !loaded {
d.initErr = E.New("outbound detour not found: ", d.detour)
}
Expand Down
6 changes: 6 additions & 0 deletions constant/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package constant

const (
TypeFileProvider = "file"
TypeHTTPProvider = "http"
)
20 changes: 11 additions & 9 deletions docs/configuration/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,24 @@ sing-box uses JSON for configuration files.
"ntp": {},
"inbounds": [],
"outbounds": [],
"outbound_providers": [],
"route": {},
"experimental": {}
}
```

### Fields

| Key | Format |
|----------------|--------------------------------|
| `log` | [Log](./log) |
| `dns` | [DNS](./dns) |
| `ntp` | [NTP](./ntp) |
| `inbounds` | [Inbound](./inbound) |
| `outbounds` | [Outbound](./outbound) |
| `route` | [Route](./route) |
| `experimental` | [Experimental](./experimental) |
| Key | Format |
|----------------------|-----------------------------------------|
| `log` | [Log](./log) |
| `dns` | [DNS](./dns) |
| `ntp` | [NTP](./ntp) |
| `inbounds` | [Inbound](./inbound) |
| `outbounds` | [Outbound](./outbound) |
| `outbound_providers` | [OutboundProvider](./outbound_provider) |
| `route` | [Route](./route) |
| `experimental` | [Experimental](./experimental) |

### Check

Expand Down
18 changes: 10 additions & 8 deletions docs/configuration/index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@ sing-box 使用 JSON 作为配置文件格式。
"dns": {},
"inbounds": [],
"outbounds": [],
"outbound_providers": [],
"route": {},
"experimental": {}
}
```

### 字段

| Key | Format |
|----------------|-----------------------|
| `log` | [日志](./log) |
| `dns` | [DNS](./dns) |
| `inbounds` | [入站](./inbound) |
| `outbounds` | [出站](./outbound) |
| `route` | [路由](./route) |
| `experimental` | [实验性](./experimental) |
| Key | Format |
|----------------------|---------------------------------|
| `log` | [日志](./log) |
| `dns` | [DNS](./dns) |
| `inbounds` | [入站](./inbound) |
| `outbounds` | [出站](./outbound) |
| `outbound_providers` | [出站提供者](./outbound_provider) |
| `route` | [路由](./route) |
| `experimental` | [实验性](./experimental) |

### 检查

Expand Down
Loading

0 comments on commit fb8c45e

Please sign in to comment.