Files
elmprodvpn/selective-vpn-api/app/transport_bootstrap_bypass_candidates.go

133 lines
2.9 KiB
Go

package app
import (
"encoding/json"
"fmt"
"os"
"strings"
)
func transportCollectBootstrapCandidates(client TransportClient) []string {
candidates := make([]string, 0, 16)
keys := []string{
"bootstrap_endpoint",
"bootstrap_endpoints",
"bootstrap_host",
"bootstrap_hosts",
"endpoint",
"endpoint_host",
"remote_host",
"server",
"host",
"url",
"uri",
"link",
"ssh_host",
}
for _, key := range keys {
candidates = append(candidates, transportConfigValues(client.Config, key)...)
}
if client.Kind == TransportClientSingBox {
candidates = append(candidates, transportCollectSingBoxConfigCandidates(client)...)
}
return dedupeStrings(candidates)
}
func transportCollectSingBoxConfigCandidates(client TransportClient) []string {
path := transportSingBoxConfigPath(client)
if path == "" {
return nil
}
data, err := os.ReadFile(path)
if err != nil || len(data) == 0 {
return nil
}
var root map[string]any
if err := json.Unmarshal(data, &root); err != nil {
return nil
}
rawOutbounds, ok := root["outbounds"].([]any)
if !ok || len(rawOutbounds) == 0 {
return nil
}
out := make([]string, 0, 8)
for _, raw := range rawOutbounds {
transportCollectServerCandidatesRecursive(raw, &out)
}
return dedupeStrings(out)
}
func transportCollectServerCandidatesRecursive(node any, out *[]string) {
switch v := node.(type) {
case map[string]any:
for key, val := range v {
k := strings.ToLower(strings.TrimSpace(key))
if k == "server" || k == "address" || k == "host" {
*out = append(*out, transportValueToStrings(val)...)
}
transportCollectServerCandidatesRecursive(val, out)
}
case []any:
for _, item := range v {
transportCollectServerCandidatesRecursive(item, out)
}
}
}
func transportConfigValues(cfg map[string]any, key string) []string {
if cfg == nil {
return nil
}
raw, ok := cfg[key]
if !ok || raw == nil {
return nil
}
return transportValueToStrings(raw)
}
func transportValueToStrings(raw any) []string {
switch v := raw.(type) {
case string:
return transportSplitValue(v)
case []string:
out := make([]string, 0, len(v))
for _, it := range v {
out = append(out, transportSplitValue(it)...)
}
return out
case []any:
out := make([]string, 0, len(v))
for _, it := range v {
switch vv := it.(type) {
case string:
out = append(out, transportSplitValue(vv)...)
default:
out = append(out, transportSplitValue(fmt.Sprint(vv))...)
}
}
return out
default:
return transportSplitValue(fmt.Sprint(v))
}
}
func transportSplitValue(raw string) []string {
s := strings.TrimSpace(raw)
if s == "" {
return nil
}
if strings.ContainsAny(s, ",;\n\t") {
repl := strings.NewReplacer(",", " ", ";", " ", "\n", " ", "\t", " ")
fields := strings.Fields(repl.Replace(s))
out := make([]string, 0, len(fields))
for _, f := range fields {
ff := strings.TrimSpace(f)
if ff != "" {
out = append(out, ff)
}
}
return out
}
return []string{s}
}