Quellcode durchsuchen

Enhancements
Fix base sing-box configuration.
Add support for adding Clash modes to sing-box configs.
Optimize codes.

Tindy X vor 2 Jahren
Ursprung
Commit
51b65ec437

+ 110 - 24
base/base/all_base.tpl

@@ -269,30 +269,116 @@ enhanced-mode-by-rule = true
 {% if request.target == "singbox" %}
 
 {
-  "log": {
-    "disabled": false,
-    "level": "info",
-    "output": "box.log",
-    "timestamp": true
-  },
-  "dns": {},
-  "ntp": {
-    "enabled": false,
-    "server": "time.apple.com",
-    "server_port": 123,
-    "interval": "30m"
-  },
-  "inbounds": [
-    {
-      "type": "socks",
-      "tag": "socks-in",
-      "listen": "127.0.0.1",
-      "listen_port": 2080
-    }
-  ],
-  "outbounds": [],
-  "route": {},
-  "experimental": {}
+    "log": {
+        "disabled": false,
+        "level": "info",
+        "timestamp": true
+    },
+    "dns": {
+        "servers": [
+            {
+                "tag": "dns_proxy",
+                "address": "tls://1.1.1.1",
+                "address_resolver": "dns_resolver"
+            },
+            {
+                "tag": "dns_direct",
+                "address": "h3://dns.alidns.com/dns-query",
+                "address_resolver": "dns_resolver",
+                "detour": "DIRECT"
+            },
+            {
+                "tag": "dns_fakeip",
+                "address": "fakeip"
+            },
+            {
+                "tag": "dns_resolver",
+                "address": "223.5.5.5",
+                "detour": "DIRECT"
+            },
+            {
+                "tag": "block",
+                "address": "rcode://success"
+            }
+        ],
+        "rules": [
+            {
+                "outbound": [
+                    "any"
+                ],
+                "server": "dns_resolver"
+            },
+            {
+                "geosite": [
+                    "category-ads-all"
+                ],
+                "server": "dns_block",
+                "disable_cache": true
+            },
+            {
+                "geosite": [
+                    "geolocation-!cn"
+                ],
+                "query_type": [
+                    "A",
+                    "AAAA"
+                ],
+                "server": "dns_fakeip"
+            },
+            {
+                "geosite": [
+                    "geolocation-!cn"
+                ],
+                "server": "dns_proxy"
+            }
+        ],
+        "final": "dns_direct",
+        "independent_cache": true,
+        "fakeip": {
+            "enabled": true,
+            {% if default(request.singbox.ipv6, "") == "1" %}
+            "inet6_range": "fc00::\/18",
+            {% endif %}
+            "inet4_range": "198.18.0.0\/15"
+        }
+    },
+    "ntp": {
+        "enabled": true,
+        "server": "time.apple.com",
+        "server_port": 123,
+        "interval": "30m",
+        "detour": "DIRECT"
+    },
+    "inbounds": [
+        {
+            "type": "mixed",
+            "tag": "mixed-in",
+            {% if bool(default(global.singbox.allow_lan, "")) %}
+            "listen": "0.0.0.0",
+            {% else %}
+            "listen": "127.0.0.1",
+            {% endif %}
+            "listen_port": {{ default(global.singbox.mixed_port, "2080") }}
+        },
+        {
+            "type": "tun",
+            "tag": "tun-in",
+            "inet4_address": "172.19.0.1/30",
+            {% if default(request.singbox.ipv6, "") == "1" %}
+            "inet6_address": "fdfe:dcba:9876::1/126",
+            {% endif %}
+            "auto_route": true,
+            "strict_route": true,
+            "stack": "mixed",
+            "sniff": true
+        }
+    ],
+    "outbounds": [],
+    "route": {
+        "rules": [],
+        "auto_detect_interface": true
+    },
+    "experimental": {}
 }
 
 {% endif %}

+ 103 - 25
base/base/singbox.json

