Skip to content

Commit 5e0cb7c

Browse files
committed
fix(inbounds): carry the Shadowsocks method through the request mapping
inboundReq had no method field, so creating an SS inbound was impossible in both directions: the strict external decoder rejected the request outright (unknown field "method"), and the lenient panel decoder dropped the field silently, leaving the operator's cipher choice to vanish. No model-level test caught it — they build the Inbound directly, bypassing the request mapping. Found by driving a real SS client end-to-end against the deployed panel: tunnel carries traffic and per-user stats attribute it correctly.
1 parent 09d0972 commit 5e0cb7c

2 files changed

Lines changed: 37 additions & 0 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package server
2+
3+
import (
4+
"testing"
5+
6+
"github.com/AppsGanin/rospanel/internal/model"
7+
)
8+
9+
// The Shadowsocks method must survive the request→model mapping. It very nearly
10+
// didn't: inboundReq had no method field, so the strict external decoder rejected
11+
// the request outright ("unknown field method") and the lenient panel decoder
12+
// dropped it silently — an operator's choice of cipher vanishing on the way in,
13+
// which is the worse of the two. The whole feature is unreachable without this, and
14+
// no model-level test sees it because they build the Inbound directly.
15+
func TestInboundReqCarriesShadowsocksMethod(t *testing.T) {
16+
req := inboundReq{
17+
Name: "ss", Protocol: model.InbShadowsocks, Port: 24900,
18+
Method: model.SS2022AES256,
19+
}
20+
in, err := req.toModel(model.LocalNodeID, 0)
21+
if err != nil {
22+
t.Fatalf("toModel: %v", err)
23+
}
24+
if in.Opts.Method != model.SS2022AES256 {
25+
t.Errorf("method lost in mapping: got %q, want %q", in.Opts.Method, model.SS2022AES256)
26+
}
27+
// And it normalizes + validates as a real inbound once the panel fills the key.
28+
in.Normalize()
29+
if in.Opts.Method != model.SS2022AES256 {
30+
t.Errorf("normalize dropped the chosen method: %q", in.Opts.Method)
31+
}
32+
}

internal/server/panel_inbounds.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ type inboundReq struct {
4747
HopEnd int `json:"hop_end"`
4848
HopInterval string `json:"hop_interval"`
4949

50+
// Shadowsocks-2022 method. The server key is generated (prepareInbound), so it is
51+
// deliberately not a request field.
52+
Method string `json:"method"`
53+
5054
// Advanced. Each transport knob is its own typed field; the three JSON-blob
5155
// sections (XHTTP extra, sockopt, extra TLS) arrive as structured forms that the
5256
// server assembles into the blob Xray reads. Every form carries a Raw escape hatch
@@ -91,6 +95,7 @@ func (r inboundReq) toModel(serverID, id int64) (model.Inbound, error) {
9195
HeaderPaths: r.HeaderPaths,
9296
Authority: r.Authority,
9397
MultiMode: r.MultiMode,
98+
Method: r.Method,
9499
},
95100
}
96101
if r.RealityAntiReplay {

0 commit comments

Comments
 (0)