mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-10-26 13:06:56 +00:00
boxdd: Add insecure mode
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
package adapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecurityPolicy interface {
|
||||||
|
CheckFeature(feature string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckSecurityFeature(ctx context.Context, feature string) error {
|
||||||
|
policy := service.FromContext[SecurityPolicy](ctx)
|
||||||
|
if policy == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return policy.CheckFeature(feature)
|
||||||
|
}
|
||||||
@@ -189,7 +189,7 @@ func New(options Options) (*Box, error) {
|
|||||||
len(certificateOptions.Certificate) > 0 ||
|
len(certificateOptions.Certificate) > 0 ||
|
||||||
len(certificateOptions.CertificatePath) > 0 ||
|
len(certificateOptions.CertificatePath) > 0 ||
|
||||||
len(certificateOptions.CertificateDirectoryPath) > 0 {
|
len(certificateOptions.CertificateDirectoryPath) > 0 {
|
||||||
certificateStore, err := certificate.NewStore(logFactory.NewLogger("certificate"), certificateOptions)
|
certificateStore, err := certificate.NewStore(ctx, logFactory.NewLogger("certificate"), certificateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -436,6 +436,12 @@ func New(options Options) (*Box, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ntpOptions.Enabled {
|
if ntpOptions.Enabled {
|
||||||
|
if ntpOptions.WriteToSystem {
|
||||||
|
err = adapter.CheckSecurityFeature(ctx, "NTP `write_to_system`")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions, ntpOptions.ServerIsDomain())
|
ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions, ntpOptions.ServerIsDomain())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "create NTP service")
|
return nil, E.Cause(err, "create NTP service")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package certificate
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
@@ -15,11 +16,13 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.CertificateStore = (*Store)(nil)
|
var _ adapter.CertificateStore = (*Store)(nil)
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
|
ctx context.Context
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
storeType string
|
storeType string
|
||||||
systemPool *x509.CertPool
|
systemPool *x509.CertPool
|
||||||
@@ -32,7 +35,7 @@ type Store struct {
|
|||||||
platform storePlatform
|
platform storePlatform
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStore(logger logger.Logger, options option.CertificateOptions) (*Store, error) {
|
func NewStore(ctx context.Context, logger logger.Logger, options option.CertificateOptions) (*Store, error) {
|
||||||
storeType := options.Store
|
storeType := options.Store
|
||||||
if storeType == "" {
|
if storeType == "" {
|
||||||
storeType = C.CertificateStoreSystem
|
storeType = C.CertificateStoreSystem
|
||||||
@@ -59,6 +62,7 @@ func NewStore(logger logger.Logger, options option.CertificateOptions) (*Store,
|
|||||||
return nil, E.New("unknown certificate store: ", options.Store)
|
return nil, E.New("unknown certificate store: ", options.Store)
|
||||||
}
|
}
|
||||||
store := &Store{
|
store := &Store{
|
||||||
|
ctx: ctx,
|
||||||
storeType: storeType,
|
storeType: storeType,
|
||||||
systemPool: systemPool,
|
systemPool: systemPool,
|
||||||
certificate: strings.Join(options.Certificate, "\n"),
|
certificate: strings.Join(options.Certificate, "\n"),
|
||||||
@@ -165,7 +169,7 @@ func (s *Store) update() error {
|
|||||||
appendPEMBlock(pemBuffer, s.certificate)
|
appendPEMBlock(pemBuffer, s.certificate)
|
||||||
}
|
}
|
||||||
for _, path := range s.certificatePaths {
|
for _, path := range s.certificatePaths {
|
||||||
pemContent, err := os.ReadFile(path)
|
pemContent, err := filemanager.ReadFile(s.ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -176,7 +180,7 @@ func (s *Store) update() error {
|
|||||||
}
|
}
|
||||||
var firstErr error
|
var firstErr error
|
||||||
for _, directoryPath := range s.certificateDirectoryPaths {
|
for _, directoryPath := range s.certificateDirectoryPaths {
|
||||||
directoryEntries, err := readUniqueDirectoryEntries(directoryPath)
|
directoryEntries, err := readUniqueDirectoryEntries(s.ctx, directoryPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if firstErr == nil && !os.IsNotExist(err) {
|
if firstErr == nil && !os.IsNotExist(err) {
|
||||||
firstErr = E.Cause(err, "invalid certificate directory: ", directoryPath)
|
firstErr = E.Cause(err, "invalid certificate directory: ", directoryPath)
|
||||||
@@ -184,7 +188,7 @@ func (s *Store) update() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, directoryEntry := range directoryEntries {
|
for _, directoryEntry := range directoryEntries {
|
||||||
pemContent, err := os.ReadFile(filepath.Join(directoryPath, directoryEntry.Name()))
|
pemContent, err := filemanager.ReadFile(s.ctx, filepath.Join(directoryPath, directoryEntry.Name()))
|
||||||
if err == nil && currentPool.AppendCertsFromPEM(pemContent) {
|
if err == nil && currentPool.AppendCertsFromPEM(pemContent) {
|
||||||
appendPEMBlock(pemBuffer, string(pemContent))
|
appendPEMBlock(pemBuffer, string(pemContent))
|
||||||
}
|
}
|
||||||
@@ -223,8 +227,8 @@ func (s *Store) newBasePool() (*x509.CertPool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) {
|
func readUniqueDirectoryEntries(ctx context.Context, dir string) ([]fs.DirEntry, error) {
|
||||||
files, err := os.ReadDir(dir)
|
files, err := filemanager.ReadDir(ctx, dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -5,6 +5,7 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
"github.com/libdns/acmedns"
|
"github.com/libdns/acmedns"
|
||||||
@@ -54,8 +56,13 @@ func startACME(ctx context.Context, logger logger.Logger, options option.Inbound
|
|||||||
}
|
}
|
||||||
var storage certmagic.Storage
|
var storage certmagic.Storage
|
||||||
if options.DataDirectory != "" {
|
if options.DataDirectory != "" {
|
||||||
|
dataDirectory := filemanager.BasePath(ctx, os.ExpandEnv(options.DataDirectory))
|
||||||
|
err := filemanager.MkdirAll(ctx, dataDirectory, 0o700)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, E.Cause(err, "create ACME data directory")
|
||||||
|
}
|
||||||
storage = &certmagic.FileStorage{
|
storage = &certmagic.FileStorage{
|
||||||
Path: options.DataDirectory,
|
Path: dataDirectory,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
storage = certmagic.Default.Storage
|
storage = certmagic.Default.Storage
|
||||||
|
|||||||
+3
-3
@@ -8,7 +8,6 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -19,6 +18,7 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
"golang.org/x/crypto/cryptobyte"
|
"golang.org/x/crypto/cryptobyte"
|
||||||
@@ -29,7 +29,7 @@ func parseECHClientConfig(ctx context.Context, clientConfig ECHCapableConfig, op
|
|||||||
if len(options.ECH.Config) > 0 {
|
if len(options.ECH.Config) > 0 {
|
||||||
echConfig = []byte(strings.Join(options.ECH.Config, "\n"))
|
echConfig = []byte(strings.Join(options.ECH.Config, "\n"))
|
||||||
} else if options.ECH.ConfigPath != "" {
|
} else if options.ECH.ConfigPath != "" {
|
||||||
content, err := os.ReadFile(options.ECH.ConfigPath)
|
content, err := filemanager.ReadFile(ctx, options.ECH.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read ECH config")
|
return nil, E.Cause(err, "read ECH config")
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions,
|
|||||||
if len(options.ECH.Key) > 0 {
|
if len(options.ECH.Key) > 0 {
|
||||||
echKey = []byte(strings.Join(options.ECH.Key, "\n"))
|
echKey = []byte(strings.Join(options.ECH.Key, "\n"))
|
||||||
} else if options.ECH.KeyPath != "" {
|
} else if options.ECH.KeyPath != "" {
|
||||||
content, err := os.ReadFile(options.ECH.KeyPath)
|
content, err := filemanager.ReadFile(ctx, options.ECH.KeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "read ECH keys")
|
return E.Cause(err, "read ECH keys")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -20,6 +19,7 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
type STDClientConfig struct {
|
type STDClientConfig struct {
|
||||||
@@ -179,7 +179,7 @@ func newSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
} else if options.CertificatePath != "" {
|
} else if options.CertificatePath != "" {
|
||||||
content, err := os.ReadFile(options.CertificatePath)
|
content, err := filemanager.ReadFile(ctx, options.CertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read certificate")
|
return nil, E.Cause(err, "read certificate")
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ func newSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
if len(options.ClientCertificate) > 0 {
|
if len(options.ClientCertificate) > 0 {
|
||||||
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
||||||
} else if options.ClientCertificatePath != "" {
|
} else if options.ClientCertificatePath != "" {
|
||||||
content, err := os.ReadFile(options.ClientCertificatePath)
|
content, err := filemanager.ReadFile(ctx, options.ClientCertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read client certificate")
|
return nil, E.Cause(err, "read client certificate")
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ func newSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
if len(options.ClientKey) > 0 {
|
if len(options.ClientKey) > 0 {
|
||||||
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
||||||
} else if options.ClientKeyPath != "" {
|
} else if options.ClientKeyPath != "" {
|
||||||
content, err := os.ReadFile(options.ClientKeyPath)
|
content, err := filemanager.ReadFile(ctx, options.ClientKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read client key")
|
return nil, E.Cause(err, "read client key")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -20,6 +19,7 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInsecureUnused = E.New("tls: insecure unused")
|
var errInsecureUnused = E.New("tls: insecure unused")
|
||||||
@@ -90,6 +90,7 @@ func getACMENextProtos(provider adapter.CertificateProvider) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type STDServerConfig struct {
|
type STDServerConfig struct {
|
||||||
|
ctx context.Context
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
config *tls.Config
|
config *tls.Config
|
||||||
handshakeTimeout time.Duration
|
handshakeTimeout time.Duration
|
||||||
@@ -260,13 +261,13 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
|||||||
if path == c.certificatePath || path == c.keyPath {
|
if path == c.certificatePath || path == c.keyPath {
|
||||||
switch path {
|
switch path {
|
||||||
case c.certificatePath:
|
case c.certificatePath:
|
||||||
certificate, err := os.ReadFile(c.certificatePath)
|
certificate, err := filemanager.ReadFile(c.ctx, c.certificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "reload certificate from ", c.certificatePath)
|
return E.Cause(err, "reload certificate from ", c.certificatePath)
|
||||||
}
|
}
|
||||||
c.certificate = certificate
|
c.certificate = certificate
|
||||||
case c.keyPath:
|
case c.keyPath:
|
||||||
key, err := os.ReadFile(c.keyPath)
|
key, err := filemanager.ReadFile(c.ctx, c.keyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "reload key from ", c.keyPath)
|
return E.Cause(err, "reload key from ", c.keyPath)
|
||||||
}
|
}
|
||||||
@@ -286,7 +287,7 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
|||||||
clientCertificateCA := x509.NewCertPool()
|
clientCertificateCA := x509.NewCertPool()
|
||||||
var reloaded bool
|
var reloaded bool
|
||||||
for _, certPath := range c.clientCertificatePath {
|
for _, certPath := range c.clientCertificatePath {
|
||||||
content, err := os.ReadFile(certPath)
|
content, err := filemanager.ReadFile(c.ctx, certPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error(E.Cause(err, "reload certificate from ", certPath))
|
c.logger.Error(E.Cause(err, "reload certificate from ", certPath))
|
||||||
continue
|
continue
|
||||||
@@ -307,7 +308,7 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
|||||||
c.access.Unlock()
|
c.access.Unlock()
|
||||||
c.logger.Info("reloaded client certificates")
|
c.logger.Info("reloaded client certificates")
|
||||||
} else if path == c.echKeyPath {
|
} else if path == c.echKeyPath {
|
||||||
echKey, err := os.ReadFile(c.echKeyPath)
|
echKey, err := filemanager.ReadFile(c.ctx, c.echKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "reload ECH keys from ", c.echKeyPath)
|
return E.Cause(err, "reload ECH keys from ", c.echKeyPath)
|
||||||
}
|
}
|
||||||
@@ -405,7 +406,7 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
} else if options.CertificatePath != "" {
|
} else if options.CertificatePath != "" {
|
||||||
content, err := os.ReadFile(options.CertificatePath)
|
content, err := filemanager.ReadFile(ctx, options.CertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read certificate")
|
return nil, E.Cause(err, "read certificate")
|
||||||
}
|
}
|
||||||
@@ -414,7 +415,7 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
if len(options.Key) > 0 {
|
if len(options.Key) > 0 {
|
||||||
key = []byte(strings.Join(options.Key, "\n"))
|
key = []byte(strings.Join(options.Key, "\n"))
|
||||||
} else if options.KeyPath != "" {
|
} else if options.KeyPath != "" {
|
||||||
content, err := os.ReadFile(options.KeyPath)
|
content, err := filemanager.ReadFile(ctx, options.KeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read key")
|
return nil, E.Cause(err, "read key")
|
||||||
}
|
}
|
||||||
@@ -457,7 +458,7 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
} else if len(options.ClientCertificatePath) > 0 {
|
} else if len(options.ClientCertificatePath) > 0 {
|
||||||
clientCertificateCA := x509.NewCertPool()
|
clientCertificateCA := x509.NewCertPool()
|
||||||
for _, path := range options.ClientCertificatePath {
|
for _, path := range options.ClientCertificatePath {
|
||||||
content, err := os.ReadFile(path)
|
content, err := filemanager.ReadFile(ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read client certificate from ", path)
|
return nil, E.Cause(err, "read client certificate from ", path)
|
||||||
}
|
}
|
||||||
@@ -494,6 +495,7 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
handshakeTimeout = C.TCPTimeout
|
handshakeTimeout = C.TCPTimeout
|
||||||
}
|
}
|
||||||
serverConfig := &STDServerConfig{
|
serverConfig := &STDServerConfig{
|
||||||
|
ctx: ctx,
|
||||||
config: tlsConfig,
|
config: tlsConfig,
|
||||||
handshakeTimeout: handshakeTimeout,
|
handshakeTimeout: handshakeTimeout,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,6 +10,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SystemTLSValidated struct {
|
type SystemTLSValidated struct {
|
||||||
@@ -89,7 +89,7 @@ func resolveSystemAnchors(ctx context.Context, options option.OutboundTLSOptions
|
|||||||
return []byte(strings.Join(options.Certificate, "\n")), true, nil, nil
|
return []byte(strings.Join(options.Certificate, "\n")), true, nil, nil
|
||||||
}
|
}
|
||||||
if options.CertificatePath != "" {
|
if options.CertificatePath != "" {
|
||||||
content, err := os.ReadFile(options.CertificatePath)
|
content, err := filemanager.ReadFile(ctx, options.CertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, nil, E.Cause(err, "read certificate")
|
return nil, false, nil, E.Cause(err, "read certificate")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -21,6 +20,7 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
utls "github.com/metacubex/utls"
|
utls "github.com/metacubex/utls"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
@@ -251,7 +251,7 @@ func newUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
} else if options.CertificatePath != "" {
|
} else if options.CertificatePath != "" {
|
||||||
content, err := os.ReadFile(options.CertificatePath)
|
content, err := filemanager.ReadFile(ctx, options.CertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read certificate")
|
return nil, E.Cause(err, "read certificate")
|
||||||
}
|
}
|
||||||
@@ -268,7 +268,7 @@ func newUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
if len(options.ClientCertificate) > 0 {
|
if len(options.ClientCertificate) > 0 {
|
||||||
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
||||||
} else if options.ClientCertificatePath != "" {
|
} else if options.ClientCertificatePath != "" {
|
||||||
content, err := os.ReadFile(options.ClientCertificatePath)
|
content, err := filemanager.ReadFile(ctx, options.ClientCertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read client certificate")
|
return nil, E.Cause(err, "read client certificate")
|
||||||
}
|
}
|
||||||
@@ -278,7 +278,7 @@ func newUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
if len(options.ClientKey) > 0 {
|
if len(options.ClientKey) > 0 {
|
||||||
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
||||||
} else if options.ClientKeyPath != "" {
|
} else if options.ClientKeyPath != "" {
|
||||||
content, err := os.ReadFile(options.ClientKeyPath)
|
content, err := filemanager.ReadFile(ctx, options.ClientKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read client key")
|
return nil, E.Cause(err, "read client key")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package daemon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
func UnaryErrorInterceptor(ctx context.Context, request any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
|
||||||
response, err := handler(ctx, request)
|
|
||||||
if err != nil {
|
|
||||||
return nil, mapStatusError(err)
|
|
||||||
}
|
|
||||||
return response, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func StreamErrorInterceptor(server any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
||||||
err := handler(server, stream)
|
|
||||||
if err != nil {
|
|
||||||
return mapStatusError(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapStatusError(err error) error {
|
|
||||||
if _, loaded := status.FromError(err); loaded {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, os.ErrInvalid):
|
|
||||||
return status.Error(codes.FailedPrecondition, "service not started")
|
|
||||||
case errors.Is(err, os.ErrClosed):
|
|
||||||
return status.Error(codes.Unavailable, "service is closing")
|
|
||||||
case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
|
|
||||||
return status.FromContextError(err).Err()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
+2
-2
@@ -15,8 +15,8 @@ import (
|
|||||||
|
|
||||||
func NewServer(startedService *StartedService, secret string) *grpc.Server {
|
func NewServer(startedService *StartedService, secret string) *grpc.Server {
|
||||||
server := grpc.NewServer(
|
server := grpc.NewServer(
|
||||||
grpc.ChainUnaryInterceptor(newUnaryAuthInterceptor(secret), UnaryErrorInterceptor),
|
grpc.ChainUnaryInterceptor(newUnaryAuthInterceptor(secret)),
|
||||||
grpc.ChainStreamInterceptor(newStreamAuthInterceptor(secret), StreamErrorInterceptor),
|
grpc.ChainStreamInterceptor(newStreamAuthInterceptor(secret)),
|
||||||
)
|
)
|
||||||
healthServer := health.NewServer()
|
healthServer := health.NewServer()
|
||||||
RegisterStartedServiceServer(server, startedService)
|
RegisterStartedServiceServer(server, startedService)
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ func (s *StartedService) StartOrReloadService(profileContent string, options *Ov
|
|||||||
}
|
}
|
||||||
oldInstance := s.instance
|
oldInstance := s.instance
|
||||||
if oldInstance != nil {
|
if oldInstance != nil {
|
||||||
|
s.instance = nil
|
||||||
s.updateStatus(ServiceStatus_STOPPING)
|
s.updateStatus(ServiceStatus_STOPPING)
|
||||||
s.serviceAccess.Unlock()
|
s.serviceAccess.Unlock()
|
||||||
_ = oldInstance.Close()
|
_ = oldInstance.Close()
|
||||||
@@ -221,6 +222,8 @@ func (s *StartedService) StartOrReloadService(profileContent string, options *Ov
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
s.instance = nil
|
||||||
|
_ = instance.Close()
|
||||||
return s.updateStatusError(err)
|
return s.updateStatusError(err)
|
||||||
}
|
}
|
||||||
s.startedAt = time.Now()
|
s.startedAt = time.Now()
|
||||||
@@ -243,16 +246,13 @@ func (s *StartedService) CloseService() error {
|
|||||||
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
default:
|
default:
|
||||||
s.serviceAccess.Unlock()
|
s.serviceAccess.Unlock()
|
||||||
return os.ErrInvalid
|
return nil
|
||||||
}
|
}
|
||||||
s.updateStatus(ServiceStatus_STOPPING)
|
s.updateStatus(ServiceStatus_STOPPING)
|
||||||
instance := s.instance
|
instance := s.instance
|
||||||
s.instance = nil
|
s.instance = nil
|
||||||
if instance != nil {
|
if instance != nil {
|
||||||
err := instance.Close()
|
_ = instance.Close()
|
||||||
if err != nil {
|
|
||||||
return s.updateStatusError(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
s.startedAt = time.Time{}
|
s.startedAt = time.Time{}
|
||||||
s.updateStatus(ServiceStatus_IDLE)
|
s.updateStatus(ServiceStatus_IDLE)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func (s *StartedService) ProvideUSBDevices(server grpc.BidiStreamingServer[USBPr
|
|||||||
instance := s.instance
|
instance := s.instance
|
||||||
s.serviceAccess.RUnlock()
|
s.serviceAccess.RUnlock()
|
||||||
if instance == nil {
|
if instance == nil {
|
||||||
return E.New("service not started")
|
return nil
|
||||||
}
|
}
|
||||||
serviceManager := service.FromContext[adapter.ServiceManager](instance.ctx)
|
serviceManager := service.FromContext[adapter.ServiceManager](instance.ctx)
|
||||||
if serviceManager == nil {
|
if serviceManager == nil {
|
||||||
@@ -122,7 +122,7 @@ func (s *StartedService) SubscribeUSBIPServerStatus(
|
|||||||
instance := s.instance
|
instance := s.instance
|
||||||
s.serviceAccess.RUnlock()
|
s.serviceAccess.RUnlock()
|
||||||
if instance == nil {
|
if instance == nil {
|
||||||
return E.New("service not started")
|
return nil
|
||||||
}
|
}
|
||||||
serviceManager := service.FromContext[adapter.ServiceManager](instance.ctx)
|
serviceManager := service.FromContext[adapter.ServiceManager](instance.ctx)
|
||||||
if serviceManager == nil {
|
if serviceManager == nil {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
|||||||
files = append(files, defaultFile)
|
files = append(files, defaultFile)
|
||||||
} else {
|
} else {
|
||||||
for _, path := range options.Path {
|
for _, path := range options.Path {
|
||||||
files = append(files, NewFile(filemanager.BasePath(ctx, os.ExpandEnv(path))))
|
files = append(files, NewFile(ctx, filemanager.BasePath(ctx, os.ExpandEnv(path))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.Predefined != nil {
|
if options.Predefined != nil {
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ package hosts
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
@@ -18,6 +19,7 @@ import (
|
|||||||
const cacheMaxAge = 5 * time.Second
|
const cacheMaxAge = 5 * time.Second
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
|
ctx context.Context
|
||||||
path string
|
path string
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
byName map[string][]netip.Addr
|
byName map[string][]netip.Addr
|
||||||
@@ -26,8 +28,9 @@ type File struct {
|
|||||||
size int64
|
size int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFile(path string) *File {
|
func NewFile(ctx context.Context, path string) *File {
|
||||||
return &File{
|
return &File{
|
||||||
|
ctx: ctx,
|
||||||
path: path,
|
path: path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +40,7 @@ func NewDefault() (*File, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "resolve default hosts path")
|
return nil, E.Cause(err, "resolve default hosts path")
|
||||||
}
|
}
|
||||||
return NewFile(defaultPathResolved), nil
|
return NewFile(context.Background(), defaultPathResolved), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) Lookup(name string) []netip.Addr {
|
func (f *File) Lookup(name string) []netip.Addr {
|
||||||
@@ -52,7 +55,7 @@ func (f *File) update() {
|
|||||||
if now.Before(f.expire) && len(f.byName) > 0 {
|
if now.Before(f.expire) && len(f.byName) > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stat, err := os.Stat(f.path)
|
stat, err := filemanager.Stat(f.ctx, f.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -61,7 +64,7 @@ func (f *File) update() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
byName := make(map[string][]netip.Addr)
|
byName := make(map[string][]netip.Addr)
|
||||||
file, err := os.Open(f.path)
|
file, err := filemanager.Open(f.ctx, f.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package hosts
|
package hosts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
|
|
||||||
func TestHosts(t *testing.T) {
|
func TestHosts(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, NewFile("testdata/hosts").Lookup("localhost"))
|
require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, NewFile(context.Background(), "testdata/hosts").Lookup("localhost"))
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
defaultPathResolved, err := defaultPath()
|
defaultPathResolved, err := defaultPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -21,7 +22,7 @@ func TestHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
content, readErr := os.ReadFile(defaultPathResolved)
|
content, readErr := os.ReadFile(defaultPathResolved)
|
||||||
require.NoError(t, readErr)
|
require.NoError(t, readErr)
|
||||||
hFile := NewFile(defaultPathResolved)
|
hFile := NewFile(context.Background(), defaultPathResolved)
|
||||||
if len(hFile.Lookup("localhost")) == 0 {
|
if len(hFile.Lookup("localhost")) == 0 {
|
||||||
t.Fatal("failed to resolve localhost: ", defaultPathResolved, ": \n", string(content))
|
t.Fatal("failed to resolve localhost: ", defaultPathResolved, ": \n", string(content))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -48,6 +49,18 @@ var commandServiceUninstall = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var commandServiceSetInsecureMode = &cobra.Command{
|
||||||
|
Use: "set-insecure-mode <enabled>",
|
||||||
|
Short: "Set whether configurations may use privileges unrelated to networking",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
err := serviceSetInsecureMode(args[0])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(E.Cause(err, "set insecure mode"))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func addPlatformServiceCommands() {
|
func addPlatformServiceCommands() {
|
||||||
commandServiceInstall.Flags().BoolVar(
|
commandServiceInstall.Flags().BoolVar(
|
||||||
&commandServiceFlagAllowUnsafeInstallation,
|
&commandServiceFlagAllowUnsafeInstallation,
|
||||||
@@ -57,6 +70,57 @@ func addPlatformServiceCommands() {
|
|||||||
)
|
)
|
||||||
commandService.AddCommand(commandServiceInstall)
|
commandService.AddCommand(commandServiceInstall)
|
||||||
commandService.AddCommand(commandServiceUninstall)
|
commandService.AddCommand(commandServiceUninstall)
|
||||||
|
commandService.AddCommand(commandServiceSetInsecureMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func serviceSetInsecureMode(value string) error {
|
||||||
|
enabled, err := strconv.ParseBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "parse value")
|
||||||
|
}
|
||||||
|
if !windows.GetCurrentProcessToken().IsElevated() {
|
||||||
|
return E.New("setting insecure mode requires an elevated process")
|
||||||
|
}
|
||||||
|
directory, err := installedServiceWorkingDirectory()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serviceUserID, err := windowsServiceSID()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "create daemon service SID")
|
||||||
|
}
|
||||||
|
err = validateProtectedWindowsWorkingDirectory(directory, serviceUserID)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "validate working directory")
|
||||||
|
}
|
||||||
|
return saveSecuritySettings(directory, securitySettings{InsecureModeEnabled: enabled})
|
||||||
|
}
|
||||||
|
|
||||||
|
func installedServiceWorkingDirectory() (string, error) {
|
||||||
|
manager, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return "", E.Cause(err, "connect to service manager")
|
||||||
|
}
|
||||||
|
defer manager.Disconnect()
|
||||||
|
service, err := manager.OpenService(serviceName)
|
||||||
|
if err != nil {
|
||||||
|
return "", E.Cause(err, "open service")
|
||||||
|
}
|
||||||
|
defer service.Close()
|
||||||
|
config, err := service.Config()
|
||||||
|
if err != nil {
|
||||||
|
return "", E.Cause(err, "query service config")
|
||||||
|
}
|
||||||
|
arguments, err := windows.DecomposeCommandLine(config.BinaryPathName)
|
||||||
|
if err != nil {
|
||||||
|
return "", E.Cause(err, "parse service command line")
|
||||||
|
}
|
||||||
|
for index, argument := range arguments {
|
||||||
|
if argument == "--working-directory" && index+1 < len(arguments) {
|
||||||
|
return arguments[index+1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", E.New("missing working directory in the service configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
func serviceInstall() error {
|
func serviceInstall() error {
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ func runWorker() error {
|
|||||||
}
|
}
|
||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
server := grpc.NewServer(
|
server := grpc.NewServer(
|
||||||
grpc.ChainUnaryInterceptor(daemon.UnaryErrorInterceptor),
|
grpc.ChainUnaryInterceptor(unaryLocaleInterceptor),
|
||||||
grpc.ChainStreamInterceptor(daemon.StreamErrorInterceptor),
|
grpc.ChainStreamInterceptor(streamLocaleInterceptor),
|
||||||
)
|
)
|
||||||
RegisterApplicationServiceServer(server, &applicationService{
|
RegisterApplicationServiceServer(server, &applicationService{
|
||||||
startedService: daemon.NewStartedService(daemon.ServiceOptions{Context: include.Context(context.Background())}),
|
startedService: daemon.NewStartedService(daemon.ServiceOptions{Context: include.Context(context.Background())}),
|
||||||
|
|||||||
@@ -196,18 +196,69 @@ func (s *desktopService) TakeOverService(ctx context.Context, empty *emptypb.Emp
|
|||||||
return &emptypb.Empty{}, nil
|
return &emptypb.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *desktopService) GetSecuritySettings(ctx context.Context, empty *emptypb.Empty) (*SecuritySettings, error) {
|
||||||
|
_, err := peerIdentityFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !insecureModeAvailable() {
|
||||||
|
return &SecuritySettings{}, nil
|
||||||
|
}
|
||||||
|
return &SecuritySettings{
|
||||||
|
Available: true,
|
||||||
|
InsecureModeEnabled: s.daemon.insecureModeEnabled(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *desktopService) SetInsecureModeEnabled(ctx context.Context, request *SetInsecureModeEnabledRequest) (*emptypb.Empty, error) {
|
||||||
|
_, err := peerIdentityFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !insecureModeAvailable() {
|
||||||
|
return nil, status.Error(codes.FailedPrecondition, "insecure mode is not available on this platform")
|
||||||
|
}
|
||||||
|
if request.Enabled {
|
||||||
|
return nil, status.Error(codes.PermissionDenied, "enabling insecure mode requires an elevated service command")
|
||||||
|
}
|
||||||
|
s.daemon.lifecycleAccess.Lock()
|
||||||
|
defer s.daemon.lifecycleAccess.Unlock()
|
||||||
|
if s.daemon.closed {
|
||||||
|
return nil, os.ErrClosed
|
||||||
|
}
|
||||||
|
wasEnabled := s.daemon.insecureModeEnabled()
|
||||||
|
err = saveSecuritySettings(workingDirectory, securitySettings{InsecureModeEnabled: false})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if wasEnabled && s.daemon.startedService.Instance() != nil {
|
||||||
|
var ownerUserID string
|
||||||
|
ownerUserID, err = loadOwner()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = s.daemon.stopServiceLocked(ownerUserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Daemon) cleanFailedStartLocked(ownerUserID string, options startOptions, startError error) error {
|
func (d *Daemon) cleanFailedStartLocked(ownerUserID string, options startOptions, startError error) error {
|
||||||
var platformError error
|
var platformError error
|
||||||
if d.platform != nil {
|
if d.platform != nil {
|
||||||
platformError = d.platform.ResetPlatformOptions()
|
platformError = d.platform.ResetPlatformOptions()
|
||||||
}
|
}
|
||||||
closeError := d.startedService.CloseService()
|
if d.startedService.Instance() != nil {
|
||||||
|
_ = d.startedService.CloseService()
|
||||||
|
}
|
||||||
directory := userWorkingDirectory(ownerUserID)
|
directory := userWorkingDirectory(ownerUserID)
|
||||||
crashReportError := tagUnownedReports(filepath.Join(directory, crashReportsDirectoryName), ownerUserID)
|
crashReportError := tagUnownedReports(filepath.Join(directory, crashReportsDirectoryName), ownerUserID)
|
||||||
oomReportError := tagUnownedReports(filepath.Join(directory, oomReportsDirectoryName), ownerUserID)
|
oomReportError := tagUnownedReports(filepath.Join(directory, oomReportsDirectoryName), ownerUserID)
|
||||||
options.WasRunning = false
|
options.WasRunning = false
|
||||||
snapshotError := saveStartOptions(ownerUserID, options)
|
snapshotError := saveStartOptions(ownerUserID, options)
|
||||||
return E.Errors(startError, platformError, closeError, crashReportError, oomReportError, snapshotError)
|
return E.Errors(startError, platformError, crashReportError, oomReportError, snapshotError)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *desktopService) GetWorkingDirectory(ctx context.Context, empty *emptypb.Empty) (*WorkingDirectoryInfo, error) {
|
func (s *desktopService) GetWorkingDirectory(ctx context.Context, empty *emptypb.Empty) (*WorkingDirectoryInfo, error) {
|
||||||
|
|||||||
@@ -1424,6 +1424,102 @@ func (x *OOMReportFile) GetIsProfile() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SecuritySettings struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Available bool `protobuf:"varint,1,opt,name=available,proto3" json:"available,omitempty"`
|
||||||
|
InsecureModeEnabled bool `protobuf:"varint,2,opt,name=insecure_mode_enabled,json=insecureModeEnabled,proto3" json:"insecure_mode_enabled,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SecuritySettings) Reset() {
|
||||||
|
*x = SecuritySettings{}
|
||||||
|
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[23]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SecuritySettings) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SecuritySettings) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SecuritySettings) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[23]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use SecuritySettings.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SecuritySettings) Descriptor() ([]byte, []int) {
|
||||||
|
return file_experimental_boxdd_desktop_service_proto_rawDescGZIP(), []int{23}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SecuritySettings) GetAvailable() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Available
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SecuritySettings) GetInsecureModeEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.InsecureModeEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetInsecureModeEnabledRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SetInsecureModeEnabledRequest) Reset() {
|
||||||
|
*x = SetInsecureModeEnabledRequest{}
|
||||||
|
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[24]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SetInsecureModeEnabledRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SetInsecureModeEnabledRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SetInsecureModeEnabledRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[24]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use SetInsecureModeEnabledRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SetInsecureModeEnabledRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_experimental_boxdd_desktop_service_proto_rawDescGZIP(), []int{24}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SetInsecureModeEnabledRequest) GetEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Enabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type InstallUpdateRequest struct {
|
type InstallUpdateRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
InstallerPath string `protobuf:"bytes,1,opt,name=installer_path,json=installerPath,proto3" json:"installer_path,omitempty"`
|
InstallerPath string `protobuf:"bytes,1,opt,name=installer_path,json=installerPath,proto3" json:"installer_path,omitempty"`
|
||||||
@@ -1433,7 +1529,7 @@ type InstallUpdateRequest struct {
|
|||||||
|
|
||||||
func (x *InstallUpdateRequest) Reset() {
|
func (x *InstallUpdateRequest) Reset() {
|
||||||
*x = InstallUpdateRequest{}
|
*x = InstallUpdateRequest{}
|
||||||
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[23]
|
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[25]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1445,7 +1541,7 @@ func (x *InstallUpdateRequest) String() string {
|
|||||||
func (*InstallUpdateRequest) ProtoMessage() {}
|
func (*InstallUpdateRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *InstallUpdateRequest) ProtoReflect() protoreflect.Message {
|
func (x *InstallUpdateRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[23]
|
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[25]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1458,7 +1554,7 @@ func (x *InstallUpdateRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use InstallUpdateRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use InstallUpdateRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*InstallUpdateRequest) Descriptor() ([]byte, []int) {
|
func (*InstallUpdateRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_experimental_boxdd_desktop_service_proto_rawDescGZIP(), []int{23}
|
return file_experimental_boxdd_desktop_service_proto_rawDescGZIP(), []int{25}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *InstallUpdateRequest) GetInstallerPath() string {
|
func (x *InstallUpdateRequest) GetInstallerPath() string {
|
||||||
@@ -1477,7 +1573,7 @@ type InstallUpdateResponse struct {
|
|||||||
|
|
||||||
func (x *InstallUpdateResponse) Reset() {
|
func (x *InstallUpdateResponse) Reset() {
|
||||||
*x = InstallUpdateResponse{}
|
*x = InstallUpdateResponse{}
|
||||||
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[24]
|
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[26]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1489,7 +1585,7 @@ func (x *InstallUpdateResponse) String() string {
|
|||||||
func (*InstallUpdateResponse) ProtoMessage() {}
|
func (*InstallUpdateResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *InstallUpdateResponse) ProtoReflect() protoreflect.Message {
|
func (x *InstallUpdateResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[24]
|
mi := &file_experimental_boxdd_desktop_service_proto_msgTypes[26]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1502,7 +1598,7 @@ func (x *InstallUpdateResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use InstallUpdateResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use InstallUpdateResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*InstallUpdateResponse) Descriptor() ([]byte, []int) {
|
func (*InstallUpdateResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_experimental_boxdd_desktop_service_proto_rawDescGZIP(), []int{24}
|
return file_experimental_boxdd_desktop_service_proto_rawDescGZIP(), []int{26}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *InstallUpdateResponse) GetResult() InstallUpdateResult {
|
func (x *InstallUpdateResponse) GetResult() InstallUpdateResult {
|
||||||
@@ -1606,7 +1702,12 @@ const file_experimental_boxdd_desktop_service_proto_rawDesc = "" +
|
|||||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" +
|
"\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" +
|
||||||
"\acontent\x18\x02 \x01(\fR\acontent\x12\x1d\n" +
|
"\acontent\x18\x02 \x01(\fR\acontent\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"is_profile\x18\x03 \x01(\bR\tisProfile\"=\n" +
|
"is_profile\x18\x03 \x01(\bR\tisProfile\"d\n" +
|
||||||
|
"\x10SecuritySettings\x12\x1c\n" +
|
||||||
|
"\tavailable\x18\x01 \x01(\bR\tavailable\x122\n" +
|
||||||
|
"\x15insecure_mode_enabled\x18\x02 \x01(\bR\x13insecureModeEnabled\"9\n" +
|
||||||
|
"\x1dSetInsecureModeEnabledRequest\x12\x18\n" +
|
||||||
|
"\aenabled\x18\x01 \x01(\bR\aenabled\"=\n" +
|
||||||
"\x14InstallUpdateRequest\x12%\n" +
|
"\x14InstallUpdateRequest\x12%\n" +
|
||||||
"\x0einstaller_path\x18\x01 \x01(\tR\rinstallerPath\"M\n" +
|
"\x0einstaller_path\x18\x01 \x01(\tR\rinstallerPath\"M\n" +
|
||||||
"\x15InstallUpdateResponse\x124\n" +
|
"\x15InstallUpdateResponse\x124\n" +
|
||||||
@@ -1620,7 +1721,7 @@ const file_experimental_boxdd_desktop_service_proto_rawDesc = "" +
|
|||||||
"!INSTALL_UPDATE_RESULT_UNSPECIFIED\x10\x00\x12!\n" +
|
"!INSTALL_UPDATE_RESULT_UNSPECIFIED\x10\x00\x12!\n" +
|
||||||
"\x1dINSTALL_UPDATE_RESULT_STARTED\x10\x01\x12)\n" +
|
"\x1dINSTALL_UPDATE_RESULT_STARTED\x10\x01\x12)\n" +
|
||||||
"%INSTALL_UPDATE_RESULT_SIGNER_MISMATCH\x10\x02\x12#\n" +
|
"%INSTALL_UPDATE_RESULT_SIGNER_MISMATCH\x10\x02\x12#\n" +
|
||||||
"\x1fINSTALL_UPDATE_RESULT_NOT_NEWER\x10\x032\x9c\v\n" +
|
"\x1fINSTALL_UPDATE_RESULT_NOT_NEWER\x10\x032\xc4\f\n" +
|
||||||
"\x0eDesktopService\x12>\n" +
|
"\x0eDesktopService\x12>\n" +
|
||||||
"\rGetDaemonInfo\x12\x16.google.protobuf.Empty\x1a\x13.desktop.DaemonInfo\"\x00\x12@\n" +
|
"\rGetDaemonInfo\x12\x16.google.protobuf.Empty\x1a\x13.desktop.DaemonInfo\"\x00\x12@\n" +
|
||||||
"\fClaimService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12C\n" +
|
"\fClaimService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12C\n" +
|
||||||
@@ -1640,7 +1741,9 @@ const file_experimental_boxdd_desktop_service_proto_rawDesc = "" +
|
|||||||
"\x0fExportOOMReport\x12\x1f.desktop.OOMReportExportRequest\x1a\x1b.desktop.CrashReportArchive\"\x00\x12F\n" +
|
"\x0fExportOOMReport\x12\x1f.desktop.OOMReportExportRequest\x1a\x1b.desktop.CrashReportArchive\"\x00\x12F\n" +
|
||||||
"\x0fDeleteOOMReport\x12\x19.desktop.OOMReportRequest\x1a\x16.google.protobuf.Empty\"\x00\x12G\n" +
|
"\x0fDeleteOOMReport\x12\x19.desktop.OOMReportRequest\x1a\x16.google.protobuf.Empty\"\x00\x12G\n" +
|
||||||
"\x13DeleteAllOOMReports\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12P\n" +
|
"\x13DeleteAllOOMReports\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12P\n" +
|
||||||
"\rInstallUpdate\x12\x1d.desktop.InstallUpdateRequest\x1a\x1e.desktop.InstallUpdateResponse\"\x002\xbd\x04\n" +
|
"\rInstallUpdate\x12\x1d.desktop.InstallUpdateRequest\x1a\x1e.desktop.InstallUpdateResponse\"\x00\x12J\n" +
|
||||||
|
"\x13GetSecuritySettings\x12\x16.google.protobuf.Empty\x1a\x19.desktop.SecuritySettings\"\x00\x12Z\n" +
|
||||||
|
"\x16SetInsecureModeEnabled\x12&.desktop.SetInsecureModeEnabledRequest\x1a\x16.google.protobuf.Empty\"\x002\xbd\x04\n" +
|
||||||
"\x12ApplicationService\x12?\n" +
|
"\x12ApplicationService\x12?\n" +
|
||||||
"\vCheckConfig\x12\x16.desktop.ConfigContent\x1a\x16.google.protobuf.Empty\"\x00\x12@\n" +
|
"\vCheckConfig\x12\x16.desktop.ConfigContent\x1a\x16.google.protobuf.Empty\"\x00\x12@\n" +
|
||||||
"\fFormatConfig\x12\x16.desktop.ConfigContent\x1a\x16.desktop.ConfigContent\"\x00\x12@\n" +
|
"\fFormatConfig\x12\x16.desktop.ConfigContent\x1a\x16.desktop.ConfigContent\"\x00\x12@\n" +
|
||||||
@@ -1664,7 +1767,7 @@ func file_experimental_boxdd_desktop_service_proto_rawDescGZIP() []byte {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
file_experimental_boxdd_desktop_service_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
file_experimental_boxdd_desktop_service_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||||
file_experimental_boxdd_desktop_service_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
|
file_experimental_boxdd_desktop_service_proto_msgTypes = make([]protoimpl.MessageInfo, 27)
|
||||||
file_experimental_boxdd_desktop_service_proto_goTypes = []any{
|
file_experimental_boxdd_desktop_service_proto_goTypes = []any{
|
||||||
(DaemonOwnership)(0), // 0: desktop.DaemonOwnership
|
(DaemonOwnership)(0), // 0: desktop.DaemonOwnership
|
||||||
(InstallUpdateResult)(0), // 1: desktop.InstallUpdateResult
|
(InstallUpdateResult)(0), // 1: desktop.InstallUpdateResult
|
||||||
@@ -1692,11 +1795,13 @@ var (
|
|||||||
(*OOMReportExportRequest)(nil), // 23: desktop.OOMReportExportRequest
|
(*OOMReportExportRequest)(nil), // 23: desktop.OOMReportExportRequest
|
||||||
(*OOMReportContent)(nil), // 24: desktop.OOMReportContent
|
(*OOMReportContent)(nil), // 24: desktop.OOMReportContent
|
||||||
(*OOMReportFile)(nil), // 25: desktop.OOMReportFile
|
(*OOMReportFile)(nil), // 25: desktop.OOMReportFile
|
||||||
(*InstallUpdateRequest)(nil), // 26: desktop.InstallUpdateRequest
|
(*SecuritySettings)(nil), // 26: desktop.SecuritySettings
|
||||||
(*InstallUpdateResponse)(nil), // 27: desktop.InstallUpdateResponse
|
(*SetInsecureModeEnabledRequest)(nil), // 27: desktop.SetInsecureModeEnabledRequest
|
||||||
(*emptypb.Empty)(nil), // 28: google.protobuf.Empty
|
(*InstallUpdateRequest)(nil), // 28: desktop.InstallUpdateRequest
|
||||||
(*daemon.NetworkQualityTestProgress)(nil), // 29: daemon.NetworkQualityTestProgress
|
(*InstallUpdateResponse)(nil), // 29: desktop.InstallUpdateResponse
|
||||||
(*daemon.STUNTestProgress)(nil), // 30: daemon.STUNTestProgress
|
(*emptypb.Empty)(nil), // 30: google.protobuf.Empty
|
||||||
|
(*daemon.NetworkQualityTestProgress)(nil), // 31: daemon.NetworkQualityTestProgress
|
||||||
|
(*daemon.STUNTestProgress)(nil), // 32: daemon.STUNTestProgress
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1709,60 +1814,64 @@ var file_experimental_boxdd_desktop_service_proto_depIdxs = []int32{
|
|||||||
21, // 5: desktop.OOMReportList.reports:type_name -> desktop.OOMReportEntry
|
21, // 5: desktop.OOMReportList.reports:type_name -> desktop.OOMReportEntry
|
||||||
25, // 6: desktop.OOMReportContent.files:type_name -> desktop.OOMReportFile
|
25, // 6: desktop.OOMReportContent.files:type_name -> desktop.OOMReportFile
|
||||||
1, // 7: desktop.InstallUpdateResponse.result:type_name -> desktop.InstallUpdateResult
|
1, // 7: desktop.InstallUpdateResponse.result:type_name -> desktop.InstallUpdateResult
|
||||||
28, // 8: desktop.DesktopService.GetDaemonInfo:input_type -> google.protobuf.Empty
|
30, // 8: desktop.DesktopService.GetDaemonInfo:input_type -> google.protobuf.Empty
|
||||||
28, // 9: desktop.DesktopService.ClaimService:input_type -> google.protobuf.Empty
|
30, // 9: desktop.DesktopService.ClaimService:input_type -> google.protobuf.Empty
|
||||||
28, // 10: desktop.DesktopService.TakeOverService:input_type -> google.protobuf.Empty
|
30, // 10: desktop.DesktopService.TakeOverService:input_type -> google.protobuf.Empty
|
||||||
7, // 11: desktop.DesktopService.StartService:input_type -> desktop.StartServiceRequest
|
7, // 11: desktop.DesktopService.StartService:input_type -> desktop.StartServiceRequest
|
||||||
28, // 12: desktop.DesktopService.GetWorkingDirectory:input_type -> google.protobuf.Empty
|
30, // 12: desktop.DesktopService.GetWorkingDirectory:input_type -> google.protobuf.Empty
|
||||||
28, // 13: desktop.DesktopService.DestroyWorkingDirectory:input_type -> google.protobuf.Empty
|
30, // 13: desktop.DesktopService.DestroyWorkingDirectory:input_type -> google.protobuf.Empty
|
||||||
28, // 14: desktop.DesktopService.ListCrashReports:input_type -> google.protobuf.Empty
|
30, // 14: desktop.DesktopService.ListCrashReports:input_type -> google.protobuf.Empty
|
||||||
15, // 15: desktop.DesktopService.ReadCrashReport:input_type -> desktop.CrashReportRequest
|
15, // 15: desktop.DesktopService.ReadCrashReport:input_type -> desktop.CrashReportRequest
|
||||||
15, // 16: desktop.DesktopService.MarkCrashReportRead:input_type -> desktop.CrashReportRequest
|
15, // 16: desktop.DesktopService.MarkCrashReportRead:input_type -> desktop.CrashReportRequest
|
||||||
16, // 17: desktop.DesktopService.ExportCrashReport:input_type -> desktop.CrashReportExportRequest
|
16, // 17: desktop.DesktopService.ExportCrashReport:input_type -> desktop.CrashReportExportRequest
|
||||||
15, // 18: desktop.DesktopService.DeleteCrashReport:input_type -> desktop.CrashReportRequest
|
15, // 18: desktop.DesktopService.DeleteCrashReport:input_type -> desktop.CrashReportRequest
|
||||||
28, // 19: desktop.DesktopService.DeleteAllCrashReports:input_type -> google.protobuf.Empty
|
30, // 19: desktop.DesktopService.DeleteAllCrashReports:input_type -> google.protobuf.Empty
|
||||||
28, // 20: desktop.DesktopService.ListOOMReports:input_type -> google.protobuf.Empty
|
30, // 20: desktop.DesktopService.ListOOMReports:input_type -> google.protobuf.Empty
|
||||||
22, // 21: desktop.DesktopService.ReadOOMReport:input_type -> desktop.OOMReportRequest
|
22, // 21: desktop.DesktopService.ReadOOMReport:input_type -> desktop.OOMReportRequest
|
||||||
22, // 22: desktop.DesktopService.MarkOOMReportRead:input_type -> desktop.OOMReportRequest
|
22, // 22: desktop.DesktopService.MarkOOMReportRead:input_type -> desktop.OOMReportRequest
|
||||||
23, // 23: desktop.DesktopService.ExportOOMReport:input_type -> desktop.OOMReportExportRequest
|
23, // 23: desktop.DesktopService.ExportOOMReport:input_type -> desktop.OOMReportExportRequest
|
||||||
22, // 24: desktop.DesktopService.DeleteOOMReport:input_type -> desktop.OOMReportRequest
|
22, // 24: desktop.DesktopService.DeleteOOMReport:input_type -> desktop.OOMReportRequest
|
||||||
28, // 25: desktop.DesktopService.DeleteAllOOMReports:input_type -> google.protobuf.Empty
|
30, // 25: desktop.DesktopService.DeleteAllOOMReports:input_type -> google.protobuf.Empty
|
||||||
26, // 26: desktop.DesktopService.InstallUpdate:input_type -> desktop.InstallUpdateRequest
|
28, // 26: desktop.DesktopService.InstallUpdate:input_type -> desktop.InstallUpdateRequest
|
||||||
9, // 27: desktop.ApplicationService.CheckConfig:input_type -> desktop.ConfigContent
|
30, // 27: desktop.DesktopService.GetSecuritySettings:input_type -> google.protobuf.Empty
|
||||||
9, // 28: desktop.ApplicationService.FormatConfig:input_type -> desktop.ConfigContent
|
27, // 28: desktop.DesktopService.SetInsecureModeEnabled:input_type -> desktop.SetInsecureModeEnabledRequest
|
||||||
10, // 29: desktop.ApplicationService.EncodeProfile:input_type -> desktop.ProfileContent
|
9, // 29: desktop.ApplicationService.CheckConfig:input_type -> desktop.ConfigContent
|
||||||
11, // 30: desktop.ApplicationService.DecodeProfile:input_type -> desktop.ProfileData
|
9, // 30: desktop.ApplicationService.FormatConfig:input_type -> desktop.ConfigContent
|
||||||
3, // 31: desktop.ApplicationService.ArchiveReport:input_type -> desktop.ArchiveReportRequest
|
10, // 31: desktop.ApplicationService.EncodeProfile:input_type -> desktop.ProfileContent
|
||||||
4, // 32: desktop.ApplicationService.StartStandaloneNetworkQualityTest:input_type -> desktop.StandaloneNetworkQualityTestRequest
|
11, // 32: desktop.ApplicationService.DecodeProfile:input_type -> desktop.ProfileData
|
||||||
5, // 33: desktop.ApplicationService.StartStandaloneSTUNTest:input_type -> desktop.StandaloneSTUNTestRequest
|
3, // 33: desktop.ApplicationService.ArchiveReport:input_type -> desktop.ArchiveReportRequest
|
||||||
6, // 34: desktop.DesktopService.GetDaemonInfo:output_type -> desktop.DaemonInfo
|
4, // 34: desktop.ApplicationService.StartStandaloneNetworkQualityTest:input_type -> desktop.StandaloneNetworkQualityTestRequest
|
||||||
28, // 35: desktop.DesktopService.ClaimService:output_type -> google.protobuf.Empty
|
5, // 35: desktop.ApplicationService.StartStandaloneSTUNTest:input_type -> desktop.StandaloneSTUNTestRequest
|
||||||
28, // 36: desktop.DesktopService.TakeOverService:output_type -> google.protobuf.Empty
|
6, // 36: desktop.DesktopService.GetDaemonInfo:output_type -> desktop.DaemonInfo
|
||||||
28, // 37: desktop.DesktopService.StartService:output_type -> google.protobuf.Empty
|
30, // 37: desktop.DesktopService.ClaimService:output_type -> google.protobuf.Empty
|
||||||
12, // 38: desktop.DesktopService.GetWorkingDirectory:output_type -> desktop.WorkingDirectoryInfo
|
30, // 38: desktop.DesktopService.TakeOverService:output_type -> google.protobuf.Empty
|
||||||
28, // 39: desktop.DesktopService.DestroyWorkingDirectory:output_type -> google.protobuf.Empty
|
30, // 39: desktop.DesktopService.StartService:output_type -> google.protobuf.Empty
|
||||||
13, // 40: desktop.DesktopService.ListCrashReports:output_type -> desktop.CrashReportList
|
12, // 40: desktop.DesktopService.GetWorkingDirectory:output_type -> desktop.WorkingDirectoryInfo
|
||||||
17, // 41: desktop.DesktopService.ReadCrashReport:output_type -> desktop.CrashReportContent
|
30, // 41: desktop.DesktopService.DestroyWorkingDirectory:output_type -> google.protobuf.Empty
|
||||||
28, // 42: desktop.DesktopService.MarkCrashReportRead:output_type -> google.protobuf.Empty
|
13, // 42: desktop.DesktopService.ListCrashReports:output_type -> desktop.CrashReportList
|
||||||
19, // 43: desktop.DesktopService.ExportCrashReport:output_type -> desktop.CrashReportArchive
|
17, // 43: desktop.DesktopService.ReadCrashReport:output_type -> desktop.CrashReportContent
|
||||||
28, // 44: desktop.DesktopService.DeleteCrashReport:output_type -> google.protobuf.Empty
|
30, // 44: desktop.DesktopService.MarkCrashReportRead:output_type -> google.protobuf.Empty
|
||||||
28, // 45: desktop.DesktopService.DeleteAllCrashReports:output_type -> google.protobuf.Empty
|
19, // 45: desktop.DesktopService.ExportCrashReport:output_type -> desktop.CrashReportArchive
|
||||||
20, // 46: desktop.DesktopService.ListOOMReports:output_type -> desktop.OOMReportList
|
30, // 46: desktop.DesktopService.DeleteCrashReport:output_type -> google.protobuf.Empty
|
||||||
24, // 47: desktop.DesktopService.ReadOOMReport:output_type -> desktop.OOMReportContent
|
30, // 47: desktop.DesktopService.DeleteAllCrashReports:output_type -> google.protobuf.Empty
|
||||||
28, // 48: desktop.DesktopService.MarkOOMReportRead:output_type -> google.protobuf.Empty
|
20, // 48: desktop.DesktopService.ListOOMReports:output_type -> desktop.OOMReportList
|
||||||
19, // 49: desktop.DesktopService.ExportOOMReport:output_type -> desktop.CrashReportArchive
|
24, // 49: desktop.DesktopService.ReadOOMReport:output_type -> desktop.OOMReportContent
|
||||||
28, // 50: desktop.DesktopService.DeleteOOMReport:output_type -> google.protobuf.Empty
|
30, // 50: desktop.DesktopService.MarkOOMReportRead:output_type -> google.protobuf.Empty
|
||||||
28, // 51: desktop.DesktopService.DeleteAllOOMReports:output_type -> google.protobuf.Empty
|
19, // 51: desktop.DesktopService.ExportOOMReport:output_type -> desktop.CrashReportArchive
|
||||||
27, // 52: desktop.DesktopService.InstallUpdate:output_type -> desktop.InstallUpdateResponse
|
30, // 52: desktop.DesktopService.DeleteOOMReport:output_type -> google.protobuf.Empty
|
||||||
28, // 53: desktop.ApplicationService.CheckConfig:output_type -> google.protobuf.Empty
|
30, // 53: desktop.DesktopService.DeleteAllOOMReports:output_type -> google.protobuf.Empty
|
||||||
9, // 54: desktop.ApplicationService.FormatConfig:output_type -> desktop.ConfigContent
|
29, // 54: desktop.DesktopService.InstallUpdate:output_type -> desktop.InstallUpdateResponse
|
||||||
11, // 55: desktop.ApplicationService.EncodeProfile:output_type -> desktop.ProfileData
|
26, // 55: desktop.DesktopService.GetSecuritySettings:output_type -> desktop.SecuritySettings
|
||||||
10, // 56: desktop.ApplicationService.DecodeProfile:output_type -> desktop.ProfileContent
|
30, // 56: desktop.DesktopService.SetInsecureModeEnabled:output_type -> google.protobuf.Empty
|
||||||
28, // 57: desktop.ApplicationService.ArchiveReport:output_type -> google.protobuf.Empty
|
30, // 57: desktop.ApplicationService.CheckConfig:output_type -> google.protobuf.Empty
|
||||||
29, // 58: desktop.ApplicationService.StartStandaloneNetworkQualityTest:output_type -> daemon.NetworkQualityTestProgress
|
9, // 58: desktop.ApplicationService.FormatConfig:output_type -> desktop.ConfigContent
|
||||||
30, // 59: desktop.ApplicationService.StartStandaloneSTUNTest:output_type -> daemon.STUNTestProgress
|
11, // 59: desktop.ApplicationService.EncodeProfile:output_type -> desktop.ProfileData
|
||||||
34, // [34:60] is the sub-list for method output_type
|
10, // 60: desktop.ApplicationService.DecodeProfile:output_type -> desktop.ProfileContent
|
||||||
8, // [8:34] is the sub-list for method input_type
|
30, // 61: desktop.ApplicationService.ArchiveReport:output_type -> google.protobuf.Empty
|
||||||
|
31, // 62: desktop.ApplicationService.StartStandaloneNetworkQualityTest:output_type -> daemon.NetworkQualityTestProgress
|
||||||
|
32, // 63: desktop.ApplicationService.StartStandaloneSTUNTest:output_type -> daemon.STUNTestProgress
|
||||||
|
36, // [36:64] is the sub-list for method output_type
|
||||||
|
8, // [8:36] is the sub-list for method input_type
|
||||||
8, // [8:8] is the sub-list for extension type_name
|
8, // [8:8] is the sub-list for extension type_name
|
||||||
8, // [8:8] is the sub-list for extension extendee
|
8, // [8:8] is the sub-list for extension extendee
|
||||||
0, // [0:8] is the sub-list for field type_name
|
0, // [0:8] is the sub-list for field type_name
|
||||||
@@ -1779,7 +1888,7 @@ func file_experimental_boxdd_desktop_service_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_experimental_boxdd_desktop_service_proto_rawDesc), len(file_experimental_boxdd_desktop_service_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_experimental_boxdd_desktop_service_proto_rawDesc), len(file_experimental_boxdd_desktop_service_proto_rawDesc)),
|
||||||
NumEnums: 3,
|
NumEnums: 3,
|
||||||
NumMessages: 25,
|
NumMessages: 27,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 2,
|
NumServices: 2,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ service DesktopService {
|
|||||||
rpc DeleteOOMReport(OOMReportRequest) returns (google.protobuf.Empty) {}
|
rpc DeleteOOMReport(OOMReportRequest) returns (google.protobuf.Empty) {}
|
||||||
rpc DeleteAllOOMReports(google.protobuf.Empty) returns (google.protobuf.Empty) {}
|
rpc DeleteAllOOMReports(google.protobuf.Empty) returns (google.protobuf.Empty) {}
|
||||||
rpc InstallUpdate(InstallUpdateRequest) returns (InstallUpdateResponse) {}
|
rpc InstallUpdate(InstallUpdateRequest) returns (InstallUpdateResponse) {}
|
||||||
|
rpc GetSecuritySettings(google.protobuf.Empty) returns (SecuritySettings) {}
|
||||||
|
rpc SetInsecureModeEnabled(SetInsecureModeEnabledRequest) returns (google.protobuf.Empty) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
service ApplicationService {
|
service ApplicationService {
|
||||||
@@ -173,6 +175,15 @@ message OOMReportFile {
|
|||||||
bool is_profile = 3;
|
bool is_profile = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message SecuritySettings {
|
||||||
|
bool available = 1;
|
||||||
|
bool insecure_mode_enabled = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetInsecureModeEnabledRequest {
|
||||||
|
bool enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message InstallUpdateRequest {
|
message InstallUpdateRequest {
|
||||||
string installer_path = 1;
|
string installer_path = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ const (
|
|||||||
DesktopService_DeleteOOMReport_FullMethodName = "/desktop.DesktopService/DeleteOOMReport"
|
DesktopService_DeleteOOMReport_FullMethodName = "/desktop.DesktopService/DeleteOOMReport"
|
||||||
DesktopService_DeleteAllOOMReports_FullMethodName = "/desktop.DesktopService/DeleteAllOOMReports"
|
DesktopService_DeleteAllOOMReports_FullMethodName = "/desktop.DesktopService/DeleteAllOOMReports"
|
||||||
DesktopService_InstallUpdate_FullMethodName = "/desktop.DesktopService/InstallUpdate"
|
DesktopService_InstallUpdate_FullMethodName = "/desktop.DesktopService/InstallUpdate"
|
||||||
|
DesktopService_GetSecuritySettings_FullMethodName = "/desktop.DesktopService/GetSecuritySettings"
|
||||||
|
DesktopService_SetInsecureModeEnabled_FullMethodName = "/desktop.DesktopService/SetInsecureModeEnabled"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DesktopServiceClient is the client API for DesktopService service.
|
// DesktopServiceClient is the client API for DesktopService service.
|
||||||
@@ -61,6 +63,8 @@ type DesktopServiceClient interface {
|
|||||||
DeleteOOMReport(ctx context.Context, in *OOMReportRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
DeleteOOMReport(ctx context.Context, in *OOMReportRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
DeleteAllOOMReports(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
DeleteAllOOMReports(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
InstallUpdate(ctx context.Context, in *InstallUpdateRequest, opts ...grpc.CallOption) (*InstallUpdateResponse, error)
|
InstallUpdate(ctx context.Context, in *InstallUpdateRequest, opts ...grpc.CallOption) (*InstallUpdateResponse, error)
|
||||||
|
GetSecuritySettings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SecuritySettings, error)
|
||||||
|
SetInsecureModeEnabled(ctx context.Context, in *SetInsecureModeEnabledRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type desktopServiceClient struct {
|
type desktopServiceClient struct {
|
||||||
@@ -261,6 +265,26 @@ func (c *desktopServiceClient) InstallUpdate(ctx context.Context, in *InstallUpd
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *desktopServiceClient) GetSecuritySettings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SecuritySettings, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(SecuritySettings)
|
||||||
|
err := c.cc.Invoke(ctx, DesktopService_GetSecuritySettings_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *desktopServiceClient) SetInsecureModeEnabled(ctx context.Context, in *SetInsecureModeEnabledRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, DesktopService_SetInsecureModeEnabled_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DesktopServiceServer is the server API for DesktopService service.
|
// DesktopServiceServer is the server API for DesktopService service.
|
||||||
// All implementations must embed UnimplementedDesktopServiceServer
|
// All implementations must embed UnimplementedDesktopServiceServer
|
||||||
// for forward compatibility.
|
// for forward compatibility.
|
||||||
@@ -284,6 +308,8 @@ type DesktopServiceServer interface {
|
|||||||
DeleteOOMReport(context.Context, *OOMReportRequest) (*emptypb.Empty, error)
|
DeleteOOMReport(context.Context, *OOMReportRequest) (*emptypb.Empty, error)
|
||||||
DeleteAllOOMReports(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
DeleteAllOOMReports(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||||
InstallUpdate(context.Context, *InstallUpdateRequest) (*InstallUpdateResponse, error)
|
InstallUpdate(context.Context, *InstallUpdateRequest) (*InstallUpdateResponse, error)
|
||||||
|
GetSecuritySettings(context.Context, *emptypb.Empty) (*SecuritySettings, error)
|
||||||
|
SetInsecureModeEnabled(context.Context, *SetInsecureModeEnabledRequest) (*emptypb.Empty, error)
|
||||||
mustEmbedUnimplementedDesktopServiceServer()
|
mustEmbedUnimplementedDesktopServiceServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +395,14 @@ func (UnimplementedDesktopServiceServer) DeleteAllOOMReports(context.Context, *e
|
|||||||
func (UnimplementedDesktopServiceServer) InstallUpdate(context.Context, *InstallUpdateRequest) (*InstallUpdateResponse, error) {
|
func (UnimplementedDesktopServiceServer) InstallUpdate(context.Context, *InstallUpdateRequest) (*InstallUpdateResponse, error) {
|
||||||
return nil, status.Error(codes.Unimplemented, "method InstallUpdate not implemented")
|
return nil, status.Error(codes.Unimplemented, "method InstallUpdate not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (UnimplementedDesktopServiceServer) GetSecuritySettings(context.Context, *emptypb.Empty) (*SecuritySettings, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method GetSecuritySettings not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedDesktopServiceServer) SetInsecureModeEnabled(context.Context, *SetInsecureModeEnabledRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method SetInsecureModeEnabled not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedDesktopServiceServer) mustEmbedUnimplementedDesktopServiceServer() {}
|
func (UnimplementedDesktopServiceServer) mustEmbedUnimplementedDesktopServiceServer() {}
|
||||||
func (UnimplementedDesktopServiceServer) testEmbeddedByValue() {}
|
func (UnimplementedDesktopServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
@@ -732,6 +766,42 @@ func _DesktopService_InstallUpdate_Handler(srv interface{}, ctx context.Context,
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _DesktopService_GetSecuritySettings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DesktopServiceServer).GetSecuritySettings(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DesktopService_GetSecuritySettings_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DesktopServiceServer).GetSecuritySettings(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DesktopService_SetInsecureModeEnabled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SetInsecureModeEnabledRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DesktopServiceServer).SetInsecureModeEnabled(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DesktopService_SetInsecureModeEnabled_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DesktopServiceServer).SetInsecureModeEnabled(ctx, req.(*SetInsecureModeEnabledRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// DesktopService_ServiceDesc is the grpc.ServiceDesc for DesktopService service.
|
// DesktopService_ServiceDesc is the grpc.ServiceDesc for DesktopService service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
@@ -815,6 +885,14 @@ var DesktopService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "InstallUpdate",
|
MethodName: "InstallUpdate",
|
||||||
Handler: _DesktopService_InstallUpdate_Handler,
|
Handler: _DesktopService_InstallUpdate_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetSecuritySettings",
|
||||||
|
Handler: _DesktopService_GetSecuritySettings_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SetInsecureModeEnabled",
|
||||||
|
Handler: _DesktopService_SetInsecureModeEnabled_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
Metadata: "experimental/boxdd/desktop_service.proto",
|
Metadata: "experimental/boxdd/desktop_service.proto",
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/tailscale/atomicfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
const securitySettingsFileName = "security.json"
|
||||||
|
|
||||||
|
type securitySettings struct {
|
||||||
|
InsecureModeEnabled bool `json:"insecure_mode_enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveSecuritySettings(directory string, settings securitySettings) error {
|
||||||
|
content, err := json.Marshal(settings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return atomicfile.WriteFile(filepath.Join(directory, securitySettingsFileName), content, 0o600)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func registerSecurityPolicy(ctx context.Context, daemon *Daemon) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func insecureModeAvailable() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) insecureModeEnabled() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/experimental/locale"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerSecurityPolicy(ctx context.Context, daemon *Daemon) {
|
||||||
|
service.MustRegister[adapter.SecurityPolicy](ctx, &daemonSecurityPolicy{daemon})
|
||||||
|
service.MustRegister[filemanager.Manager](ctx, &restrictedFileManager{daemon})
|
||||||
|
}
|
||||||
|
|
||||||
|
func insecureModeAvailable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSecuritySettings(directory string) (securitySettings, error) {
|
||||||
|
content, err := os.ReadFile(filepath.Join(directory, securitySettingsFileName))
|
||||||
|
if err != nil {
|
||||||
|
return securitySettings{}, err
|
||||||
|
}
|
||||||
|
settings, err := json.UnmarshalExtended[securitySettings](content)
|
||||||
|
if err != nil {
|
||||||
|
return securitySettings{}, err
|
||||||
|
}
|
||||||
|
return settings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Daemon) insecureModeEnabled() bool {
|
||||||
|
settings, err := loadSecuritySettings(workingDirectory)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return settings.InsecureModeEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func insecureFeatureError(feature string) error {
|
||||||
|
return E.New(fmt.Sprintf(locale.Current().InsecureFeatureMessage, feature))
|
||||||
|
}
|
||||||
|
|
||||||
|
type daemonSecurityPolicy struct {
|
||||||
|
daemon *Daemon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *daemonSecurityPolicy) CheckFeature(feature string) error {
|
||||||
|
if p.daemon.insecureModeEnabled() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return insecureFeatureError(feature)
|
||||||
|
}
|
||||||
|
|
||||||
|
type restrictedFileManager struct {
|
||||||
|
daemon *Daemon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) BasePath(name string) string {
|
||||||
|
if filepath.IsAbs(name) {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
currentDirectory, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return filepath.Join(currentDirectory, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) TempPath() string {
|
||||||
|
currentDirectory, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return "."
|
||||||
|
}
|
||||||
|
return currentDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) checkPath(name string) (string, error) {
|
||||||
|
path, err := filepath.Abs(m.BasePath(name))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if m.daemon.insecureModeEnabled() {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
currentDirectory, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
normalizedRoot := strings.ToLower(filepath.Clean(currentDirectory))
|
||||||
|
normalizedPath := strings.ToLower(filepath.Clean(path))
|
||||||
|
if normalizedPath != normalizedRoot && !strings.HasPrefix(normalizedPath, normalizedRoot+string(filepath.Separator)) {
|
||||||
|
return "", E.New(fmt.Sprintf(locale.Current().ExternalPathFeature, path))
|
||||||
|
}
|
||||||
|
existingPath := path
|
||||||
|
for {
|
||||||
|
_, err = os.Lstat(existingPath)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
parentPath := filepath.Dir(existingPath)
|
||||||
|
if parentPath == existingPath {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
existingPath = parentPath
|
||||||
|
}
|
||||||
|
resolvedRoot, err := filepath.EvalSymlinks(currentDirectory)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resolvedExistingPath, err := filepath.EvalSymlinks(existingPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
remainingPath, err := filepath.Rel(existingPath, path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resolvedPath := filepath.Join(resolvedExistingPath, remainingPath)
|
||||||
|
normalizedResolvedRoot := strings.ToLower(filepath.Clean(resolvedRoot))
|
||||||
|
normalizedResolvedPath := strings.ToLower(filepath.Clean(resolvedPath))
|
||||||
|
if normalizedResolvedPath != normalizedResolvedRoot && !strings.HasPrefix(normalizedResolvedPath, normalizedResolvedRoot+string(filepath.Separator)) {
|
||||||
|
return "", E.New(fmt.Sprintf(locale.Current().ExternalPathFeature, path))
|
||||||
|
}
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||||
|
path, err := m.checkPath(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.OpenFile(path, flag, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) Create(name string) (*os.File, error) {
|
||||||
|
path, err := m.checkPath(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.Create(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) CreateTemp(pattern string) (*os.File, error) {
|
||||||
|
currentDirectory, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.CreateTemp(currentDirectory, pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) Chown(path string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) Mkdir(path string, perm os.FileMode) error {
|
||||||
|
checkedPath, err := m.checkPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Mkdir(checkedPath, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) MkdirAll(path string, perm os.FileMode) error {
|
||||||
|
checkedPath, err := m.checkPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.MkdirAll(checkedPath, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) Remove(path string) error {
|
||||||
|
checkedPath, err := m.checkPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Remove(checkedPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) RemoveAll(path string) error {
|
||||||
|
checkedPath, err := m.checkPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.RemoveAll(checkedPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *restrictedFileManager) Rename(oldPath string, newPath string) error {
|
||||||
|
checkedOldPath, err := m.checkPath(oldPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
checkedNewPath, err := m.checkPath(newPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Rename(checkedOldPath, checkedNewPath)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/experimental/locale"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setLocaleFromContext(ctx context.Context) {
|
||||||
|
requestMetadata, loaded := metadata.FromIncomingContext(ctx)
|
||||||
|
if !loaded {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, localeID := range requestMetadata.Get("accept-language") {
|
||||||
|
if locale.Set(localeID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unaryLocaleInterceptor(ctx context.Context, request any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||||
|
setLocaleFromContext(ctx)
|
||||||
|
return handler(ctx, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func streamLocaleInterceptor(server any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
setLocaleFromContext(stream.Context())
|
||||||
|
return handler(server, stream)
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ func newDaemon() (*Daemon, error) {
|
|||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
service.MustRegister[adapter.PlatformInterface](ctx, platformInterface)
|
service.MustRegister[adapter.PlatformInterface](ctx, platformInterface)
|
||||||
}
|
}
|
||||||
|
registerSecurityPolicy(ctx, d)
|
||||||
d.startedService = daemon.NewStartedService(daemon.ServiceOptions{
|
d.startedService = daemon.NewStartedService(daemon.ServiceOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
LogMaxLines: 3000,
|
LogMaxLines: 3000,
|
||||||
@@ -65,8 +66,8 @@ func newDaemon() (*Daemon, error) {
|
|||||||
})
|
})
|
||||||
authorizer := newAuthorizer(d)
|
authorizer := newAuthorizer(d)
|
||||||
serverOptions := []grpc.ServerOption{
|
serverOptions := []grpc.ServerOption{
|
||||||
grpc.ChainUnaryInterceptor(newUnaryAuthorizeInterceptor(authorizer), daemon.UnaryErrorInterceptor),
|
grpc.ChainUnaryInterceptor(newUnaryAuthorizeInterceptor(authorizer), unaryLocaleInterceptor),
|
||||||
grpc.ChainStreamInterceptor(newStreamAuthorizeInterceptor(authorizer), daemon.StreamErrorInterceptor),
|
grpc.ChainStreamInterceptor(newStreamAuthorizeInterceptor(authorizer), streamLocaleInterceptor),
|
||||||
}
|
}
|
||||||
platformOptions, err := platformServerOptions(d)
|
platformOptions, err := platformServerOptions(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -165,11 +165,13 @@ func (c *CacheFile) startCacheCleanup() {
|
|||||||
|
|
||||||
func (c *CacheFile) start() error {
|
func (c *CacheFile) start() error {
|
||||||
const fileMode = 0o666
|
const fileMode = 0o666
|
||||||
|
cacheFile, err := filemanager.OpenFile(c.ctx, c.path, os.O_RDWR|os.O_CREATE, fileMode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cacheFile.Close()
|
||||||
options := bbolt.Options{Timeout: time.Second}
|
options := bbolt.Options{Timeout: time.Second}
|
||||||
var (
|
var db *bbolt.DB
|
||||||
db *bbolt.DB
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
for range 10 {
|
for range 10 {
|
||||||
db, err = bbolt.Open(c.path, fileMode, &options)
|
db, err = bbolt.Open(c.path, fileMode, &options)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -179,7 +181,7 @@ func (c *CacheFile) start() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
|
if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
|
||||||
rmErr := os.Remove(c.path)
|
rmErr := filemanager.Remove(c.ctx, c.path)
|
||||||
if rmErr != nil {
|
if rmErr != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -262,7 +264,7 @@ func (c *CacheFile) resetDB() {
|
|||||||
c.resetAccess.Lock()
|
c.resetAccess.Lock()
|
||||||
defer c.resetAccess.Unlock()
|
defer c.resetAccess.Unlock()
|
||||||
c.DB.Close()
|
c.DB.Close()
|
||||||
os.Remove(c.path)
|
filemanager.Remove(c.ctx, c.path)
|
||||||
db, err := bbolt.Open(c.path, 0o666, &bbolt.Options{Timeout: time.Second})
|
db, err := bbolt.Open(c.path, 0o666, &bbolt.Options{Timeout: time.Second})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_ = filemanager.Chown(c.ctx, c.path)
|
_ = filemanager.Chown(c.ctx, c.path)
|
||||||
|
|||||||
@@ -140,6 +140,10 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
|||||||
})
|
})
|
||||||
if options.ExternalUI != "" {
|
if options.ExternalUI != "" {
|
||||||
s.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
|
s.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
|
||||||
|
_, err := filemanager.ReadDir(ctx, s.externalUI)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return nil, E.Cause(err, "read external UI directory")
|
||||||
|
}
|
||||||
chiRouter.Group(func(r chi.Router) {
|
chiRouter.Group(func(r chi.Router) {
|
||||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusMovedPermanently).ServeHTTP)
|
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusMovedPermanently).ServeHTTP)
|
||||||
r.Handle("/ui/*", http.StripPrefix("/ui/", http.FileServer(Dir(s.externalUI))))
|
r.Handle("/ui/*", http.StripPrefix("/ui/", http.FileServer(Dir(s.externalUI))))
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -24,9 +23,9 @@ func (s *Server) checkAndDownloadExternalUI() {
|
|||||||
if s.externalUI == "" {
|
if s.externalUI == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
entries, err := os.ReadDir(s.externalUI)
|
entries, err := filemanager.ReadDir(s.ctx, s.externalUI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.MkdirAll(s.externalUI, 0o755)
|
filemanager.MkdirAll(s.ctx, s.externalUI, 0o755)
|
||||||
}
|
}
|
||||||
if len(entries) == 0 {
|
if len(entries) == 0 {
|
||||||
err = s.downloadExternalUI()
|
err = s.downloadExternalUI()
|
||||||
@@ -79,7 +78,7 @@ func (s *Server) downloadExternalUI() error {
|
|||||||
}
|
}
|
||||||
err = s.downloadZIP(response.Body, s.externalUI)
|
err = s.downloadZIP(response.Body, s.externalUI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
removeAllInDirectory(s.externalUI)
|
removeAllInDirectory(s.ctx, s.externalUI)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -89,7 +88,7 @@ func (s *Server) downloadZIP(body io.Reader, output string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer os.Remove(tempFile.Name())
|
defer filemanager.Remove(s.ctx, tempFile.Name())
|
||||||
_, err = io.Copy(tempFile, body)
|
_, err = io.Copy(tempFile, body)
|
||||||
tempFile.Close()
|
tempFile.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -113,7 +112,7 @@ func (s *Server) downloadZIP(body io.Reader, output string) error {
|
|||||||
if len(pathElements) > 1 {
|
if len(pathElements) > 1 {
|
||||||
saveDirectory = filepath.Join(saveDirectory, filepath.Join(pathElements[:len(pathElements)-1]...))
|
saveDirectory = filepath.Join(saveDirectory, filepath.Join(pathElements[:len(pathElements)-1]...))
|
||||||
}
|
}
|
||||||
err = os.MkdirAll(saveDirectory, 0o755)
|
err = filemanager.MkdirAll(s.ctx, saveDirectory, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -140,13 +139,13 @@ func downloadZIPEntry(ctx context.Context, zipFile *zip.File, savePath string) e
|
|||||||
return common.Error(io.Copy(saveFile, reader))
|
return common.Error(io.Copy(saveFile, reader))
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeAllInDirectory(directory string) {
|
func removeAllInDirectory(ctx context.Context, directory string) {
|
||||||
dirEntries, err := os.ReadDir(directory)
|
dirEntries, err := filemanager.ReadDir(ctx, directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, dirEntry := range dirEntries {
|
for _, dirEntry := range dirEntries {
|
||||||
os.RemoveAll(filepath.Join(directory, dirEntry.Name()))
|
filemanager.RemoveAll(ctx, filepath.Join(directory, dirEntry.Name()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ func NewHTTPClient() HTTPClient {
|
|||||||
client.transport.TLSClientConfig = &client.tls
|
client.transport.TLSClientConfig = &client.tls
|
||||||
client.transport.DisableKeepAlives = true
|
client.transport.DisableKeepAlives = true
|
||||||
if C.IsAndroid {
|
if C.IsAndroid {
|
||||||
store, err := certificate.NewStore(logger.NOP(), option.CertificateOptions{})
|
store, err := certificate.NewStore(context.Background(), logger.NOP(), option.CertificateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(E.Cause(err, "initialize certificate store"))
|
panic(E.Cause(err, "initialize certificate store"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/networkquality"
|
"github.com/sagernet/sing-box/common/networkquality"
|
||||||
@@ -102,12 +101,9 @@ func Setup(options *SetupOptions) error {
|
|||||||
return redirectStderr(filepath.Join(sWorkingPath, "CrashReport-"+sCrashReportSource+".log"))
|
return redirectStderr(filepath.Join(sWorkingPath, "CrashReport-"+sCrashReportSource+".log"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetLocale(localeId string) error {
|
func SetLocale(localeID string) error {
|
||||||
if strings.Contains(localeId, "@") {
|
if !locale.Set(localeID) {
|
||||||
localeId = strings.Split(localeId, "@")[0]
|
return E.New("unsupported locale: ", localeID)
|
||||||
}
|
|
||||||
if !locale.Set(localeId) {
|
|
||||||
return E.New("unsupported locale: ", localeId)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,103 @@
|
|||||||
package locale
|
package locale
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
localeRegistry = make(map[string]*Locale)
|
localeRegistry = map[string]*Locale{
|
||||||
current = defaultLocal
|
"en": defaultLocale,
|
||||||
|
}
|
||||||
|
localeMatcher = language.NewMatcher(
|
||||||
|
[]language.Tag{
|
||||||
|
language.English,
|
||||||
|
language.SimplifiedChinese,
|
||||||
|
language.TraditionalChinese,
|
||||||
|
language.Persian,
|
||||||
|
language.Russian,
|
||||||
|
},
|
||||||
|
language.PreferSameScript(true),
|
||||||
|
)
|
||||||
|
localeNames = []string{"en", "zh-Hans", "zh-Hant", "fa", "ru"}
|
||||||
|
current atomic.Pointer[Locale]
|
||||||
)
|
)
|
||||||
|
|
||||||
type Locale struct {
|
type Locale struct {
|
||||||
// deprecated messages for graphical clients
|
|
||||||
Locale string
|
Locale string
|
||||||
DeprecatedMessage string
|
DeprecatedMessage string
|
||||||
DeprecatedMessageNoLink string
|
DeprecatedMessageNoLink string
|
||||||
|
InsecureFeatureMessage string
|
||||||
|
ExternalPathFeature string
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultLocal = &Locale{
|
var defaultLocale = &Locale{
|
||||||
Locale: "en_US",
|
Locale: "en",
|
||||||
DeprecatedMessage: "%s is deprecated in sing-box %s and will be removed in sing-box %s please checkout documentation for migration.",
|
DeprecatedMessage: "%s is deprecated in sing-box %s and will be removed in sing-box %s. Please check the documentation for migration.",
|
||||||
DeprecatedMessageNoLink: "%s is deprecated in sing-box %s and will be removed in sing-box %s.",
|
DeprecatedMessageNoLink: "%s is deprecated in sing-box %s and will be removed in sing-box %s.",
|
||||||
|
InsecureFeatureMessage: "%s is considered insecure in the graphical client for sing-box on Windows. Enable Insecure Mode in `Settings - Core - Insecure Mode` to use it.",
|
||||||
|
ExternalPathFeature: "Access to %s (outside of the working directory) is considered insecure in the graphical client for sing-box on Windows. Enable Insecure Mode in `Settings - Core - Insecure Mode` to use it.",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
current.Store(defaultLocale)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Current() *Locale {
|
func Current() *Locale {
|
||||||
return current
|
return current.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Set(localeId string) bool {
|
func Set(localeID string) bool {
|
||||||
locale, loaded := localeRegistry[localeId]
|
localeEntries := strings.Split(localeID, ",")
|
||||||
|
for i, localeEntry := range localeEntries {
|
||||||
|
languageID, options, hasOptions := strings.Cut(localeEntry, ";")
|
||||||
|
languageID, _, _ = strings.Cut(strings.TrimSpace(languageID), "@")
|
||||||
|
languageID = strings.ReplaceAll(languageID, "_", "-")
|
||||||
|
if !hasOptions {
|
||||||
|
languageID, _, _ = strings.Cut(languageID, ".")
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case strings.EqualFold(languageID, "C"), strings.EqualFold(languageID, "POSIX"):
|
||||||
|
languageID = "en"
|
||||||
|
case strings.EqualFold(languageID, "zh-CHS"):
|
||||||
|
languageID = "zh-Hans"
|
||||||
|
case strings.EqualFold(languageID, "zh-CHT"):
|
||||||
|
languageID = "zh-Hant"
|
||||||
|
}
|
||||||
|
localeEntries[i] = languageID
|
||||||
|
if hasOptions {
|
||||||
|
localeEntries[i] += ";" + options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
localeID = strings.Join(localeEntries, ",")
|
||||||
|
tags, _, err := language.ParseAcceptLanguage(localeID)
|
||||||
|
if err != nil || len(tags) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, tag := range tags {
|
||||||
|
base, script, region := tag.Raw()
|
||||||
|
if base.String() != "zh" && base.String() != "cmn" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if script.String() == "Hans" || script.String() == "Hant" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
languageID := "zh-Hans"
|
||||||
|
if region.String() == "TW" || region.String() == "HK" || region.String() == "MO" {
|
||||||
|
languageID = "zh-Hant"
|
||||||
|
}
|
||||||
|
if region.String() != "ZZ" {
|
||||||
|
languageID += "-" + region.String()
|
||||||
|
}
|
||||||
|
tags[i] = language.MustParse(languageID)
|
||||||
|
}
|
||||||
|
_, localeIndex, _ := localeMatcher.Match(tags...)
|
||||||
|
selectedLocale, loaded := localeRegistry[localeNames[localeIndex]]
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
current = locale
|
current.Store(selectedLocale)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package locale
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
localeRegistry["fa"] = &Locale{
|
||||||
|
Locale: "fa",
|
||||||
|
DeprecatedMessage: "%s از sing-box %s منسوخ شده است و در sing-box %s حذف خواهد شد؛ لطفاً راهنمای مهاجرت را ببینید.",
|
||||||
|
DeprecatedMessageNoLink: "%s از sing-box %s منسوخ شده است و در sing-box %s حذف خواهد شد.",
|
||||||
|
InsecureFeatureMessage: "%s در کلاینت گرافیکی sing-box برای Windows ناامن تلقی میشود. برای استفاده، `حالت ناامن` را در `تنظیمات - هسته - حالت ناامن` فعال کنید.",
|
||||||
|
ExternalPathFeature: "دسترسی به %s (خارج از پوشهٔ کاری) در کلاینت گرافیکی sing-box برای Windows ناامن تلقی میشود. برای استفاده، `حالت ناامن` را در `تنظیمات - هسته - حالت ناامن` فعال کنید.",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package locale
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
localeRegistry["ru"] = &Locale{
|
||||||
|
Locale: "ru",
|
||||||
|
DeprecatedMessage: "Использование %s устарело в sing-box %s, и эта возможность будет удалена в sing-box %s. Ознакомьтесь с руководством по миграции.",
|
||||||
|
DeprecatedMessageNoLink: "Использование %s устарело в sing-box %s, и эта возможность будет удалена в sing-box %s.",
|
||||||
|
InsecureFeatureMessage: "%s считается небезопасным в графическом клиенте sing-box для Windows. Чтобы использовать эту возможность, включите `Небезопасный режим` в разделе `Настройки — Ядро — Небезопасный режим`.",
|
||||||
|
ExternalPathFeature: "Доступ к %s (за пределами рабочего каталога) считается небезопасным в графическом клиенте sing-box для Windows. Чтобы использовать эту возможность, включите `Небезопасный режим` в разделе `Настройки — Ядро — Небезопасный режим`.",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,11 @@ package locale
|
|||||||
var warningMessageForEndUsers = "\n\n如果您不明白此消息意味着什么:您的配置文件已过时,且将很快不可用。请联系您的配置提供者以更新配置。"
|
var warningMessageForEndUsers = "\n\n如果您不明白此消息意味着什么:您的配置文件已过时,且将很快不可用。请联系您的配置提供者以更新配置。"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
localeRegistry["zh_CN"] = &Locale{
|
localeRegistry["zh-Hans"] = &Locale{
|
||||||
Locale: "zh_CN",
|
Locale: "zh-Hans",
|
||||||
DeprecatedMessage: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除,请参阅迁移指南。" + warningMessageForEndUsers,
|
DeprecatedMessage: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除,请参阅迁移指南。" + warningMessageForEndUsers,
|
||||||
DeprecatedMessageNoLink: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除。" + warningMessageForEndUsers,
|
DeprecatedMessageNoLink: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除。" + warningMessageForEndUsers,
|
||||||
|
InsecureFeatureMessage: "%s 在 sing-box 的 Windows 图形客户端中被视为不安全。请在 `设置 - 核心 - 不安全模式` 中启用不安全模式后使用。",
|
||||||
|
ExternalPathFeature: "访问 %s(位于工作目录之外)在 sing-box 的 Windows 图形客户端中是不安全的。请在 `设置 - 核心 - 不安全模式` 中启用不安全模式后使用。",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package locale
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
localeRegistry["zh-Hant"] = &Locale{
|
||||||
|
Locale: "zh-Hant",
|
||||||
|
DeprecatedMessage: "%s 已在 sing-box %s 中棄用,且將在 sing-box %s 中移除,請參閱遷移指南。",
|
||||||
|
DeprecatedMessageNoLink: "%s 已在 sing-box %s 中棄用,且將在 sing-box %s 中移除。",
|
||||||
|
InsecureFeatureMessage: "%s 在 sing-box 的 Windows 圖形用戶端中被視為不安全。請在 `設置 - 核心 - 不安全模式` 中啟用不安全模式後使用。",
|
||||||
|
ExternalPathFeature: "存取 %s(位於工作目錄之外)在 sing-box 的 Windows 圖形用戶端中被視為不安全。請在 `設置 - 核心 - 不安全模式` 中啟用不安全模式後使用。",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,6 +71,7 @@ require (
|
|||||||
golang.org/x/net v0.57.0
|
golang.org/x/net v0.57.0
|
||||||
golang.org/x/sync v0.22.0
|
golang.org/x/sync v0.22.0
|
||||||
golang.org/x/sys v0.47.0
|
golang.org/x/sys v0.47.0
|
||||||
|
golang.org/x/text v0.40.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||||
google.golang.org/grpc v1.79.1
|
google.golang.org/grpc v1.79.1
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
@@ -175,7 +176,6 @@ require (
|
|||||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
||||||
golang.org/x/oauth2 v0.34.0 // indirect
|
golang.org/x/oauth2 v0.34.0 // indirect
|
||||||
golang.org/x/term v0.45.0 // indirect
|
golang.org/x/term v0.45.0 // indirect
|
||||||
golang.org/x/text v0.40.0 // indirect
|
|
||||||
golang.org/x/time v0.15.0 // indirect
|
golang.org/x/time v0.15.0 // indirect
|
||||||
golang.org/x/tools v0.47.0 // indirect
|
golang.org/x/tools v0.47.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@@ -26,6 +27,7 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterInbound(registry *inbound.Registry) {
|
func RegisterInbound(registry *inbound.Registry) {
|
||||||
@@ -73,7 +75,12 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
if options.Masquerade != nil && options.Masquerade.Type != "" {
|
if options.Masquerade != nil && options.Masquerade.Type != "" {
|
||||||
switch options.Masquerade.Type {
|
switch options.Masquerade.Type {
|
||||||
case C.Hysterai2MasqueradeTypeFile:
|
case C.Hysterai2MasqueradeTypeFile:
|
||||||
masqueradeHandler = http.FileServer(http.Dir(options.Masquerade.FileOptions.Directory))
|
masqueradeDirectory := filemanager.BasePath(ctx, os.ExpandEnv(options.Masquerade.FileOptions.Directory))
|
||||||
|
_, err = filemanager.ReadDir(ctx, masqueradeDirectory)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return nil, E.Cause(err, "read masquerade directory")
|
||||||
|
}
|
||||||
|
masqueradeHandler = http.FileServer(http.Dir(masqueradeDirectory))
|
||||||
case C.Hysterai2MasqueradeTypeProxy:
|
case C.Hysterai2MasqueradeTypeProxy:
|
||||||
masqueradeURL, err := url.Parse(options.Masquerade.ProxyOptions.URL)
|
masqueradeURL, err := url.Parse(options.Masquerade.ProxyOptions.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/cronet-go"
|
"github.com/sagernet/cronet-go"
|
||||||
@@ -25,6 +24,7 @@ import (
|
|||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/uot"
|
"github.com/sagernet/sing/common/uot"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
@@ -109,7 +109,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if len(options.TLS.Certificate) > 0 {
|
if len(options.TLS.Certificate) > 0 {
|
||||||
trustedRootCertificates = strings.Join(options.TLS.Certificate, "\n")
|
trustedRootCertificates = strings.Join(options.TLS.Certificate, "\n")
|
||||||
} else if options.TLS.CertificatePath != "" {
|
} else if options.TLS.CertificatePath != "" {
|
||||||
content, err := os.ReadFile(options.TLS.CertificatePath)
|
content, err := filemanager.ReadFile(ctx, options.TLS.CertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read certificate")
|
return nil, E.Cause(err, "read certificate")
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if len(options.TLS.ECH.Config) > 0 {
|
if len(options.TLS.ECH.Config) > 0 {
|
||||||
echConfig = []byte(strings.Join(options.TLS.ECH.Config, "\n"))
|
echConfig = []byte(strings.Join(options.TLS.ECH.Config, "\n"))
|
||||||
} else if options.TLS.ECH.ConfigPath != "" {
|
} else if options.TLS.ECH.ConfigPath != "" {
|
||||||
content, err := os.ReadFile(options.TLS.ECH.ConfigPath)
|
content, err := filemanager.ReadFile(ctx, options.TLS.ECH.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read ECH config")
|
return nil, E.Cause(err, "read ECH config")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
@@ -86,7 +87,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
privateKey = []byte(strings.Join(options.PrivateKey, "\n"))
|
privateKey = []byte(strings.Join(options.PrivateKey, "\n"))
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
privateKey, err = os.ReadFile(os.ExpandEnv(options.PrivateKeyPath))
|
privateKey, err = filemanager.ReadFile(ctx, os.ExpandEnv(options.PrivateKeyPath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "read private key")
|
return nil, E.Cause(err, "read private key")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,16 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
}
|
}
|
||||||
stateDirectory = filemanager.BasePath(ctx, os.ExpandEnv(stateDirectory))
|
stateDirectory = filemanager.BasePath(ctx, os.ExpandEnv(stateDirectory))
|
||||||
stateDirectory, _ = filepath.Abs(stateDirectory)
|
stateDirectory, _ = filepath.Abs(stateDirectory)
|
||||||
|
mkdirErr := filemanager.MkdirAll(ctx, stateDirectory, 0o700)
|
||||||
|
if mkdirErr != nil {
|
||||||
|
return nil, E.Cause(mkdirErr, "create state directory")
|
||||||
|
}
|
||||||
|
if options.SSHServer != nil && options.SSHServer.Enabled {
|
||||||
|
err := adapter.CheckSecurityFeature(ctx, "Tailscale `ssh_server`")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, advertiseRoute := range options.AdvertiseRoutes {
|
for _, advertiseRoute := range options.AdvertiseRoutes {
|
||||||
if advertiseRoute.Addr().IsUnspecified() && advertiseRoute.Bits() == 0 {
|
if advertiseRoute.Addr().IsUnspecified() && advertiseRoute.Bits() == 0 {
|
||||||
return nil, E.New("`advertise_routes` cannot be default, use `advertise_exit_node` instead.")
|
return nil, E.New("`advertise_routes` cannot be default, use `advertise_exit_node` instead.")
|
||||||
@@ -426,7 +436,7 @@ func (t *Endpoint) postStart() error {
|
|||||||
}
|
}
|
||||||
t.filter = localBackend.ExportFilter()
|
t.filter = localBackend.ExportFilter()
|
||||||
if sshEnabled {
|
if sshEnabled {
|
||||||
sshServer, err := tailssh.New(t.server, t.platformInterface, t.sshServerOptions, t.logger)
|
sshServer, err := tailssh.New(t.ctx, t.server, t.platformInterface, t.sshServerOptions, t.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "create SSH server")
|
return E.Cause(err, "create SSH server")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -28,6 +27,7 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
tsDNS "github.com/sagernet/tailscale/net/dns"
|
tsDNS "github.com/sagernet/tailscale/net/dns"
|
||||||
"github.com/sagernet/tailscale/tailcfg"
|
"github.com/sagernet/tailscale/tailcfg"
|
||||||
"github.com/sagernet/tailscale/tsnet"
|
"github.com/sagernet/tailscale/tsnet"
|
||||||
@@ -93,7 +93,7 @@ type activeSession struct {
|
|||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(tsnetServer *tsnet.Server, platformInterface adapter.PlatformInterface, options *option.TailscaleSSHServerOptions, logger logger.ContextLogger) (*Server, error) {
|
func New(ctx context.Context, tsnetServer *tsnet.Server, platformInterface adapter.PlatformInterface, options *option.TailscaleSSHServerOptions, logger logger.ContextLogger) (*Server, error) {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
tsnetServer: tsnetServer,
|
tsnetServer: tsnetServer,
|
||||||
platformInterface: platformInterface,
|
platformInterface: platformInterface,
|
||||||
@@ -104,7 +104,7 @@ func New(tsnetServer *tsnet.Server, platformInterface adapter.PlatformInterface,
|
|||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
activeConns: make(map[*activeSession]struct{}),
|
activeConns: make(map[*activeSession]struct{}),
|
||||||
}
|
}
|
||||||
s.serverCtx, s.serverCancel = context.WithCancel(context.Background())
|
s.serverCtx, s.serverCancel = context.WithCancel(ctx)
|
||||||
hostSigner, err := s.loadOrGenerateHostKey()
|
hostSigner, err := s.loadOrGenerateHostKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -132,7 +132,7 @@ func (s *Server) loadOrGenerateHostKey() (gossh.Signer, error) {
|
|||||||
if isPrivilegedUser() {
|
if isPrivilegedUser() {
|
||||||
systemKey := systemHostKeyPath()
|
systemKey := systemHostKeyPath()
|
||||||
if systemKey != "" {
|
if systemKey != "" {
|
||||||
keyData, err := os.ReadFile(systemKey)
|
keyData, err := filemanager.ReadFile(s.serverCtx, systemKey)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
signer, parseErr := gossh.ParsePrivateKey(keyData)
|
signer, parseErr := gossh.ParsePrivateKey(keyData)
|
||||||
if parseErr == nil {
|
if parseErr == nil {
|
||||||
@@ -144,7 +144,7 @@ func (s *Server) loadOrGenerateHostKey() (gossh.Signer, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
keyPath := filepath.Join(s.tsnetServer.Dir, "ssh_host_ed25519_key")
|
keyPath := filepath.Join(s.tsnetServer.Dir, "ssh_host_ed25519_key")
|
||||||
keyData, err := os.ReadFile(keyPath)
|
keyData, err := filemanager.ReadFile(s.serverCtx, keyPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
signer, parseErr := gossh.ParsePrivateKey(keyData)
|
signer, parseErr := gossh.ParsePrivateKey(keyData)
|
||||||
if parseErr == nil {
|
if parseErr == nil {
|
||||||
@@ -163,11 +163,11 @@ func (s *Server) loadOrGenerateHostKey() (gossh.Signer, error) {
|
|||||||
}
|
}
|
||||||
pemData := pem.EncodeToMemory(keyBytes)
|
pemData := pem.EncodeToMemory(keyBytes)
|
||||||
dir := filepath.Dir(keyPath)
|
dir := filepath.Dir(keyPath)
|
||||||
err = os.MkdirAll(dir, 0o700)
|
err = filemanager.MkdirAll(s.serverCtx, dir, 0o700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = os.WriteFile(keyPath, pemData, 0o600)
|
err = filemanager.WriteFile(s.serverCtx, keyPath, pemData, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-21
@@ -20,8 +20,8 @@ import (
|
|||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/rw"
|
|
||||||
"github.com/sagernet/sing/protocol/socks"
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
"github.com/cretz/bine/control"
|
"github.com/cretz/bine/control"
|
||||||
"github.com/cretz/bine/tor"
|
"github.com/cretz/bine/tor"
|
||||||
@@ -46,36 +46,50 @@ type Outbound struct {
|
|||||||
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TorOutboundOptions) (adapter.Outbound, error) {
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TorOutboundOptions) (adapter.Outbound, error) {
|
||||||
var startConf tor.StartConf
|
var startConf tor.StartConf
|
||||||
startConf.DataDir = os.ExpandEnv(options.DataDirectory)
|
startConf.DataDir = os.ExpandEnv(options.DataDirectory)
|
||||||
startConf.TempDataDirBase = os.TempDir()
|
|
||||||
startConf.ExtraArgs = options.ExtraArgs
|
|
||||||
if options.DataDirectory != "" {
|
|
||||||
dataDirAbs, _ := filepath.Abs(startConf.DataDir)
|
|
||||||
if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.IsFile(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
|
|
||||||
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath)
|
|
||||||
}
|
|
||||||
if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.IsFile(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
|
|
||||||
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if options.ExecutablePath != "" {
|
|
||||||
startConf.ExePath = options.ExecutablePath
|
|
||||||
startConf.ProcessCreator = nil
|
|
||||||
startConf.UseEmbeddedControlConn = false
|
|
||||||
}
|
|
||||||
if startConf.DataDir != "" {
|
if startConf.DataDir != "" {
|
||||||
torrcFile := filepath.Join(startConf.DataDir, "torrc")
|
startConf.DataDir = filemanager.BasePath(ctx, startConf.DataDir)
|
||||||
err := rw.MkdirParent(torrcFile)
|
}
|
||||||
|
startConf.TempDataDirBase = filemanager.TempPath(ctx)
|
||||||
|
if startConf.DataDir != "" {
|
||||||
|
err := filemanager.MkdirAll(ctx, startConf.DataDir, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !rw.IsFile(torrcFile) {
|
dataDirAbs, _ := filepath.Abs(startConf.DataDir)
|
||||||
err := os.WriteFile(torrcFile, []byte(""), 0o600)
|
geoIPPath := filepath.Join(dataDirAbs, "geoip")
|
||||||
|
geoIPInfo, err := filemanager.Stat(ctx, geoIPPath)
|
||||||
|
if err == nil && !geoIPInfo.IsDir() && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
|
||||||
|
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath)
|
||||||
|
}
|
||||||
|
geoIP6Path := filepath.Join(dataDirAbs, "geoip6")
|
||||||
|
geoIP6Info, err := filemanager.Stat(ctx, geoIP6Path)
|
||||||
|
if err == nil && !geoIP6Info.IsDir() && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
|
||||||
|
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path)
|
||||||
|
}
|
||||||
|
torrcFile := filepath.Join(startConf.DataDir, "torrc")
|
||||||
|
torrcInfo, err := filemanager.Stat(ctx, torrcFile)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = filemanager.WriteFile(ctx, torrcFile, []byte(""), 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if torrcInfo.IsDir() {
|
||||||
|
return nil, E.New("Tor configuration path is a directory: ", torrcFile)
|
||||||
}
|
}
|
||||||
startConf.TorrcFile = torrcFile
|
startConf.TorrcFile = torrcFile
|
||||||
}
|
}
|
||||||
|
startConf.ExtraArgs = options.ExtraArgs
|
||||||
|
if options.ExecutablePath != "" {
|
||||||
|
err := adapter.CheckSecurityFeature(ctx, "Tor `executable_path`")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
startConf.ExePath = options.ExecutablePath
|
||||||
|
startConf.ProcessCreator = nil
|
||||||
|
startConf.UseEmbeddedControlConn = false
|
||||||
|
}
|
||||||
outboundDialer, err := dialer.New(ctx, options.DialerOptions, false)
|
outboundDialer, err := dialer.New(ctx, options.DialerOptions, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package rule
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -100,7 +99,7 @@ func (s *LocalRuleSet) reloadFile(path string) error {
|
|||||||
var ruleSet option.PlainRuleSetCompat
|
var ruleSet option.PlainRuleSetCompat
|
||||||
switch s.fileFormat {
|
switch s.fileFormat {
|
||||||
case C.RuleSetFormatSource, "":
|
case C.RuleSetFormatSource, "":
|
||||||
content, err := os.ReadFile(path)
|
content, err := filemanager.ReadFile(s.ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -110,7 +109,7 @@ func (s *LocalRuleSet) reloadFile(path string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case C.RuleSetFormatBinary:
|
case C.RuleSetFormatBinary:
|
||||||
setFile, err := os.Open(path)
|
setFile, err := filemanager.Open(s.ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -24,6 +25,7 @@ import (
|
|||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
"github.com/caddyserver/zerossl"
|
"github.com/caddyserver/zerossl"
|
||||||
@@ -78,7 +80,12 @@ func NewCertificateProvider(ctx context.Context, logger log.ContextLogger, tag s
|
|||||||
|
|
||||||
var storage certmagic.Storage
|
var storage certmagic.Storage
|
||||||
if options.DataDirectory != "" {
|
if options.DataDirectory != "" {
|
||||||
storage = &certmagic.FileStorage{Path: options.DataDirectory}
|
dataDirectory := filemanager.BasePath(ctx, os.ExpandEnv(options.DataDirectory))
|
||||||
|
err := filemanager.MkdirAll(ctx, dataDirectory, 0o700)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "create ACME data directory")
|
||||||
|
}
|
||||||
|
storage = &certmagic.FileStorage{Path: dataDirectory}
|
||||||
} else {
|
} else {
|
||||||
storage = certmagic.Default.Storage
|
storage = certmagic.Default.Storage
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ func newDashboard(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *dashboard) start() error {
|
func (d *dashboard) start() error {
|
||||||
|
_, err := filemanager.ReadDir(d.ctx, d.path)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return E.Cause(err, "read dashboard directory")
|
||||||
|
}
|
||||||
transport, err := d.resolveTransport()
|
transport, err := d.resolveTransport()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "create dashboard http client")
|
return E.Cause(err, "create dashboard http client")
|
||||||
@@ -147,7 +151,7 @@ func (d *dashboard) loopUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *dashboard) loadState() dashboardStatus {
|
func (d *dashboard) loadState() dashboardStatus {
|
||||||
entries, err := os.ReadDir(d.path)
|
entries, err := filemanager.ReadDir(d.ctx, d.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dashboardEmpty
|
return dashboardEmpty
|
||||||
}
|
}
|
||||||
@@ -155,12 +159,12 @@ func (d *dashboard) loadState() dashboardStatus {
|
|||||||
return dashboardEmpty
|
return dashboardEmpty
|
||||||
}
|
}
|
||||||
etagPath := filepath.Join(d.path, dashboardEtagFileName)
|
etagPath := filepath.Join(d.path, dashboardEtagFileName)
|
||||||
etagBytes, err := os.ReadFile(etagPath)
|
etagBytes, err := filemanager.ReadFile(d.ctx, etagPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dashboardUserProvided
|
return dashboardUserProvided
|
||||||
}
|
}
|
||||||
d.lastEtag = strings.TrimSpace(string(etagBytes))
|
d.lastEtag = strings.TrimSpace(string(etagBytes))
|
||||||
info, err := os.Stat(etagPath)
|
info, err := filemanager.Stat(d.ctx, etagPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
d.lastUpdated = info.ModTime()
|
d.lastUpdated = info.ModTime()
|
||||||
}
|
}
|
||||||
@@ -212,7 +216,7 @@ func (d *dashboard) extract(body io.Reader, etag string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tempZipPath := tempFile.Name()
|
tempZipPath := tempFile.Name()
|
||||||
defer os.Remove(tempZipPath)
|
defer filemanager.Remove(d.ctx, tempZipPath)
|
||||||
_, err = io.Copy(tempFile, body)
|
_, err = io.Copy(tempFile, body)
|
||||||
tempFile.Close()
|
tempFile.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -271,7 +275,7 @@ func (d *dashboard) extract(body io.Reader, etag string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.Rename(tempDir, d.path)
|
return filemanager.Rename(d.ctx, tempDir, d.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractZipEntry(ctx context.Context, file *zip.File, savePath string) error {
|
func extractZipEntry(ctx context.Context, file *zip.File, savePath string) error {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ccm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -42,8 +44,8 @@ func getDefaultCredentialsPath() (string, error) {
|
|||||||
return filepath.Join(userInfo.HomeDir, ".claude", ".credentials.json"), nil
|
return filepath.Join(userInfo.HomeDir, ".claude", ".credentials.json"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readCredentialsFromFile(path string) (*oauthCredentials, error) {
|
func readCredentialsFromFile(ctx context.Context, path string) (*oauthCredentials, error) {
|
||||||
data, err := os.ReadFile(path)
|
data, err := filemanager.ReadFile(ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -60,14 +62,14 @@ func readCredentialsFromFile(path string) (*oauthCredentials, error) {
|
|||||||
return credentialsContainer.ClaudeAIAuth, nil
|
return credentialsContainer.ClaudeAIAuth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeCredentialsToFile(oauthCredentials *oauthCredentials, path string) error {
|
func writeCredentialsToFile(ctx context.Context, oauthCredentials *oauthCredentials, path string) error {
|
||||||
data, err := json.MarshalIndent(map[string]any{
|
data, err := json.MarshalIndent(map[string]any{
|
||||||
"claudeAiOauth": oauthCredentials,
|
"claudeAiOauth": oauthCredentials,
|
||||||
}, "", " ")
|
}, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.WriteFile(path, data, 0o600)
|
return filemanager.WriteFile(ctx, path, data, 0o600)
|
||||||
}
|
}
|
||||||
|
|
||||||
type oauthCredentials struct {
|
type oauthCredentials struct {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package ccm
|
package ccm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -33,9 +34,9 @@ func getKeychainServiceName() string {
|
|||||||
return "Claude Code-credentials-" + hex.EncodeToString(hash[:])[:8]
|
return "Claude Code-credentials-" + hex.EncodeToString(hash[:])[:8]
|
||||||
}
|
}
|
||||||
|
|
||||||
func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
func platformReadCredentials(ctx context.Context, customPath string) (*oauthCredentials, error) {
|
||||||
if customPath != "" {
|
if customPath != "" {
|
||||||
return readCredentialsFromFile(customPath)
|
return readCredentialsFromFile(ctx, customPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
userInfo, err := getRealUser()
|
userInfo, err := getRealUser()
|
||||||
@@ -66,12 +67,12 @@ func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return readCredentialsFromFile(defaultPath)
|
return readCredentialsFromFile(ctx, defaultPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func platformWriteCredentials(oauthCredentials *oauthCredentials, customPath string) error {
|
func platformWriteCredentials(ctx context.Context, oauthCredentials *oauthCredentials, customPath string) error {
|
||||||
if customPath != "" {
|
if customPath != "" {
|
||||||
return writeCredentialsToFile(oauthCredentials, customPath)
|
return writeCredentialsToFile(ctx, oauthCredentials, customPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
userInfo, err := getRealUser()
|
userInfo, err := getRealUser()
|
||||||
@@ -112,5 +113,5 @@ func platformWriteCredentials(oauthCredentials *oauthCredentials, customPath str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return writeCredentialsToFile(oauthCredentials, defaultPath)
|
return writeCredentialsToFile(ctx, oauthCredentials, defaultPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
package ccm
|
package ccm
|
||||||
|
|
||||||
func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
import "context"
|
||||||
|
|
||||||
|
func platformReadCredentials(ctx context.Context, customPath string) (*oauthCredentials, error) {
|
||||||
if customPath == "" {
|
if customPath == "" {
|
||||||
var err error
|
var err error
|
||||||
customPath, err = getDefaultCredentialsPath()
|
customPath, err = getDefaultCredentialsPath()
|
||||||
@@ -10,10 +12,10 @@ func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return readCredentialsFromFile(customPath)
|
return readCredentialsFromFile(ctx, customPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func platformWriteCredentials(oauthCredentials *oauthCredentials, customPath string) error {
|
func platformWriteCredentials(ctx context.Context, oauthCredentials *oauthCredentials, customPath string) error {
|
||||||
if customPath == "" {
|
if customPath == "" {
|
||||||
var err error
|
var err error
|
||||||
customPath, err = getDefaultCredentialsPath()
|
customPath, err = getDefaultCredentialsPath()
|
||||||
@@ -21,5 +23,5 @@ func platformWriteCredentials(oauthCredentials *oauthCredentials, customPath str
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return writeCredentialsToFile(oauthCredentials, customPath)
|
return writeCredentialsToFile(ctx, oauthCredentials, customPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
|
|||||||
usageTracker = &AggregatedUsage{
|
usageTracker = &AggregatedUsage{
|
||||||
LastUpdated: time.Now(),
|
LastUpdated: time.Now(),
|
||||||
Combinations: make([]CostCombination, 0),
|
Combinations: make([]CostCombination, 0),
|
||||||
|
ctx: ctx,
|
||||||
filePath: options.UsagesPath,
|
filePath: options.UsagesPath,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
@@ -201,7 +202,7 @@ func (s *Service) Start(stage adapter.StartStage) error {
|
|||||||
|
|
||||||
s.userManager.UpdateUsers(s.users)
|
s.userManager.UpdateUsers(s.users)
|
||||||
|
|
||||||
credentials, err := platformReadCredentials(s.credentialPath)
|
credentials, err := platformReadCredentials(s.ctx, s.credentialPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "read credentials")
|
return E.Cause(err, "read credentials")
|
||||||
}
|
}
|
||||||
@@ -271,7 +272,7 @@ func (s *Service) getAccessToken() (string, error) {
|
|||||||
|
|
||||||
s.credentials = newCredentials
|
s.credentials = newCredentials
|
||||||
|
|
||||||
err = platformWriteCredentials(newCredentials, s.credentialPath)
|
err = platformWriteCredentials(s.ctx, newCredentials, s.credentialPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warn("persist refreshed token: ", err)
|
s.logger.Warn("persist refreshed token: ", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ccm
|
package ccm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UsageStats struct {
|
type UsageStats struct {
|
||||||
@@ -36,6 +38,7 @@ type AggregatedUsage struct {
|
|||||||
LastUpdated time.Time `json:"last_updated"`
|
LastUpdated time.Time `json:"last_updated"`
|
||||||
Combinations []CostCombination `json:"combinations"`
|
Combinations []CostCombination `json:"combinations"`
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
ctx context.Context
|
||||||
filePath string
|
filePath string
|
||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
lastSaveTime time.Time
|
lastSaveTime time.Time
|
||||||
@@ -567,7 +570,7 @@ func (u *AggregatedUsage) Load() error {
|
|||||||
u.LastUpdated = time.Time{}
|
u.LastUpdated = time.Time{}
|
||||||
u.Combinations = nil
|
u.Combinations = nil
|
||||||
|
|
||||||
data, err := os.ReadFile(u.filePath)
|
data, err := filemanager.ReadFile(u.ctx, u.filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -601,12 +604,12 @@ func (u *AggregatedUsage) Save() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmpFile := u.filePath + ".tmp"
|
tmpFile := u.filePath + ".tmp"
|
||||||
err = os.WriteFile(tmpFile, data, 0o644)
|
err = filemanager.WriteFile(u.ctx, tmpFile, data, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpFile)
|
defer filemanager.Remove(u.ctx, tmpFile)
|
||||||
err = os.Rename(tmpFile, u.filePath)
|
err = filemanager.Rename(u.ctx, tmpFile, u.filePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
u.saveMutex.Lock()
|
u.saveMutex.Lock()
|
||||||
u.lastSaveTime = time.Now()
|
u.lastSaveTime = time.Now()
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
|
|||||||
func (d *Service) Start(stage adapter.StartStage) error {
|
func (d *Service) Start(stage adapter.StartStage) error {
|
||||||
switch stage {
|
switch stage {
|
||||||
case adapter.StartStateStart:
|
case adapter.StartStateStart:
|
||||||
config, err := readDERPConfig(filemanager.BasePath(d.ctx, d.configPath))
|
config, err := readDERPConfig(d.ctx, filemanager.BasePath(d.ctx, d.configPath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ func (d *Service) Start(stage adapter.StartStage) error {
|
|||||||
server.SetMeshKey(d.meshKey)
|
server.SetMeshKey(d.meshKey)
|
||||||
} else if d.meshKeyPath != "" {
|
} else if d.meshKeyPath != "" {
|
||||||
var meshKeyContent []byte
|
var meshKeyContent []byte
|
||||||
meshKeyContent, err = os.ReadFile(d.meshKeyPath)
|
meshKeyContent, err = filemanager.ReadFile(d.ctx, d.meshKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -447,11 +447,11 @@ type derpConfig struct {
|
|||||||
PrivateKey key.NodePrivate
|
PrivateKey key.NodePrivate
|
||||||
}
|
}
|
||||||
|
|
||||||
func readDERPConfig(path string) (*derpConfig, error) {
|
func readDERPConfig(ctx context.Context, path string) (*derpConfig, error) {
|
||||||
content, err := os.ReadFile(path)
|
content, err := filemanager.ReadFile(ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return writeNewDERPConfig(path)
|
return writeNewDERPConfig(ctx, path)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -463,9 +463,9 @@ func readDERPConfig(path string) (*derpConfig, error) {
|
|||||||
return &config, nil
|
return &config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeNewDERPConfig(path string) (*derpConfig, error) {
|
func writeNewDERPConfig(ctx context.Context, path string) (*derpConfig, error) {
|
||||||
newKey := key.NewNode()
|
newKey := key.NewNode()
|
||||||
err := os.MkdirAll(filepath.Dir(path), 0o777)
|
err := filemanager.MkdirAll(ctx, filepath.Dir(path), 0o777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -476,7 +476,7 @@ func writeNewDERPConfig(path string) (*derpConfig, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = os.WriteFile(path, content, 0o644)
|
err = filemanager.WriteFile(ctx, path, content, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ocm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -42,8 +44,8 @@ func getDefaultCredentialsPath() (string, error) {
|
|||||||
return filepath.Join(userInfo.HomeDir, ".codex", "auth.json"), nil
|
return filepath.Join(userInfo.HomeDir, ".codex", "auth.json"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readCredentialsFromFile(path string) (*oauthCredentials, error) {
|
func readCredentialsFromFile(ctx context.Context, path string) (*oauthCredentials, error) {
|
||||||
data, err := os.ReadFile(path)
|
data, err := filemanager.ReadFile(ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -55,12 +57,12 @@ func readCredentialsFromFile(path string) (*oauthCredentials, error) {
|
|||||||
return &credentials, nil
|
return &credentials, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeCredentialsToFile(credentials *oauthCredentials, path string) error {
|
func writeCredentialsToFile(ctx context.Context, credentials *oauthCredentials, path string) error {
|
||||||
data, err := json.MarshalIndent(credentials, "", " ")
|
data, err := json.MarshalIndent(credentials, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.WriteFile(path, data, 0o600)
|
return filemanager.WriteFile(ctx, path, data, 0o600)
|
||||||
}
|
}
|
||||||
|
|
||||||
type oauthCredentials struct {
|
type oauthCredentials struct {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
package ocm
|
package ocm
|
||||||
|
|
||||||
func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
import "context"
|
||||||
|
|
||||||
|
func platformReadCredentials(ctx context.Context, customPath string) (*oauthCredentials, error) {
|
||||||
if customPath == "" {
|
if customPath == "" {
|
||||||
var err error
|
var err error
|
||||||
customPath, err = getDefaultCredentialsPath()
|
customPath, err = getDefaultCredentialsPath()
|
||||||
@@ -10,10 +12,10 @@ func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return readCredentialsFromFile(customPath)
|
return readCredentialsFromFile(ctx, customPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func platformWriteCredentials(credentials *oauthCredentials, customPath string) error {
|
func platformWriteCredentials(ctx context.Context, credentials *oauthCredentials, customPath string) error {
|
||||||
if customPath == "" {
|
if customPath == "" {
|
||||||
var err error
|
var err error
|
||||||
customPath, err = getDefaultCredentialsPath()
|
customPath, err = getDefaultCredentialsPath()
|
||||||
@@ -21,5 +23,5 @@ func platformWriteCredentials(credentials *oauthCredentials, customPath string)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return writeCredentialsToFile(credentials, customPath)
|
return writeCredentialsToFile(ctx, credentials, customPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
package ocm
|
package ocm
|
||||||
|
|
||||||
func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
import "context"
|
||||||
|
|
||||||
|
func platformReadCredentials(ctx context.Context, customPath string) (*oauthCredentials, error) {
|
||||||
if customPath == "" {
|
if customPath == "" {
|
||||||
var err error
|
var err error
|
||||||
customPath, err = getDefaultCredentialsPath()
|
customPath, err = getDefaultCredentialsPath()
|
||||||
@@ -10,10 +12,10 @@ func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return readCredentialsFromFile(customPath)
|
return readCredentialsFromFile(ctx, customPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func platformWriteCredentials(credentials *oauthCredentials, customPath string) error {
|
func platformWriteCredentials(ctx context.Context, credentials *oauthCredentials, customPath string) error {
|
||||||
if customPath == "" {
|
if customPath == "" {
|
||||||
var err error
|
var err error
|
||||||
customPath, err = getDefaultCredentialsPath()
|
customPath, err = getDefaultCredentialsPath()
|
||||||
@@ -21,5 +23,5 @@ func platformWriteCredentials(credentials *oauthCredentials, customPath string)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return writeCredentialsToFile(credentials, customPath)
|
return writeCredentialsToFile(ctx, credentials, customPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
|
|||||||
usageTracker = &AggregatedUsage{
|
usageTracker = &AggregatedUsage{
|
||||||
LastUpdated: time.Now(),
|
LastUpdated: time.Now(),
|
||||||
Combinations: make([]CostCombination, 0),
|
Combinations: make([]CostCombination, 0),
|
||||||
|
ctx: ctx,
|
||||||
filePath: options.UsagesPath,
|
filePath: options.UsagesPath,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
@@ -222,7 +223,7 @@ func (s *Service) Start(stage adapter.StartStage) error {
|
|||||||
|
|
||||||
s.userManager.UpdateUsers(s.users)
|
s.userManager.UpdateUsers(s.users)
|
||||||
|
|
||||||
credentials, err := platformReadCredentials(s.credentialPath)
|
credentials, err := platformReadCredentials(s.ctx, s.credentialPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "read credentials")
|
return E.Cause(err, "read credentials")
|
||||||
}
|
}
|
||||||
@@ -292,7 +293,7 @@ func (s *Service) getAccessToken() (string, error) {
|
|||||||
|
|
||||||
s.credentials = newCredentials
|
s.credentials = newCredentials
|
||||||
|
|
||||||
err = platformWriteCredentials(newCredentials, s.credentialPath)
|
err = platformWriteCredentials(s.ctx, newCredentials, s.credentialPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warn("persist refreshed token: ", err)
|
s.logger.Warn("persist refreshed token: ", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ocm
|
package ocm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
@@ -12,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UsageStats struct {
|
type UsageStats struct {
|
||||||
@@ -56,6 +58,7 @@ type AggregatedUsage struct {
|
|||||||
LastUpdated time.Time `json:"last_updated"`
|
LastUpdated time.Time `json:"last_updated"`
|
||||||
Combinations []CostCombination `json:"combinations"`
|
Combinations []CostCombination `json:"combinations"`
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
ctx context.Context
|
||||||
filePath string
|
filePath string
|
||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
lastSaveTime time.Time
|
lastSaveTime time.Time
|
||||||
@@ -1072,7 +1075,7 @@ func (u *AggregatedUsage) Load() error {
|
|||||||
u.LastUpdated = time.Time{}
|
u.LastUpdated = time.Time{}
|
||||||
u.Combinations = nil
|
u.Combinations = nil
|
||||||
|
|
||||||
data, err := os.ReadFile(u.filePath)
|
data, err := filemanager.ReadFile(u.ctx, u.filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -1106,12 +1109,12 @@ func (u *AggregatedUsage) Save() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmpFile := u.filePath + ".tmp"
|
tmpFile := u.filePath + ".tmp"
|
||||||
err = os.WriteFile(tmpFile, data, 0o644)
|
err = filemanager.WriteFile(u.ctx, tmpFile, data, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpFile)
|
defer filemanager.Remove(u.ctx, tmpFile)
|
||||||
err = os.Rename(tmpFile, u.filePath)
|
err = filemanager.Rename(u.ctx, tmpFile, u.filePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
u.saveMutex.Lock()
|
u.saveMutex.Lock()
|
||||||
u.lastSaveTime = time.Now()
|
u.lastSaveTime = time.Now()
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -33,6 +34,7 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
)
|
)
|
||||||
@@ -109,7 +111,13 @@ func NewCertificateProvider(ctx context.Context, logger log.ContextLogger, tag s
|
|||||||
}
|
}
|
||||||
var storage certmagic.Storage
|
var storage certmagic.Storage
|
||||||
if options.DataDirectory != "" {
|
if options.DataDirectory != "" {
|
||||||
storage = &certmagic.FileStorage{Path: options.DataDirectory}
|
dataDirectory := filemanager.BasePath(ctx, os.ExpandEnv(options.DataDirectory))
|
||||||
|
mkdirErr := filemanager.MkdirAll(ctx, dataDirectory, 0o700)
|
||||||
|
if mkdirErr != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, E.Cause(mkdirErr, "create data directory")
|
||||||
|
}
|
||||||
|
storage = &certmagic.FileStorage{Path: dataDirectory}
|
||||||
} else {
|
} else {
|
||||||
storage = certmagic.Default.Storage
|
storage = certmagic.Default.Storage
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func (s *Service) loadCache() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
basePath := filemanager.BasePath(s.ctx, s.cachePath)
|
basePath := filemanager.BasePath(s.ctx, s.cachePath)
|
||||||
cacheBinary, err := os.ReadFile(basePath)
|
cacheBinary, err := filemanager.ReadFile(s.ctx, basePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -46,7 +46,7 @@ func (s *Service) loadCache() error {
|
|||||||
}
|
}
|
||||||
err = s.decodeCache(cacheBinary)
|
err = s.decodeCache(cacheBinary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.RemoveAll(basePath)
|
filemanager.RemoveAll(s.ctx, basePath)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.cacheMutex.Lock()
|
s.cacheMutex.Lock()
|
||||||
@@ -73,11 +73,11 @@ func (s *Service) saveCache() error {
|
|||||||
|
|
||||||
func (s *Service) writeCache(cacheBinary []byte) error {
|
func (s *Service) writeCache(cacheBinary []byte) error {
|
||||||
basePath := filemanager.BasePath(s.ctx, s.cachePath)
|
basePath := filemanager.BasePath(s.ctx, s.cachePath)
|
||||||
err := os.MkdirAll(filepath.Dir(basePath), 0o777)
|
err := filemanager.MkdirAll(s.ctx, filepath.Dir(basePath), 0o777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = os.WriteFile(basePath, cacheBinary, 0o644)
|
err = filemanager.WriteFile(s.ctx, basePath, cacheBinary, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user