@@ -1,26 +1,104 @@
 {
-  "log": {
-    "disabled": false,
-    "level": "info",
-    "output": "box.log",
-    "timestamp": true
-  },
-  "dns": {},
-  "ntp": {
-    "enabled": false,
-    "server": "time.apple.com",
-    "server_port": 123,
-    "interval": "30m"
-  },
-  "inbounds": [
-    {
-      "type": "socks",
-      "tag": "socks-in",
-      "listen": "127.0.0.1",
-      "listen_port": 2080
-    }
-  ],
-  "outbounds": [],
-  "route": {},
-  "experimental": {}
-}
+    "log": {
+        "disabled": false,
+        "level": "info",
+        "timestamp": true
+    },
+    "dns": {
+        "servers": [
+            {
+                "tag": "dns_proxy",
+                "address": "tls://1.1.1.1",
+                "address_resolver": "dns_resolver"
+            },
+            {
+                "tag": "dns_direct",
+                "address": "h3://dns.alidns.com/dns-query",
+                "address_resolver": "dns_resolver",
+                "detour": "DIRECT"
+            },
+            {
+                "tag": "dns_fakeip",
+                "address": "fakeip"
+            },
+            {
+                "tag": "dns_resolver",
+                "address": "223.5.5.5",
+                "detour": "DIRECT"
+            },
+            {
+                "tag": "block",
+                "address": "rcode://success"
+            }
+        ],
+        "rules": [
+            {
+                "outbound": [
+                    "any"
+                ],
+                "server": "dns_resolver"
+            },
+            {
+                "geosite": [
+                    "category-ads-all"
+                ],
+                "server": "dns_block",
+                "disable_cache": true
+            },
+            {
+                "geosite": [
+                    "geolocation-!cn"
+                ],
+                "query_type": [
+                    "A",
+                    "AAAA"
+                ],
+                "server": "dns_fakeip"
+            },
+            {
+                "geosite": [
+                    "geolocation-!cn"
+                ],
+                "server": "dns_proxy"
+            }
+        ],
+        "final": "dns_direct",
+        "independent_cache": true,
+        "fakeip": {
+            "enabled": true,
+            "inet6_range": "fc00::\/18",
+            "inet4_range": "198.18.0.0\/15"
+        }
+    },
+    "ntp": {
+        "enabled": true,
+        "server": "time.apple.com",
+        "server_port": 123,
+        "interval": "30m",
+        "detour": "DIRECT"
+    },
+    "inbounds": [
+        {
+            "type": "mixed",
+            "tag": "mixed-in",
+            "listen": "0.0.0.0",
+            "listen_port": 2080
+        },
+        {
+            "type": "tun",
+            "tag": "tun-in",
+            "inet4_address": "172.19.0.1/30",
+            "inet6_address": "fdfe:dcba:9876::1/126",
+            "auto_route": true,
+            "strict_route": true,
+            "stack": "mixed",
+            "sniff": true
+        }
+    ],
+    "outbounds": [],
+    "route": {
+        "rules": [],
+        "auto_detect_interface": true
+    },
+    "experimental": {}
+}

+ 5 - 0
base/pref.example.ini

@@ -114,6 +114,9 @@ clash_use_new_field_name=true
 ;         key: value
 clash_proxies_style=flow
 
+;add Clash mode to sing-box rules, and add a GLOBAL group to end of outbounds
+singbox_add_clash_modes=true
+
 ;Rename remarks with the following patterns. Supports regular expression.
 ;Format: Search_Pattern@Replace_Pattern
 ;rename_node=IPLC@专线
@@ -226,6 +229,8 @@ clash.http_port=7890
 clash.socks_port=7891
 clash.allow_lan=true
 clash.log_level=info
+singbox.allow_lan=true
+singbox.mixed_port=2080
 
 [aliases]
 ;Aliases for accessing interfaces. Can be used to shorten the URI.

+ 11 - 0
base/pref.example.toml

