diff --git a/internal/sub/service.go b/internal/sub/service.go index 753895255..cc90e3150 100644 --- a/internal/sub/service.go +++ b/internal/sub/service.go @@ -1209,6 +1209,12 @@ func (s *SubService) genHysteriaLink(inbound *model.Inbound, email string) strin protocol = "hysteria" } + // Set before the externalProxy fan-out: a Host overrides only the + // address, so every endpoint inherits the inbound's UDP hop range. + if hopPorts := hysteriaHopPorts(stream); hopPorts != "" { + params["mport"] = hopPorts + } + // Fan out one link per External Proxy entry if any. Previously this // generator ignored `externalProxy` entirely, so the link kept the // server's own IP/port even when the admin configured an alternate @@ -1238,9 +1244,6 @@ func (s *SubService) genHysteriaLink(inbound *model.Inbound, email string) strin // No external proxy configured — use the inbound's resolved address so // node-managed inbounds get the node's host instead of the central panel's. - if hopPorts := hysteriaHopPorts(stream); hopPorts != "" { - params["mport"] = hopPorts - } link := fmt.Sprintf("%s://%s@%s", protocol, auth, joinHostPort(s.resolveInboundAddress(inbound), inbound.Port)) return buildLinkWithParams(link, params, s.genRemark(inbound, email, "", "quic")) } diff --git a/internal/sub/service_test.go b/internal/sub/service_test.go index 043f539e2..5904f1898 100644 --- a/internal/sub/service_test.go +++ b/internal/sub/service_test.go @@ -1189,3 +1189,33 @@ func TestGenHysteriaLinkOmitsFinalMaskQueryParam(t *testing.T) { t.Fatalf("missing standard obfs-password: %s", got) } } + +func TestGenHysteriaLinkKeepsHopPortsWithExternalProxy(t *testing.T) { + stream := `{ + "security":"tls", + "tlsSettings":{"serverName":"hy.sni"}, + "finalmask":{"quicParams":{"udpHop":{"ports":"20000-50000","interval":"5-10"}}}, + "externalProxy":[ + {"dest":"cdn.example.com","port":8443}, + {"dest":"2001:db8::10","port":9443} + ] + }` + in := &model.Inbound{ + Listen: "203.0.113.1", + Port: 443, + Protocol: model.Hysteria, + Remark: "hy2", + Settings: `{"version":2,"clients":[{"auth":"hyauth","email":"user"}]}`, + StreamSettings: stream, + } + got := (&SubService{}).genHysteriaLink(in, "user") + links := strings.Split(got, "\n") + if len(links) != 2 { + t.Fatalf("expected one link per external proxy, got %d: %q", len(links), got) + } + for _, link := range links { + if !strings.Contains(link, "mport=20000-50000") { + t.Fatalf("external-proxy link lost the UDP hop range: %s", link) + } + } +}