mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-27 16:25:31 +03:00
fix(sub): expand CIDR hosts in the hosts-first subscription order
The hosts-first order (#6895) rendered each host through its own endpoint builder, which skipped the CIDR expansion from #6784, so a range host went out as the bare network address. One helper, hostEndpointMaps, now builds a host's entries for both orders.
This commit is contained in:
@@ -104,7 +104,7 @@ func (v subscriptionHostVariant) endpoint(s *SubService, inbound *model.Inbound)
|
|||||||
if v.host == nil {
|
if v.host == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return []map[string]any{hostToExternalProxyMap(v.host, s.resolveInboundAddress(inbound), inbound.Port)}
|
return hostEndpointMaps(v.host, s.resolveInboundAddress(inbound), inbound.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v subscriptionHostVariant) inbound(s *SubService, inbound *model.Inbound) *model.Inbound {
|
func (v subscriptionHostVariant) inbound(s *SubService, inbound *model.Inbound) *model.Inbound {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -255,3 +256,24 @@ func TestSub_GlobalHostOrder(t *testing.T) {
|
|||||||
setHostSortMode(t, "inbound-first")
|
setHostSortMode(t, "inbound-first")
|
||||||
assertAll([]string{"203.0.113.5", "shared1.example.com", "shared2.example.com", "a.example.com", "c.example.com", "b.example.com", "shared1.example.com"})
|
assertAll([]string{"203.0.113.5", "shared1.example.com", "shared2.example.com", "a.example.com", "c.example.com", "b.example.com", "shared1.example.com"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The hosts-first order renders each host on its own, and a CIDR host must still expand into its
|
||||||
|
// random addresses there, as it does in inbound order, on every format.
|
||||||
|
func TestSub_HostOrderExpandsCIDRHosts(t *testing.T) {
|
||||||
|
seedSubDB(t)
|
||||||
|
setHostSortMode(t, "hosts-first")
|
||||||
|
ib := seedSubInbound(t, "order-sub", "cidr", 46910, 0, wsTLSStream)
|
||||||
|
seedHost(t, &model.Host{GroupId: "cidr", InboundId: ib.Id, Remark: "R", Address: "198.51.100.0/24", RandomCount: 3})
|
||||||
|
_, cidr, _ := net.ParseCIDR("198.51.100.0/24")
|
||||||
|
for _, format := range []string{"raw", "clash", "json"} {
|
||||||
|
got := renderedHostAddresses(t, format, NewSubService(""))
|
||||||
|
if len(got) != 3 {
|
||||||
|
t.Fatalf("%s addresses = %v, want 3 random addresses from the range", format, got)
|
||||||
|
}
|
||||||
|
for _, addr := range got {
|
||||||
|
if ip := net.ParseIP(addr); ip == nil || !cidr.Contains(ip) {
|
||||||
|
t.Fatalf("%s address %q is not inside 198.51.100.0/24", format, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+26
-23
@@ -37,33 +37,36 @@ func (s *SubService) hostEndpoints(inbound *model.Inbound, format string) []map[
|
|||||||
if h.IsDisabled || slices.Contains(h.ExcludeFromSubTypes, format) {
|
if h.IsDisabled || slices.Contains(h.ExcludeFromSubTypes, format) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if IsCIDR(h.Address) {
|
eps = append(eps, hostEndpointMaps(h, defaultDest, inbound.Port)...)
|
||||||
count := h.RandomCount
|
|
||||||
if count <= 0 {
|
|
||||||
count = 1
|
|
||||||
}
|
|
||||||
ips, err := RandomIPsFromCIDR(h.Address, count)
|
|
||||||
if err == nil && len(ips) > 0 {
|
|
||||||
for i, ip := range ips {
|
|
||||||
hCopy := *h
|
|
||||||
hCopy.Address = ip
|
|
||||||
if len(ips) > 1 {
|
|
||||||
if h.Remark != "" {
|
|
||||||
hCopy.Remark = fmt.Sprintf("%s - %d", h.Remark, i+1)
|
|
||||||
} else {
|
|
||||||
hCopy.Remark = fmt.Sprintf("%d", i+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eps = append(eps, hostToExternalProxyMap(&hCopy, defaultDest, inbound.Port))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eps = append(eps, hostToExternalProxyMap(h, defaultDest, inbound.Port))
|
|
||||||
}
|
}
|
||||||
return eps
|
return eps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hostEndpointMaps is one host's externalProxy entries: a CIDR address expands into RandomCount
|
||||||
|
// random addresses from the range, any other address is a single entry.
|
||||||
|
func hostEndpointMaps(h *model.Host, defaultDest string, defaultPort int) []map[string]any {
|
||||||
|
if IsCIDR(h.Address) {
|
||||||
|
count := max(h.RandomCount, 1)
|
||||||
|
if ips, err := RandomIPsFromCIDR(h.Address, count); err == nil && len(ips) > 0 {
|
||||||
|
eps := make([]map[string]any, 0, len(ips))
|
||||||
|
for i, ip := range ips {
|
||||||
|
hCopy := *h
|
||||||
|
hCopy.Address = ip
|
||||||
|
if len(ips) > 1 {
|
||||||
|
if h.Remark != "" {
|
||||||
|
hCopy.Remark = fmt.Sprintf("%s - %d", h.Remark, i+1)
|
||||||
|
} else {
|
||||||
|
hCopy.Remark = fmt.Sprintf("%d", i+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eps = append(eps, hostToExternalProxyMap(&hCopy, defaultDest, defaultPort))
|
||||||
|
}
|
||||||
|
return eps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []map[string]any{hostToExternalProxyMap(h, defaultDest, defaultPort)}
|
||||||
|
}
|
||||||
|
|
||||||
// hostToExternalProxyMap projects a Host onto the externalProxy entry shape the
|
// hostToExternalProxyMap projects a Host onto the externalProxy entry shape the
|
||||||
// raw/json/clash renderers already consume. Address/port fall back to the
|
// raw/json/clash renderers already consume. Address/port fall back to the
|
||||||
// inbound's own when the host leaves them blank (override-only host).
|
// inbound's own when the host leaves them blank (override-only host).
|
||||||
|
|||||||
Reference in New Issue
Block a user