@@ -140,6 +140,9 @@ clash_use_new_field_name = true
 #         key: value
 clash_proxies_style = "flow"
 
+# add Clash mode to sing-box rules, and add a GLOBAL group to end of outbounds
+singbox_add_clash_modes = true
+
 [[node_pref.rename_node]]
 match = '\(?((x|X)?(\d+)(\.?\d+)?)((\s?倍率?)|(x|X))\)?'
 replace = "$1x"
@@ -237,6 +240,14 @@ value = "true"
 key = "clash.log_level"
 value = "info"
 
+[[template.globals]]
+key = "singbox.allow_lan"
+value = "true"
+
+[[template.globals]]
+key = "singbox.mixed_port"
+value = "2080"
+
 [[aliases]]
 uri = "/clash"
 target = "/sub?target=clash"

+ 3 - 0
base/pref.example.yml

@@ -49,6 +49,7 @@ node_pref:
   append_sub_userinfo: true
   clash_use_new_field_name: true
   clash_proxies_style: flow
+  singbox_add_clash_modes: true
   rename_node:
 #  - {match: "\\(?((x|X)?(\\d+)(\\.?\\d+)?)((\\s?倍率?)|(x|X))\\)?", replace: "$1x"}
 #  - {script: "function rename(node){}"}
@@ -106,6 +107,8 @@ template:
   - {key: clash.socks_port, value: 7891}
   - {key: clash.allow_lan, value: true}
   - {key: clash.log_level, value: info}
+  - {key: singbox.allow_lan, value: true}
+  - {key: singbox.mixed_port, value: 2080}
   
 aliases:
   - {uri: /v, target: /version}

+ 9 - 1
src/generator/config/ruleconvert.cpp

@@ -487,7 +487,7 @@ void rulesetToSingBox(rapidjson::Document &base_rule, std::vector<RulesetContent
     std::string rule_group, retrieved_rules, strLine, final;
     std::stringstream strStrm;
     size_t total_rules = 0;
-    rapidjson::MemoryPoolAllocator<>& allocator = base_rule.GetAllocator();
+    auto &allocator = base_rule.GetAllocator();
 
     rapidjson::Value rules(rapidjson::kArrayType);
     if (!overwrite_original_rules)
@@ -496,6 +496,14 @@ void rulesetToSingBox(rapidjson::Document &base_rule, std::vector<RulesetContent
             rules.Swap(base_rule["route"]["rules"]);
     }
 
+    if (global.singBoxAddClashModes)
+    {
+        auto global_object = buildObject(allocator, "clash_mode", "Global", "outbound", "GLOBAL");
+        auto direct_object = buildObject(allocator, "clash_mode", "Direct", "outbound", "DIRECT");
+        rules.PushBack(global_object, allocator);
+        rules.PushBack(direct_object, allocator);
+    }
+
     for(RulesetContent &x : ruleset_content_array)
     {
         if(global.maxAllowedRules && total_rules > global.maxAllowedRules)

+ 60 - 46
src/generator/config/subexport.cpp

@@ -155,29 +155,29 @@ bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &
     return true;
 }
 
-void processRemark(std::string &oldremark, std::string &newremark, string_array &remarks_list, bool proc_comma = true)
+void processRemark(std::string &remark, string_array &remarks_list, bool proc_comma = true)
 {
     // Replace every '=' with '-' in the remark string to avoid parse errors from the clients.
     //     Surge is tested to yield an error when handling '=' in the remark string, 
     //     not sure if other clients have the same problem.
-    std::replace(oldremark.begin(), oldremark.end(), '=', '-');
+    std::replace(remark.begin(), remark.end(), '=', '-');
 
     if(proc_comma)
     {
-        if(oldremark.find(',') != std::string::npos)
+        if(remark.find(',') != std::string::npos)
         {
-            oldremark.insert(0, "\"");
-            oldremark.append("\"");
+            remark.insert(0, "\"");
+            remark.append("\"");
         }
     }
-    newremark = oldremark;
+    std::string tempRemark = remark;
     int cnt = 2;
-    while(std::find(remarks_list.begin(), remarks_list.end(), newremark) != remarks_list.end())
+    while(std::find(remarks_list.begin(), remarks_list.end(), tempRemark) != remarks_list.end())
     {
-        newremark = oldremark + " " + std::to_string(cnt);
+        tempRemark = remark + " " + std::to_string(cnt);
         cnt++;
     }
-    oldremark = newremark;
+    remark = tempRemark;
 }
 
 void groupGenerate(const std::string &rule, std::vector<Proxy> &nodelist, string_array &filtered_nodelist, bool add_direct, extra_settings &ext)
@@ -241,18 +241,18 @@ void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const ProxyGr
         YAML::Node singleproxy;
 
         std::string type = getProxyTypeName(x.Type);
-        std::string remark, pluginopts = replaceAllDistinct(x.PluginOption, ";", "&");
+        std::string pluginopts = replaceAllDistinct(x.PluginOption, ";", "&");
         if(ext.append_proxy_type)
             x.Remark = "[" + type + "] " + x.Remark;
 
-        processRemark(x.Remark, remark, remarks_list, false);
+        processRemark(x.Remark, remarks_list, false);
 
         tribool udp = ext.udp;
         tribool scv = ext.skip_cert_verify;
         udp.define(x.UDP);
         scv.define(x.AllowInsecure);
 
-        singleproxy["name"] = remark;
+        singleproxy["name"] = x.Remark;
         singleproxy["server"] = x.Hostname;
         singleproxy["port"] = x.Port;
 
@@ -471,7 +471,7 @@ void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const ProxyGr
         else
             singleproxy.SetStyle(YAML::EmitterStyle::Flow);
         proxies.push_back(singleproxy);
-        remarks_list.emplace_back(std::move(remark));
+        remarks_list.emplace_back(x.Remark);
         nodelist.emplace_back(x);
     }
 
