Skip to content

Commit b17902b

Browse files
authored
adding function for adding custom sing-box config from user (#156)
* feat: adding code for receiving user manual configuration * feat: adding url parser * feat: parsing config based on received vmess config * fix: removing tag parameter since this information is coming from the URL or it already exists inside the sing-box config * fix: using sing-box context for unmarshaling json * feat: adding unit tests and improve error handling * fix: checking if ss url username has the expected length * fix: parsing vmess transport config * fix: using function provided context * feat: refactoring function for being specific for sing-box json format * fix: removing parser since it's being moved to another repository * fix: updating span name so it matches the function name
1 parent e4eea72 commit b17902b

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

servers/manager.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package servers
55

66
import (
7+
"context"
78
"errors"
89
"fmt"
910
"io"
@@ -19,11 +20,13 @@ import (
1920
"sync"
2021

2122
sbx "github.com/getlantern/sing-box-extensions"
23+
"go.opentelemetry.io/otel"
2224

2325
C "github.com/getlantern/common"
2426

2527
"github.com/getlantern/radiance/common"
2628
"github.com/getlantern/radiance/internal"
29+
"github.com/getlantern/radiance/traces"
2730

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

3841
trustFingerprintFileName = "trusted_server_fingerprints.json"
42+
43+
tracerName = "github.com/getlantern/radiance/servers"
3944
)
4045

4146
type Options struct {
@@ -447,3 +452,21 @@ func (m *Manager) getClientForTrustedFingerprint(ip string, port int, trustFinge
447452
}
448453
return client, nil
449454
}
455+
456+
// AddServerWithSingboxJSON parse a value that can be a JSON sing-box config.
457+
// It parses the config into a sing-box config and add it to the user managed group.
458+
func (m *Manager) AddServerWithSingboxJSON(ctx context.Context, value []byte) error {
459+
ctx, span := otel.Tracer(tracerName).Start(ctx, "Manager.AddServerWithSingboxJSON")
460+
defer span.End()
461+
var option Options
462+
if err := json.UnmarshalContext(sbx.BoxContext(), value, &option); err != nil {
463+
return traces.RecordError(ctx, fmt.Errorf("failed to parse config: %w", err))
464+
}
465+
if len(option.Endpoints) == 0 && len(option.Outbounds) == 0 {
466+
return traces.RecordError(ctx, fmt.Errorf("no endpoints or outbounds found in the provided configuration"))
467+
}
468+
if err := m.AddServers(SGUser, option); err != nil {
469+
return traces.RecordError(ctx, fmt.Errorf("failed to add servers: %w", err))
470+
}
471+
return nil
472+
}

servers/manager_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package servers
22

33
import (
4+
"context"
5+
"encoding/json"
46
"fmt"
57
"net/http"
68
"net/http/httptest"
@@ -139,3 +141,53 @@ func (s *lanternServerManagerMock) ServeHTTP(w http.ResponseWriter, r *http.Requ
139141

140142
w.WriteHeader(http.StatusNotFound)
141143
}
144+
145+
func TestAddServerWithSingBoxJSON(t *testing.T) {
146+
dataPath := t.TempDir()
147+
manager := &Manager{
148+
servers: Servers{
149+
SGLantern: Options{
150+
Outbounds: make([]option.Outbound, 0),
151+
Endpoints: make([]option.Endpoint, 0),
152+
Locations: make(map[string]C.ServerLocation),
153+
},
154+
SGUser: Options{
155+
Outbounds: make([]option.Outbound, 0),
156+
Endpoints: make([]option.Endpoint, 0),
157+
Locations: make(map[string]C.ServerLocation),
158+
},
159+
},
160+
optsMaps: map[ServerGroup]map[string]any{
161+
SGLantern: make(map[string]any),
162+
SGUser: make(map[string]any),
163+
},
164+
serversFile: filepath.Join(dataPath, common.ServersFileName),
165+
fingerprintsFile: filepath.Join(dataPath, trustFingerprintFileName),
166+
}
167+
168+
ctx := context.Background()
169+
jsonConfig := `
170+
{
171+
"outbounds": [
172+
{
173+
"type": "shadowsocks",
174+
"tag": "ss-out",
175+
"server": "127.0.0.1",
176+
"server_port": 8388,
177+
"method": "chacha20-ietf-poly1305",
178+
"password": "randompasswordwith24char",
179+
"network": "tcp"
180+
}
181+
]
182+
}`
183+
184+
t.Run("adding server with a sing-box json config should work", func(t *testing.T) {
185+
require.NoError(t, manager.AddServerWithSingboxJSON(ctx, []byte(jsonConfig)))
186+
})
187+
t.Run("using a empty config should return an error", func(t *testing.T) {
188+
require.Error(t, manager.AddServerWithSingboxJSON(ctx, []byte{}))
189+
})
190+
t.Run("providing a json that doesn't have any endpoints or outbounds should return a error", func(t *testing.T) {
191+
require.Error(t, manager.AddServerWithSingboxJSON(ctx, json.RawMessage("{}")))
192+
})
193+
}

0 commit comments

Comments
 (0)