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
23 changes: 23 additions & 0 deletions servers/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package servers

import (
"context"
"errors"
"fmt"
"io"
Expand All @@ -19,11 +20,13 @@ import (
"sync"

sbx "github.com/getlantern/sing-box-extensions"
"go.opentelemetry.io/otel"

C "github.com/getlantern/common"

"github.com/getlantern/radiance/common"
"github.com/getlantern/radiance/internal"
"github.com/getlantern/radiance/traces"

"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/json"
Expand All @@ -36,6 +39,8 @@ const (
SGUser ServerGroup = "user"

trustFingerprintFileName = "trusted_server_fingerprints.json"

tracerName = "github.com/getlantern/radiance/servers"
)

type Options struct {
Expand Down Expand Up @@ -447,3 +452,21 @@ func (m *Manager) getClientForTrustedFingerprint(ip string, port int, trustFinge
}
return client, nil
}

// AddServerWithSingboxJSON parse a value that can be a JSON sing-box config.
// It parses the config into a sing-box config and add it to the user managed group.
func (m *Manager) AddServerWithSingboxJSON(ctx context.Context, value []byte) error {
ctx, span := otel.Tracer(tracerName).Start(ctx, "Manager.AddServerWithSingboxJSON")
defer span.End()
var option Options
if err := json.UnmarshalContext(sbx.BoxContext(), value, &option); err != nil {
return traces.RecordError(ctx, fmt.Errorf("failed to parse config: %w", err))
}
if len(option.Endpoints) == 0 && len(option.Outbounds) == 0 {
return traces.RecordError(ctx, fmt.Errorf("no endpoints or outbounds found in the provided configuration"))
}
if err := m.AddServers(SGUser, option); err != nil {
return traces.RecordError(ctx, fmt.Errorf("failed to add servers: %w", err))
}
return nil
}
52 changes: 52 additions & 0 deletions servers/manager_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package servers

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -139,3 +141,53 @@ func (s *lanternServerManagerMock) ServeHTTP(w http.ResponseWriter, r *http.Requ

w.WriteHeader(http.StatusNotFound)
}

func TestAddServerWithSingBoxJSON(t *testing.T) {
dataPath := t.TempDir()
manager := &Manager{
servers: Servers{
SGLantern: Options{
Outbounds: make([]option.Outbound, 0),
Endpoints: make([]option.Endpoint, 0),
Locations: make(map[string]C.ServerLocation),
},
SGUser: Options{
Outbounds: make([]option.Outbound, 0),
Endpoints: make([]option.Endpoint, 0),
Locations: make(map[string]C.ServerLocation),
},
},
optsMaps: map[ServerGroup]map[string]any{
SGLantern: make(map[string]any),
SGUser: make(map[string]any),
},
serversFile: filepath.Join(dataPath, common.ServersFileName),
fingerprintsFile: filepath.Join(dataPath, trustFingerprintFileName),
}

ctx := context.Background()
jsonConfig := `
{
"outbounds": [
{
"type": "shadowsocks",
"tag": "ss-out",
"server": "127.0.0.1",
"server_port": 8388,
"method": "chacha20-ietf-poly1305",
"password": "randompasswordwith24char",
"network": "tcp"
}
]
}`

t.Run("adding server with a sing-box json config should work", func(t *testing.T) {
require.NoError(t, manager.AddServerWithSingboxJSON(ctx, []byte(jsonConfig)))
})
t.Run("using a empty config should return an error", func(t *testing.T) {
require.Error(t, manager.AddServerWithSingboxJSON(ctx, []byte{}))
})
t.Run("providing a json that doesn't have any endpoints or outbounds should return a error", func(t *testing.T) {
require.Error(t, manager.AddServerWithSingboxJSON(ctx, json.RawMessage("{}")))
})
}