@@ -657,14 +657,13 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
 
     for(Proxy &x : nodes)
     {
-        std::string remark;
         if(ext.append_proxy_type)
         {
             std::string type = getProxyTypeName(x.Type);
             x.Remark = "[" + type + "] " + x.Remark;
         }
 
-        processRemark(x.Remark, remark, remarks_list);
+        processRemark(x.Remark, remarks_list);
 
         std::string &hostname = x.Hostname, &username = x.Username, &password = x.Password, &method = x.EncryptMethod, &id = x.UserId, &transproto = x.TransferProtocol, &host = x.Host, &edge = x.Edge, &path = x.Path, &protocol = x.Protocol, &protoparam = x.ProtocolParam, &obfs = x.OBFS, &obfsparam = x.OBFSParam, &plugin = x.Plugin, &pluginopts = x.PluginOption;
         std::string port = std::to_string(x.Port);
@@ -834,13 +833,13 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
             proxy += ", udp-relay=" + udp.get_str();
 
         if(ext.nodelist)
-            output_nodelist += remark + " = " + proxy + "\n";
+            output_nodelist += x.Remark + " = " + proxy + "\n";
         else
         {
-            ini.set("{NONAME}", remark + " = " + proxy);
+            ini.set("{NONAME}", x.Remark + " = " + proxy);
             nodelist.emplace_back(x);
         }
-        remarks_list.emplace_back(std::move(remark));
+        remarks_list.emplace_back(x.Remark);
     }
 
     if(ext.nodelist)
