Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/CRONET_GO_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2fef65f9dba90ddb89a87d00a6eb6165487c10c1
ea7cd33752aed62603775af3df946c1b83f4b0b3
21 changes: 21 additions & 0 deletions adapter/certificate/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package certificate

type Adapter struct {
providerType string
providerTag string
}

func NewAdapter(providerType string, providerTag string) Adapter {
return Adapter{
providerType: providerType,
providerTag: providerTag,
}
}

func (a *Adapter) Type() string {
return a.providerType
}

func (a *Adapter) Tag() string {
return a.providerTag
}
158 changes: 158 additions & 0 deletions adapter/certificate/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package certificate

import (
"context"
"os"
"sync"
"time"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/taskmonitor"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
)

var _ adapter.CertificateProviderManager = (*Manager)(nil)

type Manager struct {
logger log.ContextLogger
registry adapter.CertificateProviderRegistry
access sync.Mutex
started bool
stage adapter.StartStage
providers []adapter.CertificateProviderService
providerByTag map[string]adapter.CertificateProviderService
}

func NewManager(logger log.ContextLogger, registry adapter.CertificateProviderRegistry) *Manager {
return &Manager{
logger: logger,
registry: registry,
providerByTag: make(map[string]adapter.CertificateProviderService),
}
}

func (m *Manager) Start(stage adapter.StartStage) error {
m.access.Lock()
if m.started && m.stage >= stage {
panic("already started")
}
m.started = true
m.stage = stage
providers := m.providers
m.access.Unlock()
for _, provider := range providers {
name := "certificate-provider/" + provider.Type() + "[" + provider.Tag() + "]"
m.logger.Trace(stage, " ", name)
startTime := time.Now()
err := adapter.LegacyStart(provider, stage)
if err != nil {
return E.Cause(err, stage, " ", name)
}
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
}
return nil
}

func (m *Manager) Close() error {
m.access.Lock()
defer m.access.Unlock()
if !m.started {
return nil
}
m.started = false
providers := m.providers
m.providers = nil
monitor := taskmonitor.New(m.logger, C.StopTimeout)
var err error
for _, provider := range providers {
name := "certificate-provider/" + provider.Type() + "[" + provider.Tag() + "]"
m.logger.Trace("close ", name)
startTime := time.Now()
monitor.Start("close ", name)
err = E.Append(err, provider.Close(), func(err error) error {
return E.Cause(err, "close ", name)
})
monitor.Finish()
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
}
return err
}

func (m *Manager) CertificateProviders() []adapter.CertificateProviderService {
m.access.Lock()
defer m.access.Unlock()
return m.providers
}

func (m *Manager) Get(tag string) (adapter.CertificateProviderService, bool) {
m.access.Lock()
provider, found := m.providerByTag[tag]
m.access.Unlock()
return provider, found
}

func (m *Manager) Remove(tag string) error {
m.access.Lock()
provider, found := m.providerByTag[tag]
if !found {
m.access.Unlock()
return os.ErrInvalid
}
delete(m.providerByTag, tag)
index := common.Index(m.providers, func(it adapter.CertificateProviderService) bool {
return it == provider
})
if index == -1 {
panic("invalid certificate provider index")
}
m.providers = append(m.providers[:index], m.providers[index+1:]...)
started := m.started
m.access.Unlock()
if started {
return provider.Close()
}
return nil
}

func (m *Manager) Create(ctx context.Context, logger log.ContextLogger, tag string, providerType string, options any) error {
provider, err := m.registry.Create(ctx, logger, tag, providerType, options)
if err != nil {
return err
}
m.access.Lock()
defer m.access.Unlock()
if m.started {
name := "certificate-provider/" + provider.Type() + "[" + provider.Tag() + "]"
for _, stage := range adapter.ListStartStages {
m.logger.Trace(stage, " ", name)
startTime := time.Now()
err = adapter.LegacyStart(provider, stage)
if err != nil {
return E.Cause(err, stage, " ", name)
}
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
}
}
if existsProvider, loaded := m.providerByTag[tag]; loaded {
if m.started {
err = existsProvider.Close()
if err != nil {
return E.Cause(err, "close certificate-provider/", existsProvider.Type(), "[", existsProvider.Tag(), "]")
}
}
existsIndex := common.Index(m.providers, func(it adapter.CertificateProviderService) bool {
return it == existsProvider
})
if existsIndex == -1 {
panic("invalid certificate provider index")
}
m.providers = append(m.providers[:existsIndex], m.providers[existsIndex+1:]...)
}
m.providers = append(m.providers, provider)
m.providerByTag[tag] = provider
return nil
}
72 changes: 72 additions & 0 deletions adapter/certificate/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package certificate

