endpoint.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package sub
  2. import (
  3. "strings"
  4. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  5. )
  6. // ShareEndpoint is one render target for a subscription link: the address/port
  7. // to dial plus an optional set of TLS overrides. It unifies two sources behind
  8. // one type so the per-protocol link builders don't branch on where the override
  9. // came from:
  10. //
  11. // - a legacy externalProxy entry (Phase 1): the source map is carried in `ep`
  12. // and applied through the unchanged applyExternalProxyTLS* helpers, so the
  13. // emitted link is byte-identical to the pre-refactor output;
  14. // - a Host row (Phase 4): leaves `ep` nil and uses typed override fields.
  15. //
  16. // ForceTls is the verbatim "same"/"tls"/"none"/"" value — never pre-resolved,
  17. // because three behaviors branch on the raw string (keep-base, obj["tls"]
  18. // rewrite, none-strip).
  19. type ShareEndpoint struct {
  20. Address string
  21. Port int
  22. Remark string // extra remark slot fed to genRemark, not a rendered remark
  23. ForceTls string
  24. // ep is the source externalProxy entry. nil for host/default endpoints.
  25. ep map[string]any
  26. }
  27. // externalProxyToEndpoint maps one externalProxy entry to an endpoint that
  28. // carries the entry for delegated, provably-identical TLS application.
  29. func externalProxyToEndpoint(ep map[string]any) ShareEndpoint {
  30. e := ShareEndpoint{ep: ep}
  31. e.Address, _ = ep["dest"].(string)
  32. if p, ok := ep["port"].(float64); ok {
  33. e.Port = int(p)
  34. }
  35. e.Remark, _ = ep["remark"].(string)
  36. e.ForceTls, _ = ep["forceTls"].(string)
  37. return e
  38. }
  39. // inboundDefaultEndpoint is the endpoint for an inbound's own resolved
  40. // address/port (the no-externalProxy default). forceTls "same" keeps the base
  41. // security; no per-endpoint TLS override.
  42. func (s *SubService) inboundDefaultEndpoint(inbound *model.Inbound) ShareEndpoint {
  43. return ShareEndpoint{
  44. Address: s.resolveInboundAddress(inbound),
  45. Port: inbound.Port,
  46. ForceTls: "same",
  47. }
  48. }
  49. // applyEndpointTLSParams applies an endpoint's TLS overrides onto a URL-param
  50. // map. External-proxy endpoints delegate to the unchanged helper; host/default
  51. // endpoints carry no override yet (Phase 4).
  52. func applyEndpointTLSParams(e ShareEndpoint, params map[string]string, security string) {
  53. if e.ep != nil {
  54. applyExternalProxyTLSParams(e.ep, params, security)
  55. }
  56. }
  57. // applyEndpointTLSObj is applyEndpointTLSParams for the VMess base64-JSON form.
  58. func applyEndpointTLSObj(e ShareEndpoint, obj map[string]any, security string) {
  59. if e.ep != nil {
  60. applyExternalProxyTLSObj(e.ep, obj, security)
  61. }
  62. }
  63. // buildEndpointLinks renders one URL-param link per endpoint (vless/trojan/ss).
  64. // securityToApply mirrors the legacy externalProxy loop: "same" keeps the base
  65. // security, otherwise the endpoint's forceTls wins; "none" strips TLS hint
  66. // fields at emit time.
  67. func (s *SubService) buildEndpointLinks(
  68. eps []ShareEndpoint,
  69. params map[string]string,
  70. baseSecurity string,
  71. makeLink func(dest string, port int) string,
  72. makeRemark func(e ShareEndpoint) string,
  73. ) string {
  74. links := make([]string, 0, len(eps))
  75. for _, e := range eps {
  76. securityToApply := baseSecurity
  77. if e.ForceTls != "same" {
  78. securityToApply = e.ForceTls
  79. }
  80. nextParams := cloneStringMap(params)
  81. applyEndpointTLSParams(e, nextParams, securityToApply)
  82. applyEndpointRealityParams(e, nextParams, securityToApply)
  83. applyEndpointHostPath(e, nextParams)
  84. applyEndpointAllowInsecure(e, nextParams, securityToApply)
  85. links = append(links, buildLinkWithParamsAndSecurity(
  86. makeLink(e.Address, e.Port),
  87. nextParams,
  88. makeRemark(e),
  89. securityToApply,
  90. e.ForceTls == "none",
  91. ))
  92. }
  93. return strings.Join(links, "\n")
  94. }
  95. // buildEndpointVmessLinks renders one VMess base64-JSON link per endpoint.
  96. func (s *SubService) buildEndpointVmessLinks(eps []ShareEndpoint, baseObj map[string]any, inbound *model.Inbound, email string) string {
  97. var links strings.Builder
  98. for index, e := range eps {
  99. securityToApply, _ := baseObj["tls"].(string)
  100. if e.ForceTls != "same" {
  101. securityToApply = e.ForceTls
  102. }
  103. newObj := cloneVmessShareObj(baseObj, e.ForceTls)
  104. newObj["ps"] = s.endpointRemark(inbound, email, e.ep)
  105. newObj["add"] = e.Address
  106. newObj["port"] = e.Port
  107. if e.ForceTls != "same" {
  108. newObj["tls"] = e.ForceTls
  109. }
  110. applyEndpointTLSObj(e, newObj, securityToApply)
  111. applyEndpointHostPathObj(e, newObj)
  112. if index > 0 {
  113. links.WriteString("\n")
  114. }
  115. links.WriteString(buildVmessLink(newObj))
  116. }
  117. return links.String()
  118. }