@@ -1083,15 +1082,13 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetC
     ini.erase_section();
     for(Proxy &x : nodes)
     {
-        std::string remark = x.Remark;
-
         if(ext.append_proxy_type)
         {
             std::string type = getProxyTypeName(x.Type);
             x.Remark = "[" + type + "] " + x.Remark;
         }
 
-        processRemark(x.Remark, remark, remarks_list);
+        processRemark(x.Remark, remarks_list);
 
         std::string &hostname = x.Hostname, &method = x.EncryptMethod, &password = x.Password, &id = x.UserId, &transproto = x.TransferProtocol, &host = x.Host, &path = x.Path, &edge = x.Edge, &protocol = x.Protocol, &protoparam = x.ProtocolParam, &obfs = x.OBFS, &obfsparam = x.OBFSParam, &plugin = x.Plugin, &pluginopts = x.PluginOption, &username = x.Username;
         std::string port = std::to_string(x.Port);
@@ -1106,7 +1103,7 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetC
 
             if(method == "auto")
                 method = "chacha20-ietf-poly1305";
-            proxyStr = remark + " = vmess, " + hostname + ", " + port + ", " + method + ", \"" + id + "\", group=" + x.Group;
+            proxyStr = x.Remark + " = vmess, " + hostname + ", " + port + ", " + method + ", \"" + id + "\", group=" + x.Group;
             if(tlssecure)
             {
                 proxyStr += ", over-tls=true, tls-host=" + host;
@@ -1128,12 +1125,12 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetC
             if(ext.nodelist)
             {
                 proxyStr = "ssr://" + urlSafeBase64Encode(hostname + ":" + port + ":" + protocol + ":" + method + ":" + obfs + ":" + urlSafeBase64Encode(password) \
-                           + "/?group=" + urlSafeBase64Encode(x.Group) + "&remarks=" + urlSafeBase64Encode(remark) \
+                           + "/?group=" + urlSafeBase64Encode(x.Group) + "&remarks=" + urlSafeBase64Encode(x.Remark) \
                            + "&obfsparam=" + urlSafeBase64Encode(obfsparam) + "&protoparam=" + urlSafeBase64Encode(protoparam));
             }
             else
             {
-                proxyStr = remark + " = shadowsocksr, " + hostname + ", " + port + ", " + method + ", \"" + password + "\", group=" + x.Group + ", protocol=" + protocol + ", obfs=" + obfs;
+                proxyStr = x.Remark + " = shadowsocksr, " + hostname + ", " + port + ", " + method + ", \"" + password + "\", group=" + x.Group + ", protocol=" + protocol + ", obfs=" + obfs;
                 if(!protoparam.empty())
                     proxyStr += ", protocol_param=" + protoparam;
                 if(!obfsparam.empty())
@@ -1148,11 +1145,11 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetC
                 {
                     proxyStr += "/?plugin=" + urlEncode(plugin + ";" + pluginopts);
                 }
-                proxyStr += "&group=" + urlSafeBase64Encode(x.Group) + "#" + urlEncode(remark);
+                proxyStr += "&group=" + urlSafeBase64Encode(x.Group) + "#" + urlEncode(x.Remark);
             }
             else
             {
-                proxyStr = remark + " = shadowsocks, " + hostname + ", " + port + ", " + method + ", \"" + password + "\", group=" + x.Group;
+                proxyStr = x.Remark + " = shadowsocks, " + hostname + ", " + port + ", " + method + ", \"" + password + "\", group=" + x.Group;
                 if(plugin == "obfs-local" && !pluginopts.empty())
                 {
                     proxyStr += ", " + replaceAllDistinct(pluginopts, ";", ", ");
@@ -1161,7 +1158,7 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetC
             break;
         case ProxyType::HTTP:
         case ProxyType::HTTPS:
-            proxyStr = remark + " = http, upstream-proxy-address=" + hostname + ", upstream-proxy-port=" + port + ", group=" + x.Group;
+            proxyStr = x.Remark + " = http, upstream-proxy-address=" + hostname + ", upstream-proxy-port=" + port + ", group=" + x.Group;
             if(!username.empty() && !password.empty())
                 proxyStr += ", upstream-proxy-auth=true, upstream-proxy-username=" + username + ", upstream-proxy-password=" + password;
             else
@@ -1180,7 +1177,7 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetC
                 proxyStr = "http://" + urlSafeBase64Encode(proxyStr);
             break;
         case ProxyType::SOCKS5:
-            proxyStr = remark + " = socks, upstream-proxy-address=" + hostname + ", upstream-proxy-port=" + port + ", group=" + x.Group;
+            proxyStr = x.Remark + " = socks, upstream-proxy-address=" + hostname + ", upstream-proxy-port=" + port + ", group=" + x.Group;
             if(!username.empty() && !password.empty())
                 proxyStr += ", upstream-proxy-auth=true, upstream-proxy-username=" + username + ", upstream-proxy-password=" + password;
             else
@@ -1203,7 +1200,7 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetC
         }
 
         ini.set("{NONAME}", proxyStr);
-        remarks_list.emplace_back(std::move(remark));
+        remarks_list.emplace_back(x.Remark);
         nodelist.emplace_back(x);
     }
 
@@ -1311,7 +1308,6 @@ std::string proxyToQuanX(std::vector<Proxy> &nodes, const std::string &base_conf
 void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
 {
     std::string type;
-    std::string remark;
     std::string proxyStr;
     tribool udp, tfo, scv, tls13;
     std::vector<Proxy> nodelist;
@@ -1324,7 +1320,7 @@ void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<Ruleset
         if(ext.append_proxy_type)
             x.Remark = "[" + type + "] " + x.Remark;
 
-        processRemark(x.Remark, remark, remarks_list);
+        processRemark(x.Remark, remarks_list);
 
         std::string &hostname = x.Hostname, &method = x.EncryptMethod, &id = x.UserId, &transproto = x.TransferProtocol, &host = x.Host, &path = x.Path, &password = x.Password, &plugin = x.Plugin, &pluginopts = x.PluginOption, &protocol = x.Protocol, &protoparam = x.ProtocolParam, &obfs = x.OBFS, &obfsparam = x.OBFSParam, &username = x.Username;
         std::string port = std::to_string(x.Port);
@@ -1455,10 +1451,10 @@ void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<Ruleset
             proxyStr += ", udp-relay=" + udp.get_str();
         if(tlssecure && !scv.is_undef() && (x.Type != ProxyType::Shadowsocks && x.Type != ProxyType::ShadowsocksR))
             proxyStr += ", tls-verification=" + scv.reverse().get_str();
-        proxyStr += ", tag=" + remark;
+        proxyStr += ", tag=" + x.Remark;
 
         ini.set("{NONAME}", proxyStr);
-        remarks_list.emplace_back(std::move(remark));
+        remarks_list.emplace_back(x.Remark);
         nodelist.emplace_back(x);
     }
 
@@ -1661,7 +1657,7 @@ std::string proxyToMellow(std::vector<Proxy> &nodes, const std::string &base_con
 void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
 {
     std::string proxy;
-    std::string remark, username, password, method;
+    std::string username, password, method;
     std::string plugin, pluginopts;
     std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret, tlssecure;
     std::string url;
@@ -1679,7 +1675,7 @@ void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<Rulese
             x.Remark = "[" + type + "] " + x.Remark;
         }
 
-        processRemark(x.Remark, remark, remarks_list);
+        processRemark(x.Remark, remarks_list);
 
         std::string &hostname = x.Hostname, port = std::to_string(x.Port);
 
@@ -1693,10 +1689,10 @@ void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<Rulese
         case ProxyType::Shadowsocks:
             if(!x.Plugin.empty())
                 continue;
-            proxy = remark + ", ss, ss://" + urlSafeBase64Encode(method + ":" + password) + "@" + hostname + ":" + port;
+            proxy = x.Remark + ", ss, ss://" + urlSafeBase64Encode(method + ":" + password) + "@" + hostname + ":" + port;
             break;
         case ProxyType::VMess:
-            proxy = remark + ", vmess1, vmess1://" + id + "@" + hostname + ":" + port;
+            proxy = x.Remark + ", vmess1, vmess1://" + id + "@" + hostname + ":" + port;
             if(!path.empty())
                 proxy += path;
             proxy += "?network=" + transproto;
@@ -1729,17 +1725,17 @@ void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<Rulese
                 proxy += "&sockopt.tcpfastopen=" + tfo.get_str();
             break;
         case ProxyType::SOCKS5:
-            proxy = remark + ", builtin, socks, address=" + hostname + ", port=" + port + ", user=" + username + ", pass=" + password;
+            proxy = x.Remark + ", builtin, socks, address=" + hostname + ", port=" + port + ", user=" + username + ", pass=" + password;
             break;
         case ProxyType::HTTP:
-            proxy = remark + ", builtin, http, address=" + hostname + ", port=" + port + ", user=" + username + ", pass=" + password;
+            proxy = x.Remark + ", builtin, http, address=" + hostname + ", port=" + port + ", user=" + username + ", pass=" + password;
             break;
         default:
             continue;
         }
 
         ini.set("{NONAME}", proxy);
-        remarks_list.emplace_back(std::move(remark));
+        remarks_list.emplace_back(x.Remark);
         nodelist.emplace_back(x);
     }
 
@@ -1824,8 +1820,7 @@ std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
             std::string type = getProxyTypeName(x.Type);
             x.Remark = "[" + type + "] " + x.Remark;
         }
-        std::string remark = x.Remark;
-        processRemark(x.Remark, remark, remarks_list);
+        processRemark(x.Remark, remarks_list);
 
         std::string &hostname = x.Hostname, &username = x.Username, &password = x.Password, &method = x.EncryptMethod, &plugin = x.Plugin, &pluginopts = x.PluginOption, &id = x.UserId, &transproto = x.TransferProtocol, &host = x.Host, &path = x.Path, &protocol = x.Protocol, &protoparam = x.ProtocolParam, &obfs = x.OBFS, &obfsparam = x.OBFSParam;
         std::string port = std::to_string(x.Port), aid = std::to_string(x.AlterId);
@@ -1931,12 +1926,12 @@ std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
 
 
         if(ext.nodelist)
-            output_nodelist += remark + " = " + proxy + "\n";
+            output_nodelist += x.Remark + " = " + proxy + "\n";
         else
         {
-            ini.set("{NONAME}", remark + " = " + proxy);
+            ini.set("{NONAME}", x.Remark + " = " + proxy);
             nodelist.emplace_back(x);
-            remarks_list.emplace_back(std::move(remark));
+            remarks_list.emplace_back(x.Remark);
         }
     }
 
