Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
04c0490
Add MAC and hostname rule items
nekohasekai Mar 3, 2026
45339d1
Add Android support for MAC and hostname rule items
nekohasekai Mar 4, 2026
eeb5dea
Add macOS support for MAC and hostname rule items
nekohasekai Mar 6, 2026
77e5103
documentation: Update descriptions for neighbor rules
nekohasekai Mar 6, 2026
47742ab
cronet-go: Update chromium to 145.0.7632.159
nekohasekai Mar 11, 2026
2132e68
Refactor ACME support to certificate provider
nekohasekai Mar 23, 2026
ab323e0
Add BBR profile and hop interval randomization for Hysteria2
nekohasekai Mar 30, 2026
ebf8a21
Bump version
nekohasekai Mar 7, 2026
b68f467
Add cloudflare tunnel inbound
nekohasekai Mar 18, 2026
87a2f4c
Fix cloudflared registration parameter inconsistencies
nekohasekai Mar 24, 2026
01a8405
Implement router-backed cloudflare tunnel ingress config
nekohasekai Mar 24, 2026
124379f
Support regional cloudflare edge selection
nekohasekai Mar 24, 2026
b3cad02
Apply origin request SNI selection
nekohasekai Mar 24, 2026
71c7a58
Route cloudflare tunnel ICMP through sing-box router
nekohasekai Mar 24, 2026
4579ca9
Add cloudflare tunnel bastion and socks special services
nekohasekai Mar 24, 2026
25a94ac
Support router-backed cloudflare stream services
nekohasekai Mar 24, 2026
8547189
Honor cloudflare warp active flow limits
nekohasekai Mar 24, 2026
ed6be9b
Validate cloudflare access protected origins
nekohasekai Mar 24, 2026
1ea083c
Apply cloudflare origin proxy transport options
nekohasekai Mar 24, 2026
289101f
Enforce cloudflare access on all ingress services
nekohasekai Mar 24, 2026
d017cbe
Stabilize cloudflare edge transport fallback
nekohasekai Mar 24, 2026
2321e94
Route cloudflare control plane through configurable dialer
nekohasekai Mar 24, 2026
d7b8689
Serve cloudflare hello world over TLS
nekohasekai Mar 24, 2026
e6a7efc
Cover direct cloudflare origin services
nekohasekai Mar 24, 2026
2340db6
Report unreachable cloudflare v3 registrations
nekohasekai Mar 24, 2026
e54707c
Return v3 registration protocol errors
nekohasekai Mar 24, 2026
a95f56c
cloudflare: enforce socks-proxy ip_rules
nekohasekai Mar 24, 2026
af2afc5
cloudflare: require remote-managed tunnels
nekohasekai Mar 24, 2026
2cf2ff3
Rename cloudflare-tunnel type to cloudflared
nekohasekai Mar 24, 2026
012335e
fix cloudflared warp datagram behavior
nekohasekai Mar 24, 2026
6e35f4d
Route cloudflare TCP through pipe
nekohasekai Mar 24, 2026
1320b73
Align cloudflare runtime behavior with cloudflared
nekohasekai Mar 24, 2026
7ca692d
Remove hello_world cloudflare service
nekohasekai Mar 24, 2026
4497f61
Fix cloudflared test and protocol parity
nekohasekai Mar 25, 2026
316c255
Fix cloudflared compatibility gaps
nekohasekai Mar 25, 2026
3eb6265
Fix cloudflared parity gaps
nekohasekai Mar 25, 2026
c07abee
Fix cloudflared parity regressions
nekohasekai Mar 26, 2026
e1847aa
Align cloudflared stream scheme handling
nekohasekai Mar 26, 2026
2edbc42
Fix cloudflared safe stream, safe transport and split dialer
nekohasekai Mar 26, 2026
e3ed3f0
Fix cloudflared parity gaps
nekohasekai Mar 27, 2026
d5a2fd7
Fix low-cost cloudflared parity gaps
nekohasekai Mar 27, 2026
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
Loading