Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions account/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -700,11 +700,16 @@ func (a *Client) setData(data *UserData) {
}

func (a *Client) ClearUser() {
settings.Clear(settings.UserIDKey)
settings.Clear(settings.TokenKey)
settings.Clear(settings.UserLevelKey)
settings.Clear(settings.EmailKey)
settings.Clear(settings.DevicesKey)
settings.Clear(settings.JwtTokenKey)
settings.Clear(settings.UserDataKey)
err := settings.Clear(
settings.UserIDKey,
settings.TokenKey,
settings.UserLevelKey,
Comment thread
garmr-ulfr marked this conversation as resolved.
settings.EmailKey,
settings.DevicesKey,
settings.JwtTokenKey,
settings.UserDataKey,
)
if err != nil {
slog.Warn("failed to clear user info", "error", err)
}
}
113 changes: 56 additions & 57 deletions backend/radiance.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"path/filepath"
"reflect"
"slices"
"strings"
"sync"

"time"
Expand Down Expand Up @@ -233,38 +232,21 @@ func (r *LocalBackend) Start() {
return
}
cfg := evt.New
locs := make(map[string]C.ServerLocation, len(cfg.OutboundLocations))
// Track which cities are already covered by active outbounds.
coveredCities := make(map[string]bool, len(cfg.OutboundLocations))
for k, v := range cfg.OutboundLocations {
if v == nil {
slog.Warn("Server location is nil, skipping", "tag", k)
continue
var srvs []*servers.Server
addSvr := func(tag, typ string, opts any, loc *C.ServerLocation) {
s := &servers.Server{
Tag: tag, Type: typ, IsLantern: true, Options: opts,
}
locs[k] = *v
coveredCities[v.City+"|"+v.CountryCode] = true
}
// Include available server locations not already covered by active
// outbounds so the client's location picker shows every location.
for _, sl := range cfg.Servers {
if coveredCities[sl.City+"|"+sl.CountryCode] {
continue
if loc != nil {
s.Location = *loc
}
key := strings.ToLower(strings.ReplaceAll(sl.City, " ", "-") + "-" + sl.CountryCode)
locs[key] = sl
srvs = append(srvs, s)
}
var srvs []*servers.Server
for _, out := range cfg.Options.Outbounds {
srvs = append(srvs, &servers.Server{
Tag: out.Tag, Type: out.Type, IsLantern: true,
Options: out, Location: locs[out.Tag],
})
addSvr(out.Tag, out.Type, out, cfg.OutboundLocations[out.Tag])
}
for _, ep := range cfg.Options.Endpoints {
srvs = append(srvs, &servers.Server{
Tag: ep.Tag, Type: ep.Type, IsLantern: true,
Options: ep, Location: locs[ep.Tag],
})
addSvr(ep.Tag, ep.Type, ep, cfg.OutboundLocations[ep.Tag])
}
list := servers.ServerList{Servers: srvs, URLOverrides: cfg.BanditURLOverrides}
if len(cfg.BanditURLOverrides) > 0 {
Expand Down Expand Up @@ -562,16 +544,6 @@ func (r *LocalBackend) GetServerByTag(tag string) (*servers.Server, bool) {
return r.srvManager.GetServerByTag(tag)
}

func (r *LocalBackend) AddServers(list servers.ServerList) error {
if err := r.srvManager.AddServers(list, false); err != nil {
return fmt.Errorf("failed to add servers to ServerManager: %w", err)
}
if err := r.vpnClient.AddOutbounds(list); err != nil && !errors.Is(err, vpn.ErrTunnelNotConnected) {
return fmt.Errorf("failed to add outbounds to VPN client: %w", err)
}
return nil
}

func (r *LocalBackend) RemoveServers(tags []string) error {
removed, err := r.srvManager.RemoveServers(tags)
if err != nil {
Expand All @@ -590,6 +562,50 @@ func (r *LocalBackend) RemoveServers(tags []string) error {
return nil
}

func (r *LocalBackend) AddServers(list servers.ServerList) error {
if err := r.srvManager.AddServers(list, false); err != nil {
return fmt.Errorf("failed to add servers to ServerManager: %w", err)
}
if err := r.vpnClient.AddOutbounds(list); err != nil && !errors.Is(err, vpn.ErrTunnelNotConnected) {
return fmt.Errorf("failed to add outbounds to VPN client: %w", err)
}
return nil
}

func (r *LocalBackend) AddServersByJSON(config string) ([]string, error) {
list, err := r.srvManager.AddServersByJSON(r.ctx, []byte(config))
if err != nil {
return nil, fmt.Errorf("failed to add servers by JSON: %w", err)
}
if err := r.vpnClient.AddOutbounds(*list); err != nil && !errors.Is(err, vpn.ErrTunnelNotConnected) {
return nil, fmt.Errorf("failed to add outbounds to VPN client: %w", err)
}
return list.Tags(), nil
}

func (r *LocalBackend) AddServersByURL(urls []string, skipCertVerification bool) ([]string, error) {
list, err := r.srvManager.AddServersByURL(r.ctx, urls, skipCertVerification)
if err != nil {
return nil, fmt.Errorf("failed to add servers by URL: %w", err)
}
if err := r.vpnClient.AddOutbounds(*list); err != nil && !errors.Is(err, vpn.ErrTunnelNotConnected) {
return nil, fmt.Errorf("failed to add outbounds to VPN client: %w", err)
}
return list.Tags(), nil
}

func (r *LocalBackend) AddPrivateServer(tag, ip string, port int, accessToken string, loc C.ServerLocation, joined bool) error {
return r.srvManager.AddPrivateServer(tag, ip, port, accessToken, loc, joined)
}

func (r *LocalBackend) InviteToPrivateServer(ip string, port int, accessToken string, inviteName string) (string, error) {
return r.srvManager.InviteToPrivateServer(ip, port, accessToken, inviteName)
}

func (r *LocalBackend) RevokePrivateServerInvite(ip string, port int, accessToken string, inviteName string) error {
return r.srvManager.RevokePrivateServerInvite(ip, port, accessToken, inviteName)
}

func (r *LocalBackend) setServers(list servers.ServerList, isLantern bool) error {
if err := r.srvManager.SetServers(list, isLantern); err != nil {
return fmt.Errorf("failed to set servers in ServerManager: %w", err)
Expand All @@ -600,6 +616,9 @@ func (r *LocalBackend) setServers(list servers.ServerList, isLantern bool) error
if err := r.vpnClient.UpdateOutbounds(allList); err != nil && !errors.Is(err, vpn.ErrTunnelNotConnected) {
return fmt.Errorf("failed to update VPN outbounds: %w", err)
}
if r.vpnClient.Status() != vpn.Connected {
r.clearSelectedIfMissing()
}
return nil
}

Expand All @@ -621,26 +640,6 @@ func (r *LocalBackend) clearSelectedIfMissing() {
}
}

func (r *LocalBackend) AddServersByJSON(config string) ([]string, error) {
return r.srvManager.AddServersByJSON(context.Background(), []byte(config))
}

func (r *LocalBackend) AddServersByURL(urls []string, skipCertVerification bool) ([]string, error) {
return r.srvManager.AddServersByURL(context.Background(), urls, skipCertVerification)
}

func (r *LocalBackend) AddPrivateServer(tag, ip string, port int, accessToken string, loc C.ServerLocation, joined bool) error {
return r.srvManager.AddPrivateServer(tag, ip, port, accessToken, loc, joined)
}

func (r *LocalBackend) InviteToPrivateServer(ip string, port int, accessToken string, inviteName string) (string, error) {
return r.srvManager.InviteToPrivateServer(ip, port, accessToken, inviteName)
}

func (r *LocalBackend) RevokePrivateServerInvite(ip string, port int, accessToken string, inviteName string) error {
return r.srvManager.RevokePrivateServerInvite(ip, port, accessToken, inviteName)
}

const selectionHistoryFlushInterval = 5 * time.Second

func (r *LocalBackend) updateSelectionHistoryListener(status vpn.VPNStatus) {
Expand Down
23 changes: 18 additions & 5 deletions common/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ type candidateSource struct {
// 1. <fileDir>/settings.json — canonical
// 2. <fileDir>/local.json — v9.0.x (renamed in #370)
// 3. Windows ${PUBLIC}\Lantern\data\* — v9.0.x cross-dir (#3460);
// spliced in below, Windows only
// spliced in below, Windows only
// 4. pre-9.x platform-specific YAML (legacy_yaml.go); spliced in below
// 5. <fileDir>/data/settings.json — v9.1.x (bugged: #370's
// setupDirectories appended an
// unconditional "/data" suffix)
// setupDirectories appended an
// unconditional "/data" suffix)
//
// Pick the highest-priority candidate with user_level=="pro"; if none
// is pro, the highest-priority candidate that exists. Losing Pro is
Expand Down Expand Up @@ -315,15 +315,25 @@ func Exists(key _key) bool {
}

func Set(key _key, value any) error {
// take lock for the entire duration of the Set + save sequence to prevent multiple Set
// calls from interleaving and leaving the file in an inconsistent state until the next write.
k.mu.Lock()
defer k.mu.Unlock()
err := k.k.Set(key.String(), value)
if err != nil {
return fmt.Errorf("could not set key %s: %w", key, err)
}
return save()
}

func Clear(key _key) {
k.k.Delete(key.String())
func Clear(keys ..._key) error {
// take lock for the entire duration. See [Set] for explanation.
k.mu.Lock()
defer k.mu.Unlock()
for _, key := range keys {
k.k.Delete(key.String())
}
return save()
}

type Settings map[_key]any
Expand Down Expand Up @@ -359,6 +369,9 @@ func GetAllFor(keys ..._key) Settings {

// Patch takes a map of settings to update and applies them all at once.
func Patch(updates Settings) error {
// take lock for the entire duration. See [Set] for explanation.
k.mu.Lock()
defer k.mu.Unlock()
for key, value := range updates {
if err := k.k.Set(_key(key).String(), value); err != nil {
return fmt.Errorf("could not set key %s: %w", key, err)
Expand Down
14 changes: 6 additions & 8 deletions servers/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ func (m *Manager) AddServers(list ServerList, force bool) error {
}
}
for _, srv := range list.Servers {
m.servers[srv.Tag] = srv
m.servers[srv.Tag] = srv.Clone()
}
return nil
}(); err != nil {
Expand Down Expand Up @@ -638,7 +638,7 @@ func (m *Manager) RevokePrivateServerInvite(ip string, port int, accessToken str
}

// AddServersByJSON adds any outbounds and endpoints defined in the provided sing-box JSON config.
func (m *Manager) AddServersByJSON(ctx context.Context, config []byte) ([]string, error) {
func (m *Manager) AddServersByJSON(ctx context.Context, config []byte) (*ServerList, error) {
ctx, span := otel.Tracer(tracerName).Start(ctx, "Manager.AddServerBySingboxJSON")
defer span.End()
type singboxConfig struct {
Expand All @@ -653,29 +653,27 @@ func (m *Manager) AddServersByJSON(ctx context.Context, config []byte) ([]string
return nil, traces.RecordError(ctx, fmt.Errorf("no endpoints or outbounds found in the provided configuration"))
}
servers := make([]*Server, 0, len(cfg.Outbounds)+len(cfg.Endpoints))
tags := make([]string, 0, len(cfg.Outbounds)+len(cfg.Endpoints))
for _, out := range cfg.Outbounds {
if out.Tag == "" {
return nil, traces.RecordError(ctx, fmt.Errorf("outbound missing tag"))
}
servers = append(servers, &Server{Tag: out.Tag, Type: out.Type, Options: out})
tags = append(tags, out.Tag)
}
for _, ep := range cfg.Endpoints {
if ep.Tag == "" {
return nil, traces.RecordError(ctx, fmt.Errorf("endpoint missing tag"))
}
servers = append(servers, &Server{Tag: ep.Tag, Type: ep.Type, Options: ep})
tags = append(tags, ep.Tag)
}
if err := m.AddServers(ServerList{Servers: servers}, false); err != nil {
list := ServerList{Servers: servers}
if err := m.AddServers(list, false); err != nil {
return nil, traces.RecordError(ctx, fmt.Errorf("failed to add servers: %w", err))
}
return tags, nil
return &list, nil
}

// AddServersByURL adds a server(s) by downloading and parsing the config from a list of URLs.
func (m *Manager) AddServersByURL(ctx context.Context, urls []string, skipCertVerification bool) ([]string, error) {
func (m *Manager) AddServersByURL(ctx context.Context, urls []string, skipCertVerification bool) (*ServerList, error) {
ctx, span := otel.Tracer(tracerName).Start(ctx, "Manager.AddServerByURLs")
defer span.End()
urlProvider, loaded := pluriconfig.GetProvider(string(model.ProviderURL))
Expand Down
8 changes: 4 additions & 4 deletions servers/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ func TestAddServersByJSON(t *testing.T) {
Options: cfg.Outbounds[0],
}
m := testManager(t)
tags, err := m.AddServersByJSON(t.Context(), testConfig)
list, err := m.AddServersByJSON(t.Context(), testConfig)
require.NoError(t, err)
assert.Equal(t, []string{"out"}, tags)
assert.Equal(t, []string{"out"}, list.Tags())
got, exists := m.GetServerByTag("out")
assert.True(t, exists, "server was not added")
assert.Equal(t, want.Tag, got.Tag)
Expand All @@ -181,9 +181,9 @@ func TestAddServersByURL(t *testing.T) {
}
t.Run("valid urls", func(t *testing.T) {
m := testManager(t)
tags, err := m.AddServersByURL(t.Context(), urls, false)
list, err := m.AddServersByURL(t.Context(), urls, false)
require.NoError(t, err)
assert.Len(t, tags, 2)
assert.Len(t, list.Tags(), 2)
_, exists := m.GetServerByTag("VLESS+over+WS+with+TLS")
assert.True(t, exists, "VLESS server should be added")
_, exists = m.GetServerByTag("Trojan+with+TLS")
Expand Down
4 changes: 3 additions & 1 deletion vpn/boxoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,9 @@ func hasGlobalIPv6Using(getSnapshots func() ([]ifaceSnapshot, error)) bool {
// baseOpts returns the minimum sing-box options required for the tunnel to
// function. Do not modify without understanding the downstream effects.
func baseOpts(basePath string) O.Options {
splitTunnelPath := filepath.Join(basePath, splitTunnelFile)
// ensure split tunnel file exists
splitTunnelPath := newSplitTunnel(basePath, slog.Default()).ruleFile

cacheFile := cacheFilePath(basePath)
loopbackAddr := badoption.Addr(netip.MustParseAddr("127.0.0.1"))

Expand Down
1 change: 0 additions & 1 deletion vpn/vpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ func NewVPNClient(dataPath string, logger *slog.Logger, platformIfce PlatformInt
if logger == nil {
logger = slog.Default()
}
_ = newSplitTunnel(dataPath, logger)
done := make(chan struct{})
close(done)
c := &VPNClient{
Expand Down
Loading