import (
"context"
"sync"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
)

type ConstructorFunc[T any] func(ctx context.Context, logger log.ContextLogger, tag string, options T) (adapter.CertificateProviderService, error)

func Register[Options any](registry *Registry, providerType string, constructor ConstructorFunc[Options]) {
registry.register(providerType, func() any {
return new(Options)
}, func(ctx context.Context, logger log.ContextLogger, tag string, rawOptions any) (adapter.CertificateProviderService, error) {
var options *Options
if rawOptions != nil {
options = rawOptions.(*Options)
}
return constructor(ctx, logger, tag, common.PtrValueOrDefault(options))
})
}

var _ adapter.CertificateProviderRegistry = (*Registry)(nil)

type (
optionsConstructorFunc func() any
constructorFunc func(ctx context.Context, logger log.ContextLogger, tag string, options any) (adapter.CertificateProviderService, error)
)

type Registry struct {
access sync.Mutex
optionsType map[string]optionsConstructorFunc
constructor map[string]constructorFunc
}

func NewRegistry() *Registry {
return &Registry{
optionsType: make(map[string]optionsConstructorFunc),
constructor: make(map[string]constructorFunc),
}
}

func (m *Registry) CreateOptions(providerType string) (any, bool) {
m.access.Lock()
defer m.access.Unlock()
optionsConstructor, loaded := m.optionsType[providerType]
if !loaded {
return nil, false
}
return optionsConstructor(), true
}

func (m *Registry) Create(ctx context.Context, logger log.ContextLogger, tag string, providerType string, options any) (adapter.CertificateProviderService, error) {
m.access.Lock()
defer m.access.Unlock()
constructor, loaded := m.constructor[providerType]
if !loaded {
return nil, E.New("certificate provider type not found: " + providerType)
}
return constructor(ctx, logger, tag, options)
}

func (m *Registry) register(providerType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
m.access.Lock()
defer m.access.Unlock()
m.optionsType[providerType] = optionsConstructor
m.constructor[providerType] = constructor
}
38 changes: 38 additions & 0 deletions adapter/certificate_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package adapter

import (
"context"
"crypto/tls"

"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
)

type CertificateProvider interface {
GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error)
}

type ACMECertificateProvider interface {
CertificateProvider
GetACMENextProtos() []string
}

type CertificateProviderService interface {
Lifecycle
Type() string
Tag() string
CertificateProvider
}

type CertificateProviderRegistry interface {
option.CertificateProviderOptionsRegistry
Create(ctx context.Context, logger log.ContextLogger, tag string, providerType string, options any) (CertificateProviderService, error)
}

type CertificateProviderManager interface {
Lifecycle
CertificateProviders() []CertificateProviderService
Get(tag string) (CertificateProviderService, bool)
Remove(tag string) error
Create(ctx context.Context, logger log.ContextLogger, tag string, providerType string, options any) error
}
3 changes: 3 additions & 0 deletions adapter/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package adapter

import (
"context"
"net"
"net/netip"
"time"

Expand Down Expand Up @@ -82,6 +83,8 @@ type InboundContext struct {
SourceGeoIPCode string
GeoIPCode string
ProcessInfo *ConnectionOwner
SourceMACAddress net.HardwareAddr
SourceHostname string
QueryType uint16
FakeIP bool

Expand Down
23 changes: 23 additions & 0 deletions adapter/neighbor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package adapter

import (
"net"
"net/netip"
)

type NeighborEntry struct {
Address netip.Addr
MACAddress net.HardwareAddr
Hostname string
}

type NeighborResolver interface {
LookupMAC(address netip.Addr) (net.HardwareAddr, bool)
LookupHostname(address netip.Addr) (string, bool)
Start() error
Close() error
}

type NeighborUpdateListener interface {
UpdateNeighborTable(entries []NeighborEntry)
}
4 changes: 4 additions & 0 deletions adapter/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ type PlatformInterface interface {

UsePlatformNotification() bool
SendNotification(notification *Notification) error

UsePlatformNeighborResolver() bool
StartNeighborMonitor(listener NeighborUpdateListener) error
CloseNeighborMonitor(listener NeighborUpdateListener) error
}

type FindConnectionOwnerRequest struct {
Expand Down
2 changes: 2 additions & 0 deletions adapter/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type Router interface {
RuleSet(tag string) (RuleSet, bool)
Rules() []Rule
NeedFindProcess() bool
NeedFindNeighbor() bool
NeighborResolver() NeighborResolver
AppendTracker(tracker ConnectionTracker)
ResetNetwork()
}
Expand Down
Loading