@@ -2096,6 +2091,7 @@ void proxyToSingBox(std::vector<Proxy> &nodes, rapidjson::Document &json, std::v
     rapidjson::Document::AllocatorType &allocator = json.GetAllocator();
     rapidjson::Value outbounds(rapidjson::kArrayType), route(rapidjson::kArrayType);
     std::vector<Proxy> nodelist;
+    string_array remarks_list;
 
     auto direct = buildObject(allocator, "type", "direct", "tag", "DIRECT");
     outbounds.PushBack(direct, allocator);
@@ -2108,6 +2104,8 @@ void proxyToSingBox(std::vector<Proxy> &nodes, rapidjson::Document &json, std::v
         if (ext.append_proxy_type)
             x.Remark = "[" + type + "] " + x.Remark;
 
+        processRemark(x.Remark, remarks_list, false);
+
         tribool udp = ext.udp, tfo = ext.tfo, scv = ext.skip_cert_verify;
         udp.define(x.UDP);
         tfo.define(x.TCPFastOpen);
@@ -2118,7 +2116,7 @@ void proxyToSingBox(std::vector<Proxy> &nodes, rapidjson::Document &json, std::v
         {
             case ProxyType::Shadowsocks:
             {
-                addSingBoxCommonMembers(proxy, x, "ss", allocator);
+                addSingBoxCommonMembers(proxy, x, "shadowsocks", allocator);
                 proxy.AddMember("method", rapidjson::StringRef(x.EncryptMethod.c_str()), allocator);
                 proxy.AddMember("password", rapidjson::StringRef(x.Password.c_str()), allocator);
                 if(!x.Plugin.empty() && !x.PluginOption.empty())
@@ -2243,6 +2241,7 @@ void proxyToSingBox(std::vector<Proxy> &nodes, rapidjson::Document &json, std::v
             proxy.AddMember("tcp_fast_open", buildBooleanValue(tfo), allocator);
         }
         nodelist.push_back(x);
+        remarks_list.emplace_back(x.Remark);
         outbounds.PushBack(proxy, allocator);
     }
     for (const ProxyGroupConfig &x: extra_proxy_group)
@@ -2293,6 +2292,21 @@ void proxyToSingBox(std::vector<Proxy> &nodes, rapidjson::Document &json, std::v
         }
         outbounds.PushBack(group, allocator);
     }
+
+    if (global.singBoxAddClashModes)
+    {
+        auto global_group = rapidjson::Value(rapidjson::kObjectType);
+        global_group.AddMember("type", "selector", allocator);
+        global_group.AddMember("tag", "GLOBAL", allocator);
+        global_group.AddMember("outbounds", rapidjson::Value(rapidjson::kArrayType), allocator);
+        global_group["outbounds"].PushBack("DIRECT", allocator);
+        for (auto &x: remarks_list)
+        {
+            global_group["outbounds"].PushBack(rapidjson::Value(x.c_str(), allocator), allocator);
+        }
+        outbounds.PushBack(global_group, allocator);
+    }
+
     json | AddMemberOrReplace("outbounds", outbounds, allocator);
 }
 

+ 4 - 1
src/handler/settings.cpp

@@ -376,6 +376,7 @@ void readYAMLConf(YAML::Node &node)
         section["append_sub_userinfo"] >> global.appendUserinfo;
         section["clash_use_new_field_name"] >> global.clashUseNewField;
         section["clash_proxies_style"] >> global.clashProxiesStyle;
+        section["singbox_add_clash_modes"] >> global.singBoxAddClashModes;
     }
 
     if(section["rename_node"].IsSequence())
@@ -634,7 +635,8 @@ void readTOMLConf(toml::value &root)
                   "filter_deprecated_nodes", global.filterDeprecated,
                   "append_sub_userinfo", global.appendUserinfo,
                   "clash_use_new_field_name", global.clashUseNewField,
-                  "clash_proxies_style", global.clashProxiesStyle
+                  "clash_proxies_style", global.clashProxiesStyle,
+                  "singbox_add_clash_modes", global.singBoxAddClashModes
     );
 
     auto renameconfs = toml::find_or<std::vector<toml::value>>(section_node_pref, "rename_node", {});
@@ -878,6 +880,7 @@ void readConf()
         ini.get_bool_if_exist("append_sub_userinfo", global.appendUserinfo);
         ini.get_bool_if_exist("clash_use_new_field_name", global.clashUseNewField);
         ini.get_if_exist("clash_proxies_style", global.clashProxiesStyle);
+        ini.get_bool_if_exist("singbox_add_clash_modes", global.singBoxAddClashModes);
         if(ini.item_prefix_exist("rename_node"))
         {
             ini.get_all("rename_node", tempArray);

+ 1 - 1
src/handler/settings.h

@@ -46,7 +46,7 @@ struct Settings
     bool addEmoji = false, removeEmoji = false, appendType = false, filterDeprecated = true;
     tribool UDPFlag, TFOFlag, skipCertVerify, TLS13Flag, enableInsert;
     bool enableSort = false, updateStrict = false;
-    bool clashUseNewField = false;
+    bool clashUseNewField = false, singBoxAddClashModes = true;
     std::string clashProxiesStyle = "flow";
     std::string proxyConfig, proxyRuleset, proxySubscription;
     int updateInterval = 0;