Skip to content

Commit bfa6e93

Browse files
committed
feat(inbound): guard protocol/transport by node core; add http/h2 + xhttp transports
1 parent 9c1eccc commit bfa6e93

5 files changed

Lines changed: 97 additions & 5 deletions

File tree

cmd/panel/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func run(ctx context.Context, log *slog.Logger, logBuf *logbuf.Handler, cfg *con
203203
nodeSvc := service.NewNodeService(nodes, h)
204204
nodeSvc.SetLogQuerier(h)
205205
nodeSvc.SetCoreController(h)
206-
inboundSvc := service.NewInboundService(store.Inbounds(), syncSvc)
206+
inboundSvc := service.NewInboundService(store.Inbounds(), nodes, syncSvc)
207207
outboundSvc := service.NewOutboundService(store.Outbounds(), syncSvc)
208208
routingSvc := service.NewRoutingService(store.Routing(), syncSvc)
209209
balancerSvc := service.NewBalancerService(store.Balancers(), syncSvc)

internal/core/xray/config.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,25 @@ func streamSettings(in domain.Inbound) json.RawMessage {
390390
hu["host"] = in.Host[0]
391391
}
392392
ss["httpupgradeSettings"] = hu
393+
case "http", "h2":
394+
ss["network"] = "http" // xray's HTTP/2 transport token
395+
h := map[string]any{}
396+
if in.Path != "" {
397+
h["path"] = in.Path
398+
}
399+
if len(in.Host) > 0 {
400+
h["host"] = in.Host
401+
}
402+
ss["httpSettings"] = h
403+
case "xhttp":
404+
x := map[string]any{"mode": "auto"}
405+
if in.Path != "" {
406+
x["path"] = in.Path
407+
}
408+
if len(in.Host) > 0 {
409+
x["host"] = in.Host[0]
410+
}
411+
ss["xhttpSettings"] = x
393412
}
394413
return mustRaw(ss)
395414
}

internal/panel/service/inbound.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,47 @@ func provisionSecurity(in *domain.Inbound) {
7070
// InboundService manages inbounds and reconciles the owning node's live config
7171
// after every change via the SyncService.
7272
type InboundService struct {
73-
repo port.InboundRepository
74-
sync *SyncService
73+
repo port.InboundRepository
74+
nodes port.NodeRepository
75+
sync *SyncService
7576
}
7677

7778
// NewInboundService wires the service.
78-
func NewInboundService(repo port.InboundRepository, sync *SyncService) *InboundService {
79-
return &InboundService{repo: repo, sync: sync}
79+
func NewInboundService(repo port.InboundRepository, nodes port.NodeRepository, sync *SyncService) *InboundService {
80+
return &InboundService{repo: repo, nodes: nodes, sync: sync}
81+
}
82+
83+
// coreSupports reports whether the given core can run this protocol+network,
84+
// returning a clear error describing the incompatibility (or nil if supported).
85+
func coreSupports(core domain.CoreType, proto domain.Protocol, network string) error {
86+
if network == "" {
87+
network = "tcp"
88+
}
89+
xrayProtos := map[domain.Protocol]bool{domain.ProtoVMess: true, domain.ProtoVLESS: true, domain.ProtoTrojan: true, domain.ProtoShadowsocks: true}
90+
sbProtos := map[domain.Protocol]bool{domain.ProtoVMess: true, domain.ProtoVLESS: true, domain.ProtoTrojan: true, domain.ProtoShadowsocks: true, domain.ProtoHysteria2: true, domain.ProtoTUIC: true}
91+
xrayNets := map[string]bool{"tcp": true, "ws": true, "grpc": true, "httpupgrade": true, "http": true, "h2": true, "xhttp": true}
92+
sbNets := map[string]bool{"tcp": true, "ws": true, "grpc": true, "httpupgrade": true, "http": true, "h2": true}
93+
94+
if proto == domain.ProtoWireGuard {
95+
return fmt.Errorf("protocol %q is not supported as an inbound on any core", proto)
96+
}
97+
switch core {
98+
case domain.CoreSingbox:
99+
if !sbProtos[proto] {
100+
return fmt.Errorf("protocol %q is not supported on the sing-box core", proto)
101+
}
102+
if !sbNets[network] {
103+
return fmt.Errorf("transport %q is not supported on the sing-box core", network)
104+
}
105+
default: // xray (and unspecified)
106+
if !xrayProtos[proto] {
107+
return fmt.Errorf("protocol %q is not supported on the xray core (use a sing-box node)", proto)
108+
}
109+
if !xrayNets[network] {
110+
return fmt.Errorf("transport %q is not supported on the xray core", network)
111+
}
112+
}
113+
return nil
80114
}
81115

82116
// CreateInboundInput describes a new inbound.
@@ -103,6 +137,13 @@ func (s *InboundService) Create(ctx context.Context, in CreateInboundInput) (*do
103137
if in.Tag == "" || in.Port == 0 {
104138
return nil, errors.New("tag and port are required")
105139
}
140+
node, err := s.nodes.GetByID(ctx, in.NodeID)
141+
if err != nil {
142+
return nil, errors.New("node not found")
143+
}
144+
if err := coreSupports(node.Core, in.Protocol, orStr(in.Network, "tcp")); err != nil {
145+
return nil, err
146+
}
106147
inbound := &domain.Inbound{
107148
ID: uuid.New(),
108149
NodeID: in.NodeID,
@@ -161,6 +202,13 @@ func (s *InboundService) Update(ctx context.Context, id uuid.UUID, in UpdateInbo
161202
if err != nil {
162203
return nil, err
163204
}
205+
node, err := s.nodes.GetByID(ctx, existing.NodeID)
206+
if err != nil {
207+
return nil, errors.New("node not found")
208+
}
209+
if err := coreSupports(node.Core, existing.Protocol, orStr(in.Network, "tcp")); err != nil {
210+
return nil, err
211+
}
164212
existing.Listen = in.Listen
165213
existing.Port = in.Port
166214
existing.Network = orStr(in.Network, "tcp")

internal/subscription/clash.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,15 @@ func applyNetwork(base map[string]any, p Proxy) {
121121
opts["headers"] = map[string]any{"Host": p.HostHeader}
122122
}
123123
base["ws-opts"] = opts
124+
case "http", "h2":
125+
base["network"] = "h2"
126+
opts := map[string]any{}
127+
if p.Path != "" {
128+
opts["path"] = p.Path
129+
}
130+
if p.HostHeader != "" {
131+
opts["host"] = []string{p.HostHeader}
132+
}
133+
base["h2-opts"] = opts
124134
}
125135
}

internal/subscription/links.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,21 @@ func transportQuery(p Proxy) url.Values {
9292
if p.HostHeader != "" {
9393
q.Set("host", p.HostHeader)
9494
}
95+
case "http", "h2":
96+
if p.Path != "" {
97+
q.Set("path", p.Path)
98+
}
99+
if p.HostHeader != "" {
100+
q.Set("host", p.HostHeader)
101+
}
102+
case "xhttp":
103+
if p.Path != "" {
104+
q.Set("path", p.Path)
105+
}
106+
if p.HostHeader != "" {
107+
q.Set("host", p.HostHeader)
108+
}
109+
q.Set("mode", "auto")
95110
}
96111
return q
97112
}

0 commit comments

Comments
 (0)