mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-26 20:21:35 +08:00
Update On Mon Apr 28 20:38:09 CEST 2025
This commit is contained in:
1
.github/update.log
vendored
1
.github/update.log
vendored
@@ -982,3 +982,4 @@ Update On Fri Apr 25 14:32:58 CEST 2025
|
||||
Update On Fri Apr 25 20:35:32 CEST 2025
|
||||
Update On Sat Apr 26 20:33:12 CEST 2025
|
||||
Update On Sun Apr 27 20:32:16 CEST 2025
|
||||
Update On Mon Apr 28 20:38:01 CEST 2025
|
||||
|
@@ -7,13 +7,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
"github.com/metacubex/mihomo/common/batch"
|
||||
"github.com/metacubex/mihomo/common/singledo"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type HealthCheckOption struct {
|
||||
@@ -147,7 +147,8 @@ func (hc *HealthCheck) check() {
|
||||
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
||||
id := utils.NewUUIDV4().String()
|
||||
log.Debugln("Start New Health Checking {%s}", id)
|
||||
b, _ := batch.New[bool](hc.ctx, batch.WithConcurrencyNum[bool](10))
|
||||
b := new(errgroup.Group)
|
||||
b.SetLimit(10)
|
||||
|
||||
// execute default health check
|
||||
option := &extraOption{filters: nil, expectedStatus: hc.expectedStatus}
|
||||
@@ -159,13 +160,13 @@ func (hc *HealthCheck) check() {
|
||||
hc.execute(b, url, id, option)
|
||||
}
|
||||
}
|
||||
b.Wait()
|
||||
_ = b.Wait()
|
||||
log.Debugln("Finish A Health Checking {%s}", id)
|
||||
return struct{}{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *extraOption) {
|
||||
func (hc *HealthCheck) execute(b *errgroup.Group, url, uid string, option *extraOption) {
|
||||
url = strings.TrimSpace(url)
|
||||
if len(url) == 0 {
|
||||
log.Debugln("Health Check has been skipped due to testUrl is empty, {%s}", uid)
|
||||
@@ -195,13 +196,13 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex
|
||||
}
|
||||
|
||||
p := proxy
|
||||
b.Go(p.Name(), func() (bool, error) {
|
||||
b.Go(func() error {
|
||||
ctx, cancel := context.WithTimeout(hc.ctx, hc.timeout)
|
||||
defer cancel()
|
||||
log.Debugln("Health Checking, proxy: %s, url: %s, id: {%s}", p.Name(), url, uid)
|
||||
_, _ = p.URLTest(ctx, url, expectedStatus)
|
||||
log.Debugln("Health Checked, proxy: %s, url: %s, alive: %t, delay: %d ms uid: {%s}", p.Name(), url, p.AliveForTestUrl(url), p.LastDelayForTestUrl(url), uid)
|
||||
return false, nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
"github.com/metacubex/mihomo/common/batch"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/geodata"
|
||||
_ "github.com/metacubex/mihomo/component/geodata/standard"
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -169,41 +169,25 @@ func UpdateGeoSite() (err error) {
|
||||
func updateGeoDatabases() error {
|
||||
defer runtime.GC()
|
||||
|
||||
b, _ := batch.New[interface{}](context.Background())
|
||||
b := errgroup.Group{}
|
||||
|
||||
if geodata.GeoIpEnable() {
|
||||
if geodata.GeodataMode() {
|
||||
b.Go("UpdateGeoIp", func() (_ interface{}, err error) {
|
||||
err = UpdateGeoIp()
|
||||
return
|
||||
})
|
||||
b.Go(UpdateGeoIp)
|
||||
} else {
|
||||
b.Go("UpdateMMDB", func() (_ interface{}, err error) {
|
||||
err = UpdateMMDB()
|
||||
return
|
||||
})
|
||||
b.Go(UpdateMMDB)
|
||||
}
|
||||
}
|
||||
|
||||
if geodata.ASNEnable() {
|
||||
b.Go("UpdateASN", func() (_ interface{}, err error) {
|
||||
err = UpdateASN()
|
||||
return
|
||||
})
|
||||
b.Go(UpdateASN)
|
||||
}
|
||||
|
||||
if geodata.GeoSiteEnable() {
|
||||
b.Go("UpdateGeoSite", func() (_ interface{}, err error) {
|
||||
err = UpdateGeoSite()
|
||||
return
|
||||
})
|
||||
b.Go(UpdateGeoSite)
|
||||
}
|
||||
|
||||
if e := b.Wait(); e != nil {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
return nil
|
||||
return b.Wait()
|
||||
}
|
||||
|
||||
var ErrGetDatabaseUpdateSkip = errors.New("GEO database is updating, skip")
|
||||
|
@@ -375,9 +375,8 @@ func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func updateSniffer(snifferConfig *sniffer.Config) {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 1,
|
||||
"latest": {
|
||||
"mihomo": "v1.19.5",
|
||||
"mihomo_alpha": "alpha-a2f0ee7",
|
||||
"mihomo_alpha": "alpha-d55b047",
|
||||
"clash_rs": "v0.7.7",
|
||||
"clash_premium": "2023-09-05-gdcc8d87",
|
||||
"clash_rs_alpha": "0.7.7-alpha+sha.0abd79a"
|
||||
@@ -69,5 +69,5 @@
|
||||
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
||||
}
|
||||
},
|
||||
"updated_at": "2025-04-25T22:21:31.448Z"
|
||||
"updated_at": "2025-04-27T22:20:44.396Z"
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@
|
||||
- Windows 错误的全局快捷键 'Ctrl+Q' 注册
|
||||
- Vless URL 解码时网络类型错误
|
||||
- 切换自定义代理地址导致系统代理状态异常
|
||||
- Macos TUN 默认无效网卡名称
|
||||
|
||||
#### 新增了:
|
||||
- 允许代理主机地址设置为非 127.0.0.1 对 WSL 代理友好
|
||||
|
@@ -17,7 +17,7 @@ use tokio::time::Duration;
|
||||
// Windows only
|
||||
|
||||
const SERVICE_URL: &str = "http://127.0.0.1:33211";
|
||||
const REQUIRED_SERVICE_VERSION: &str = "1.0.5"; // 定义所需的服务版本号
|
||||
const REQUIRED_SERVICE_VERSION: &str = "1.0.6"; // 定义所需的服务版本号
|
||||
|
||||
// 限制重装时间和次数的常量
|
||||
const REINSTALL_COOLDOWN_SECS: u64 = 300; // 5分钟冷却期
|
||||
|
@@ -0,0 +1,85 @@
|
||||
From e184e8609f8c1cd9fef703f667245b6ebd89c2ed Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Tue, 3 Oct 2023 14:34:24 +0100
|
||||
Subject: [PATCH] net: sfp: re-implement ignoring the hardware TX_FAULT signal
|
||||
|
||||
Re-implement how we ignore the hardware TX_FAULT signal. Rather than
|
||||
having a separate boolean for this, use a bitmask of the hardware
|
||||
signals that we wish to ignore. This gives more flexibility in the
|
||||
future to ignore other signals such as RX_LOS.
|
||||
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Tested-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Link: https://lore.kernel.org/r/E1qnfXc-008UDY-91@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/sfp.c | 16 ++++++++--------
|
||||
1 file changed, 8 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/sfp.c
|
||||
+++ b/drivers/net/phy/sfp.c
|
||||
@@ -257,6 +257,7 @@ struct sfp {
|
||||
unsigned int state_hw_drive;
|
||||
unsigned int state_hw_mask;
|
||||
unsigned int state_soft_mask;
|
||||
+ unsigned int state_ignore_mask;
|
||||
unsigned int state;
|
||||
|
||||
struct delayed_work poll;
|
||||
@@ -280,7 +281,6 @@ struct sfp {
|
||||
unsigned int rs_state_mask;
|
||||
|
||||
bool have_a2;
|
||||
- bool tx_fault_ignore;
|
||||
|
||||
const struct sfp_quirk *quirk;
|
||||
|
||||
@@ -347,7 +347,7 @@ static void sfp_fixup_long_startup(struc
|
||||
|
||||
static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
|
||||
{
|
||||
- sfp->tx_fault_ignore = true;
|
||||
+ sfp->state_ignore_mask |= SFP_F_TX_FAULT;
|
||||
}
|
||||
|
||||
// For 10GBASE-T short-reach modules
|
||||
@@ -796,7 +796,8 @@ static void sfp_soft_start_poll(struct s
|
||||
|
||||
mutex_lock(&sfp->st_mutex);
|
||||
// Poll the soft state for hardware pins we want to ignore
|
||||
- sfp->state_soft_mask = ~sfp->state_hw_mask & mask;
|
||||
+ sfp->state_soft_mask = ~sfp->state_hw_mask & ~sfp->state_ignore_mask &
|
||||
+ mask;
|
||||
|
||||
if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
|
||||
!sfp->need_poll)
|
||||
@@ -2321,7 +2322,7 @@ static int sfp_sm_mod_probe(struct sfp *
|
||||
sfp->module_t_start_up = T_START_UP;
|
||||
sfp->module_t_wait = T_WAIT;
|
||||
|
||||
- sfp->tx_fault_ignore = false;
|
||||
+ sfp->state_ignore_mask = 0;
|
||||
|
||||
if (sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SFI ||
|
||||
sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SR ||
|
||||
@@ -2344,6 +2345,8 @@ static int sfp_sm_mod_probe(struct sfp *
|
||||
|
||||
if (sfp->quirk && sfp->quirk->fixup)
|
||||
sfp->quirk->fixup(sfp);
|
||||
+
|
||||
+ sfp->state_hw_mask &= ~sfp->state_ignore_mask;
|
||||
mutex_unlock(&sfp->st_mutex);
|
||||
|
||||
return 0;
|
||||
@@ -2844,10 +2847,7 @@ static void sfp_check_state(struct sfp *
|
||||
mutex_lock(&sfp->st_mutex);
|
||||
state = sfp_get_state(sfp);
|
||||
changed = state ^ sfp->state;
|
||||
- if (sfp->tx_fault_ignore)
|
||||
- changed &= SFP_F_PRESENT | SFP_F_LOS;
|
||||
- else
|
||||
- changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
|
||||
+ changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
|
||||
|
||||
for (i = 0; i < GPIO_MAX; i++)
|
||||
if (changed & BIT(i))
|
@@ -1,63 +0,0 @@
|
||||
From 7cc39a6bedbd85f3ff7e16845f310e4ce8d9833f Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 6 Sep 2022 00:31:19 +0100
|
||||
Subject: [PATCH] net: sfp: add quirk for ATS SFP-GE-T 1000Base-TX module
|
||||
To: netdev@vger.kernel.org,
|
||||
linux-kernel@vger.kernel.org,
|
||||
Russell King <linux@armlinux.org.uk>,
|
||||
Andrew Lunn <andrew@lunn.ch>,
|
||||
Heiner Kallweit <hkallweit1@gmail.com>
|
||||
Cc: David S. Miller <davem@davemloft.net>,
|
||||
Eric Dumazet <edumazet@google.com>,
|
||||
Jakub Kicinski <kuba@kernel.org>,
|
||||
Paolo Abeni <pabeni@redhat.com>,
|
||||
Josef Schlehofer <pepe.schlehofer@gmail.com>
|
||||
|
||||
This copper module comes with broken TX_FAULT indicator which must be
|
||||
ignored for it to work. Implement ignoring TX_FAULT state bit also
|
||||
during reset/insertion and mute the warning telling the user that the
|
||||
module indicates TX_FAULT.
|
||||
|
||||
Co-authored-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/phy/sfp.c | 14 +++++++++++---
|
||||
1 file changed, 11 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/sfp.c
|
||||
+++ b/drivers/net/phy/sfp.c
|
||||
@@ -485,6 +485,9 @@ static const struct sfp_quirk sfp_quirks
|
||||
SFP_QUIRK("HUAWEI", "MA5671A", sfp_quirk_2500basex,
|
||||
sfp_fixup_ignore_tx_fault),
|
||||
|
||||
+ // OEM SFP-GE-T is 1000Base-T module
|
||||
+ SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault),
|
||||
+
|
||||
// Lantech 8330-262D-E can operate at 2500base-X, but incorrectly report
|
||||
// 2500MBd NRZ in their EEPROM
|
||||
SFP_QUIRK_M("Lantech", "8330-262D-E", sfp_quirk_2500basex),
|
||||
@@ -2604,7 +2607,8 @@ static void sfp_sm_main(struct sfp *sfp,
|
||||
* or t_start_up, so assume there is a fault.
|
||||
*/
|
||||
sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT,
|
||||
- sfp->sm_fault_retries == N_FAULT_INIT);
|
||||
+ !sfp->tx_fault_ignore &&
|
||||
+ (sfp->sm_fault_retries == N_FAULT_INIT));
|
||||
} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
|
||||
init_done:
|
||||
/* Create mdiobus and start trying for PHY */
|
||||
@@ -2862,10 +2866,12 @@ static void sfp_check_state(struct sfp *
|
||||
mutex_lock(&sfp->st_mutex);
|
||||
state = sfp_get_state(sfp);
|
||||
changed = state ^ sfp->state;
|
||||
- if (sfp->tx_fault_ignore)
|
||||
+ if (sfp->tx_fault_ignore) {
|
||||
changed &= SFP_F_PRESENT | SFP_F_LOS;
|
||||
- else
|
||||
+ state &= ~SFP_F_TX_FAULT;
|
||||
+ } else {
|
||||
changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
|
||||
+ }
|
||||
|
||||
for (i = 0; i < GPIO_MAX; i++)
|
||||
if (changed & BIT(i))
|
@@ -7,13 +7,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
"github.com/metacubex/mihomo/common/batch"
|
||||
"github.com/metacubex/mihomo/common/singledo"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type HealthCheckOption struct {
|
||||
@@ -147,7 +147,8 @@ func (hc *HealthCheck) check() {
|
||||
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
||||
id := utils.NewUUIDV4().String()
|
||||
log.Debugln("Start New Health Checking {%s}", id)
|
||||
b, _ := batch.New[bool](hc.ctx, batch.WithConcurrencyNum[bool](10))
|
||||
b := new(errgroup.Group)
|
||||
b.SetLimit(10)
|
||||
|
||||
// execute default health check
|
||||
option := &extraOption{filters: nil, expectedStatus: hc.expectedStatus}
|
||||
@@ -159,13 +160,13 @@ func (hc *HealthCheck) check() {
|
||||
hc.execute(b, url, id, option)
|
||||
}
|
||||
}
|
||||
b.Wait()
|
||||
_ = b.Wait()
|
||||
log.Debugln("Finish A Health Checking {%s}", id)
|
||||
return struct{}{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *extraOption) {
|
||||
func (hc *HealthCheck) execute(b *errgroup.Group, url, uid string, option *extraOption) {
|
||||
url = strings.TrimSpace(url)
|
||||
if len(url) == 0 {
|
||||
log.Debugln("Health Check has been skipped due to testUrl is empty, {%s}", uid)
|
||||
@@ -195,13 +196,13 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex
|
||||
}
|
||||
|
||||
p := proxy
|
||||
b.Go(p.Name(), func() (bool, error) {
|
||||
b.Go(func() error {
|
||||
ctx, cancel := context.WithTimeout(hc.ctx, hc.timeout)
|
||||
defer cancel()
|
||||
log.Debugln("Health Checking, proxy: %s, url: %s, id: {%s}", p.Name(), url, uid)
|
||||
_, _ = p.URLTest(ctx, url, expectedStatus)
|
||||
log.Debugln("Health Checked, proxy: %s, url: %s, alive: %t, delay: %d ms uid: {%s}", p.Name(), url, p.AliveForTestUrl(url), p.LastDelayForTestUrl(url), uid)
|
||||
return false, nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
"github.com/metacubex/mihomo/common/batch"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/geodata"
|
||||
_ "github.com/metacubex/mihomo/component/geodata/standard"
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -169,41 +169,25 @@ func UpdateGeoSite() (err error) {
|
||||
func updateGeoDatabases() error {
|
||||
defer runtime.GC()
|
||||
|
||||
b, _ := batch.New[interface{}](context.Background())
|
||||
b := errgroup.Group{}
|
||||
|
||||
if geodata.GeoIpEnable() {
|
||||
if geodata.GeodataMode() {
|
||||
b.Go("UpdateGeoIp", func() (_ interface{}, err error) {
|
||||
err = UpdateGeoIp()
|
||||
return
|
||||
})
|
||||
b.Go(UpdateGeoIp)
|
||||
} else {
|
||||
b.Go("UpdateMMDB", func() (_ interface{}, err error) {
|
||||
err = UpdateMMDB()
|
||||
return
|
||||
})
|
||||
b.Go(UpdateMMDB)
|
||||
}
|
||||
}
|
||||
|
||||
if geodata.ASNEnable() {
|
||||
b.Go("UpdateASN", func() (_ interface{}, err error) {
|
||||
err = UpdateASN()
|
||||
return
|
||||
})
|
||||
b.Go(UpdateASN)
|
||||
}
|
||||
|
||||
if geodata.GeoSiteEnable() {
|
||||
b.Go("UpdateGeoSite", func() (_ interface{}, err error) {
|
||||
err = UpdateGeoSite()
|
||||
return
|
||||
})
|
||||
b.Go(UpdateGeoSite)
|
||||
}
|
||||
|
||||
if e := b.Wait(); e != nil {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
return nil
|
||||
return b.Wait()
|
||||
}
|
||||
|
||||
var ErrGetDatabaseUpdateSkip = errors.New("GEO database is updating, skip")
|
||||
|
@@ -375,9 +375,8 @@ func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func updateSniffer(snifferConfig *sniffer.Config) {
|
||||
|
@@ -9,6 +9,7 @@ local split = api.split
|
||||
|
||||
local local_version = api.get_app_version("sing-box")
|
||||
local version_ge_1_11_0 = api.compare_versions(local_version:match("[^v]+"), ">=", "1.11.0")
|
||||
local version_ge_1_12_0 = api.compare_versions(local_version:match("[^v]+"), ">=", "1.12.0")
|
||||
|
||||
local geosite_all_tag = {}
|
||||
local geoip_all_tag = {}
|
||||
@@ -126,6 +127,15 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
detour = node.detour,
|
||||
}
|
||||
|
||||
if version_ge_1_12_0 then
|
||||
--https://sing-box.sagernet.org/migration/#migrate-outbound-domain-strategy-option-to-domain-resolver
|
||||
result.domain_strategy = nil
|
||||
result.domain_resolver = {
|
||||
server = "direct",
|
||||
strategy = (node.domain_strategy and node.domain_strategy ~="") and node.domain_strategy or nil
|
||||
}
|
||||
end
|
||||
|
||||
local tls = nil
|
||||
if node.tls == "1" then
|
||||
local alpn = nil
|
||||
@@ -841,6 +851,7 @@ function gen_config(var)
|
||||
local direct_dns_tcp_server = var["-direct_dns_tcp_server"]
|
||||
local direct_dns_dot_server = var["-direct_dns_dot_server"]
|
||||
local direct_dns_query_strategy = var["-direct_dns_query_strategy"]
|
||||
local remote_dns_server = var["-remote_dns_server"]
|
||||
local remote_dns_port = var["-remote_dns_port"]
|
||||
local remote_dns_udp_server = var["-remote_dns_udp_server"]
|
||||
local remote_dns_tcp_server = var["-remote_dns_tcp_server"]
|
||||
@@ -1420,10 +1431,12 @@ function gen_config(var)
|
||||
fakeip = nil,
|
||||
}
|
||||
|
||||
if not version_ge_1_12_0 then --Migrate to new DNS server formats
|
||||
table.insert(dns.servers, {
|
||||
tag = "block",
|
||||
address = "rcode://success",
|
||||
})
|
||||
end
|
||||
|
||||
if dns_socks_address and dns_socks_port then
|
||||
default_outTag = "dns_socks_out"
|
||||
@@ -1444,7 +1457,11 @@ function gen_config(var)
|
||||
remote_strategy = "ipv6_only"
|
||||
end
|
||||
|
||||
local remote_server = {
|
||||
local remote_server = {}
|
||||
local fakedns_tag = "remote_fakeip"
|
||||
|
||||
if not version_ge_1_12_0 then --Migrate to new DNS server formats
|
||||
remote_server = {
|
||||
tag = "remote",
|
||||
address_strategy = "prefer_ipv4",
|
||||
strategy = remote_strategy,
|
||||
@@ -1470,7 +1487,6 @@ function gen_config(var)
|
||||
table.insert(dns.servers, remote_server)
|
||||
end
|
||||
|
||||
local fakedns_tag = "remote_fakeip"
|
||||
if remote_dns_fake then
|
||||
dns.fakeip = {
|
||||
enabled = true,
|
||||
@@ -1493,8 +1509,66 @@ function gen_config(var)
|
||||
path = api.CACHE_PATH .. "/singbox_" .. flag .. ".db"
|
||||
}
|
||||
end
|
||||
else -- Migrate to 1.12 DNS
|
||||
remote_server = {
|
||||
tag = "remote",
|
||||
domain_strategy = remote_strategy,
|
||||
domain_resolver = "direct",
|
||||
detour = default_outTag,
|
||||
}
|
||||
|
||||
if remote_dns_udp_server then
|
||||
local server_port = tonumber(remote_dns_port) or 53
|
||||
remote_server.type = "udp"
|
||||
remote_server.server = remote_dns_udp_server
|
||||
remote_server.server_port = server_port
|
||||
end
|
||||
|
||||
if remote_dns_tcp_server then
|
||||
local server_port = tonumber(remote_dns_port) or 53
|
||||
remote_server.type = "tcp"
|
||||
remote_server.server = remote_dns_server
|
||||
remote_server.server_port = server_port
|
||||
end
|
||||
|
||||
if remote_dns_doh_url and remote_dns_doh_host then
|
||||
local server_port = tonumber(remote_dns_port) or 443
|
||||
remote_server.type = "https"
|
||||
remote_server.server = remote_dns_doh_host
|
||||
remote_server.server_port = server_port
|
||||
end
|
||||
|
||||
if remote_server.server then
|
||||
table.insert(dns.servers, remote_server)
|
||||
end
|
||||
|
||||
if remote_dns_fake then
|
||||
table.insert(dns.servers, {
|
||||
tag = fakedns_tag,
|
||||
type = "fakeip",
|
||||
inet4_range = "198.18.0.0/15",
|
||||
inet6_range = "fc00::/18",
|
||||
})
|
||||
|
||||
if not experimental then
|
||||
experimental = {}
|
||||
end
|
||||
experimental.cache_file = {
|
||||
enabled = true,
|
||||
store_fakeip = true,
|
||||
path = api.CACHE_PATH .. "/singbox_" .. flag .. ".db"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local direct_strategy = "prefer_ipv6"
|
||||
if direct_dns_udp_server or direct_dns_tcp_server or direct_dns_dot_server then
|
||||
if direct_dns_query_strategy == "UseIPv4" then
|
||||
direct_strategy = "ipv4_only"
|
||||
elseif direct_dns_query_strategy == "UseIPv6" then
|
||||
direct_strategy = "ipv6_only"
|
||||
end
|
||||
|
||||
local domain = {}
|
||||
local nodes_domain_text = sys.exec('uci show passwall | grep ".address=" | cut -d "\'" -f 2 | grep "[a-zA-Z]$" | sort -u')
|
||||
string.gsub(nodes_domain_text, '[^' .. "\r\n" .. ']+', function(w)
|
||||
@@ -1503,17 +1577,12 @@ function gen_config(var)
|
||||
if #domain > 0 then
|
||||
table.insert(dns_domain_rules, 1, {
|
||||
outboundTag = "direct",
|
||||
domain = domain
|
||||
domain = domain,
|
||||
strategy = version_ge_1_12_0 and direct_strategy or nil
|
||||
})
|
||||
end
|
||||
|
||||
local direct_strategy = "prefer_ipv6"
|
||||
if direct_dns_query_strategy == "UseIPv4" then
|
||||
direct_strategy = "ipv4_only"
|
||||
elseif direct_dns_query_strategy == "UseIPv6" then
|
||||
direct_strategy = "ipv6_only"
|
||||
end
|
||||
|
||||
if not version_ge_1_12_0 then --Migrate to new DNS server formats
|
||||
local direct_dns_server, port
|
||||
if direct_dns_udp_server then
|
||||
port = tonumber(direct_dns_port) or 53
|
||||
@@ -1537,6 +1606,32 @@ function gen_config(var)
|
||||
strategy = direct_strategy,
|
||||
detour = "direct",
|
||||
})
|
||||
else -- Migrate to 1.12 DNS
|
||||
local direct_dns_server, port, type
|
||||
if direct_dns_udp_server then
|
||||
port = tonumber(direct_dns_port) or 53
|
||||
direct_dns_server = direct_dns_udp_server
|
||||
type = "udp"
|
||||
elseif direct_dns_tcp_server then
|
||||
port = tonumber(direct_dns_port) or 53
|
||||
direct_dns_server = direct_dns_tcp_server
|
||||
type = "tcp"
|
||||
elseif direct_dns_dot_server then
|
||||
port = tonumber(direct_dns_port) or 853
|
||||
direct_dns_server = direct_dns_dot_server
|
||||
type = "tls"
|
||||
end
|
||||
|
||||
table.insert(dns.servers, {
|
||||
tag = "direct",
|
||||
type = type,
|
||||
server = direct_dns_server,
|
||||
server_port = port,
|
||||
domain_strategy = direct_strategy,
|
||||
detour = "direct",
|
||||
})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local default_dns_flag = "remote"
|
||||
@@ -1561,6 +1656,9 @@ function gen_config(var)
|
||||
end
|
||||
end
|
||||
dns.final = default_dns_flag
|
||||
if version_ge_1_12_0 then -- Migrate to 1.12 DNS
|
||||
dns.strategy = (default_dns_flag == "direct") and direct_strategy or remote_strategy
|
||||
end
|
||||
|
||||
--按分流顺序DNS
|
||||
if dns_domain_rules and #dns_domain_rules > 0 then
|
||||
@@ -1574,15 +1672,24 @@ function gen_config(var)
|
||||
domain_regex = (value.domain_regex and #value.domain_regex > 0) and value.domain_regex or nil,
|
||||
rule_set = (value.rule_set and #value.rule_set > 0) and value.rule_set or nil, --适配srs
|
||||
disable_cache = false,
|
||||
strategy = (version_ge_1_12_0 and value.outboundTag == "direct") and direct_strategy or nil --Migrate to 1.12 DNS
|
||||
}
|
||||
if version_ge_1_12_0 and value.outboundTag == "block" then --Migrate to 1.12 DNS
|
||||
dns_rule.action = "predefined"
|
||||
dns_rule.rcode = "NOERROR"
|
||||
dns_rule.server = nil
|
||||
dns_rule.disable_cache = nil
|
||||
end
|
||||
if value.outboundTag ~= "block" and value.outboundTag ~= "direct" then
|
||||
dns_rule.server = "remote"
|
||||
if value.outboundTag ~= COMMON.default_outbound_tag and remote_server.address then
|
||||
local remote_dns_server = api.clone(remote_server)
|
||||
remote_dns_server.tag = value.outboundTag
|
||||
remote_dns_server.detour = value.outboundTag
|
||||
table.insert(dns.servers, remote_dns_server)
|
||||
dns_rule.server = remote_dns_server.tag
|
||||
dns_rule.strategy = version_ge_1_12_0 and remote_strategy or nil --Migrate to 1.12 DNS
|
||||
dns_rule.client_subnet = (version_ge_1_12_0 and remote_dns_client_ip and remote_dns_client_ip ~= "") and remote_dns_client_ip or nil --Migrate to 1.12 DNS
|
||||
if value.outboundTag ~= COMMON.default_outbound_tag and (remote_server.address or remote_server.server) then
|
||||
local remote_shunt_server = api.clone(remote_server)
|
||||
remote_shunt_server.tag = value.outboundTag
|
||||
remote_shunt_server.detour = value.outboundTag
|
||||
table.insert(dns.servers, remote_shunt_server)
|
||||
dns_rule.server = remote_shunt_server.tag
|
||||
end
|
||||
if remote_dns_fake then
|
||||
local fakedns_dns_rule = api.clone(dns_rule)
|
||||
@@ -1638,12 +1745,44 @@ function gen_config(var)
|
||||
--实验性
|
||||
experimental = experimental,
|
||||
}
|
||||
table.insert(outbounds, {
|
||||
|
||||
local direct_outbound = {
|
||||
type = "direct",
|
||||
tag = "direct",
|
||||
routing_mark = 255,
|
||||
domain_strategy = "prefer_ipv6",
|
||||
})
|
||||
}
|
||||
if not version_ge_1_12_0 then --Migrate to 1.12 DNS
|
||||
direct_outbound.domain_strategy = "prefer_ipv6"
|
||||
else
|
||||
local domain_resolver = {
|
||||
server = "direct",
|
||||
strategy = "prefer_ipv6"
|
||||
}
|
||||
direct_outbound.domain_resolver = domain_resolver
|
||||
|
||||
-- 当没有 direct dns 服务器时添加 local
|
||||
local hasDirect = false
|
||||
if config.dns and config.dns.servers then
|
||||
for _, server in ipairs(config.dns.servers) do
|
||||
if server.tag == "direct" then
|
||||
hasDirect = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not hasDirect then
|
||||
config.dns = {
|
||||
servers = {
|
||||
{
|
||||
type = "local",
|
||||
tag = "direct"
|
||||
}
|
||||
},
|
||||
}
|
||||
end
|
||||
end
|
||||
table.insert(outbounds,direct_outbound)
|
||||
|
||||
table.insert(outbounds, {
|
||||
type = "block",
|
||||
tag = "block"
|
||||
|
4
shadowsocks-rust/Cargo.lock
generated
4
shadowsocks-rust/Cargo.lock
generated
@@ -4349,9 +4349,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.8"
|
||||
version = "0.26.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9"
|
||||
checksum = "29aad86cec885cafd03e8305fd727c418e970a521322c91688414d5b8efba16b"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
@@ -99,8 +99,8 @@ allow = [
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"Unicode-3.0",
|
||||
"Unicode-DFS-2016",
|
||||
"MPL-2.0",
|
||||
"CDLA-Permissive-2.0",
|
||||
"CC0-1.0",
|
||||
"0BSD",
|
||||
]
|
||||
@@ -116,7 +116,6 @@ exceptions = [
|
||||
# list
|
||||
#{ allow = ["Zlib"], crate = "adler32" },
|
||||
{ allow = ["WTFPL"], crate = "tun" },
|
||||
{ allow = ["LicenseRef-ring"], crate = "ring" },
|
||||
]
|
||||
|
||||
# Some crates don't have (easily) machine readable licensing information,
|
||||
|
@@ -1,158 +0,0 @@
|
||||
package humanize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// IEC Sizes.
|
||||
// kibis of bits
|
||||
const (
|
||||
Byte = 1 << (iota * 10)
|
||||
KiByte
|
||||
MiByte
|
||||
GiByte
|
||||
TiByte
|
||||
PiByte
|
||||
EiByte
|
||||
)
|
||||
|
||||
// SI Sizes.
|
||||
const (
|
||||
IByte = 1
|
||||
KByte = IByte * 1000
|
||||
MByte = KByte * 1000
|
||||
GByte = MByte * 1000
|
||||
TByte = GByte * 1000
|
||||
PByte = TByte * 1000
|
||||
EByte = PByte * 1000
|
||||
)
|
||||
|
||||
var defaultSizeTable = map[string]uint64{
|
||||
"b": Byte,
|
||||
"kib": KiByte,
|
||||
"kb": KByte,
|
||||
"mib": MiByte,
|
||||
"mb": MByte,
|
||||
"gib": GiByte,
|
||||
"gb": GByte,
|
||||
"tib": TiByte,
|
||||
"tb": TByte,
|
||||
"pib": PiByte,
|
||||
"pb": PByte,
|
||||
"eib": EiByte,
|
||||
"eb": EByte,
|
||||
// Without suffix
|
||||
"": Byte,
|
||||
"ki": KiByte,
|
||||
"k": KByte,
|
||||
"mi": MiByte,
|
||||
"m": MByte,
|
||||
"gi": GiByte,
|
||||
"g": GByte,
|
||||
"ti": TiByte,
|
||||
"t": TByte,
|
||||
"pi": PiByte,
|
||||
"p": PByte,
|
||||
"ei": EiByte,
|
||||
"e": EByte,
|
||||
}
|
||||
|
||||
var memorysSizeTable = map[string]uint64{
|
||||
"b": Byte,
|
||||
"kb": KiByte,
|
||||
"mb": MiByte,
|
||||
"gb": GiByte,
|
||||
"tb": TiByte,
|
||||
"pb": PiByte,
|
||||
"eb": EiByte,
|
||||
"": Byte,
|
||||
"k": KiByte,
|
||||
"m": MiByte,
|
||||
"g": GiByte,
|
||||
"t": TiByte,
|
||||
"p": PiByte,
|
||||
"e": EiByte,
|
||||
}
|
||||
|
||||
var (
|
||||
defaultSizes = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
|
||||
iSizes = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
|
||||
)
|
||||
|
||||
func Bytes(s uint64) string {
|
||||
return humanateBytes(s, 1000, defaultSizes)
|
||||
}
|
||||
|
||||
func MemoryBytes(s uint64) string {
|
||||
return humanateBytes(s, 1024, defaultSizes)
|
||||
}
|
||||
|
||||
func IBytes(s uint64) string {
|
||||
return humanateBytes(s, 1024, iSizes)
|
||||
}
|
||||
|
||||
func logn(n, b float64) float64 {
|
||||
return math.Log(n) / math.Log(b)
|
||||
}
|
||||
|
||||
func humanateBytes(s uint64, base float64, sizes []string) string {
|
||||
if s < 10 {
|
||||
return fmt.Sprintf("%d B", s)
|
||||
}
|
||||
e := math.Floor(logn(float64(s), base))
|
||||
suffix := sizes[int(e)]
|
||||
val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
|
||||
f := "%.0f %s"
|
||||
if val < 10 {
|
||||
f = "%.1f %s"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(f, val, suffix)
|
||||
}
|
||||
|
||||
func ParseBytes(s string) (uint64, error) {
|
||||
return parseBytes0(s, defaultSizeTable)
|
||||
}
|
||||
|
||||
func ParseMemoryBytes(s string) (uint64, error) {
|
||||
return parseBytes0(s, memorysSizeTable)
|
||||
}
|
||||
|
||||
func parseBytes0(s string, sizeTable map[string]uint64) (uint64, error) {
|
||||
lastDigit := 0
|
||||
hasComma := false
|
||||
for _, r := range s {
|
||||
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
||||
break
|
||||
}
|
||||
if r == ',' {
|
||||
hasComma = true
|
||||
}
|
||||
lastDigit++
|
||||
}
|
||||
|
||||
num := s[:lastDigit]
|
||||
if hasComma {
|
||||
num = strings.Replace(num, ",", "", -1)
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(num, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
|
||||
if m, ok := sizeTable[extra]; ok {
|
||||
f *= float64(m)
|
||||
if f >= math.MaxUint64 {
|
||||
return 0, fmt.Errorf("too large: %v", s)
|
||||
}
|
||||
return uint64(f), nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unhandled size name: %v", extra)
|
||||
}
|
@@ -24,9 +24,9 @@ func applyDebugOptions(options option.DebugOptions) {
|
||||
if options.TraceBack != "" {
|
||||
debug.SetTraceback(options.TraceBack)
|
||||
}
|
||||
if options.MemoryLimit != 0 {
|
||||
debug.SetMemoryLimit(int64(float64(options.MemoryLimit) / 1.5))
|
||||
conntrack.MemoryLimit = uint64(options.MemoryLimit)
|
||||
if options.MemoryLimit.Value() != 0 {
|
||||
debug.SetMemoryLimit(int64(float64(options.MemoryLimit.Value()) / 1.5))
|
||||
conntrack.MemoryLimit = options.MemoryLimit.Value()
|
||||
}
|
||||
if options.OOMKiller != nil {
|
||||
conntrack.KillerEnabled = *options.OOMKiller
|
||||
|
@@ -7,9 +7,9 @@ import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/byteformats"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
@@ -38,9 +38,9 @@ func applyDebugListenOption(options option.DebugOptions) {
|
||||
runtime.ReadMemStats(&memStats)
|
||||
|
||||
var memObject badjson.JSONObject
|
||||
memObject.Put("heap", humanize.MemoryBytes(memStats.HeapInuse))
|
||||
memObject.Put("stack", humanize.MemoryBytes(memStats.StackInuse))
|
||||
memObject.Put("idle", humanize.MemoryBytes(memStats.HeapIdle-memStats.HeapReleased))
|
||||
memObject.Put("heap", byteformats.FormatMemoryBytes(memStats.HeapInuse))
|
||||
memObject.Put("stack", byteformats.FormatMemoryBytes(memStats.StackInuse))
|
||||
memObject.Put("idle", byteformats.FormatMemoryBytes(memStats.HeapIdle-memStats.HeapReleased))
|
||||
memObject.Put("goroutines", runtime.NumGoroutine())
|
||||
memObject.Put("rss", rusageMaxRSS())
|
||||
|
||||
|
@@ -93,11 +93,12 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if !serverAddr.Addr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
} else if serverAddr.Port == 0 {
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 443
|
||||
}
|
||||
if !serverAddr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
}
|
||||
return NewHTTPSRaw(
|
||||
dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeHTTPS, tag, options.RemoteDNSServerOptions),
|
||||
logger,
|
||||
|
@@ -89,11 +89,12 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if !serverAddr.Addr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
} else if serverAddr.Port == 0 {
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 443
|
||||
}
|
||||
if !serverAddr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
}
|
||||
return &HTTP3Transport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeHTTP3, tag, options.RemoteDNSServerOptions),
|
||||
logger: logger,
|
||||
|
@@ -56,11 +56,12 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
tlsConfig.SetNextProtos([]string{"doq"})
|
||||
}
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if !serverAddr.Addr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
} else if serverAddr.Port == 0 {
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 853
|
||||
}
|
||||
if !serverAddr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
}
|
||||
return &Transport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeQUIC, tag, options.RemoteDNSServerOptions),
|
||||
ctx: ctx,
|
||||
|
@@ -38,11 +38,12 @@ func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if !serverAddr.Addr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
} else if serverAddr.Port == 0 {
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 53
|
||||
}
|
||||
if !serverAddr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
}
|
||||
return &TCPTransport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeTCP, tag, options),
|
||||
dialer: transportDialer,
|
||||
|
@@ -54,11 +54,12 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if !serverAddr.Addr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
} else if serverAddr.Port == 0 {
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 853
|
||||
}
|
||||
if !serverAddr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
}
|
||||
return &TLSTransport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeTLS, tag, options.RemoteDNSServerOptions),
|
||||
logger: logger,
|
||||
|
@@ -45,11 +45,12 @@ func NewUDP(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
return nil, err
|
||||
}
|
||||
serverAddr := options.DNSServerAddressOptions.Build()
|
||||
if !serverAddr.Addr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
} else if serverAddr.Port == 0 {
|
||||
if serverAddr.Port == 0 {
|
||||
serverAddr.Port = 53
|
||||
}
|
||||
if !serverAddr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
}
|
||||
return NewUDPRaw(logger, dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeUDP, tag, options), transportDialer, serverAddr), nil
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.12.0-beta.6
|
||||
#### 1.12.0-beta.7
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
|
@@ -42,16 +42,18 @@ AnyTLS padding scheme line array.
|
||||
|
||||
Default padding scheme:
|
||||
|
||||
```
|
||||
stop=8
|
||||
0=30-30
|
||||
1=100-400
|
||||
2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000
|
||||
3=9-9,500-1000
|
||||
4=500-1000
|
||||
5=500-1000
|
||||
6=500-1000
|
||||
7=500-1000
|
||||
```json
|
||||
[
|
||||
"stop=8",
|
||||
"0=30-30",
|
||||
"1=100-400",
|
||||
"2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000",
|
||||
"3=9-9,500-1000",
|
||||
"4=500-1000",
|
||||
"5=500-1000",
|
||||
"6=500-1000",
|
||||
"7=500-1000"
|
||||
]
|
||||
```
|
||||
|
||||
#### tls
|
||||
|
@@ -42,16 +42,18 @@ AnyTLS 填充方案行数组。
|
||||
|
||||
默认填充方案:
|
||||
|
||||
```
|
||||
stop=8
|
||||
0=30-30
|
||||
1=100-400
|
||||
2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000
|
||||
3=9-9,500-1000
|
||||
4=500-1000
|
||||
5=500-1000
|
||||
6=500-1000
|
||||
7=500-1000
|
||||
```json
|
||||
[
|
||||
"stop=8",
|
||||
"0=30-30",
|
||||
"1=100-400",
|
||||
"2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000",
|
||||
"3=9-9,500-1000",
|
||||
"4=500-1000",
|
||||
"5=500-1000",
|
||||
"6=500-1000",
|
||||
"7=500-1000"
|
||||
]
|
||||
```
|
||||
|
||||
#### tls
|
||||
|
@@ -7,10 +7,10 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/locale"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common/byteformats"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -75,11 +75,11 @@ func Version() string {
|
||||
}
|
||||
|
||||
func FormatBytes(length int64) string {
|
||||
return humanize.Bytes(uint64(length))
|
||||
return byteformats.FormatBytes(uint64(length))
|
||||
}
|
||||
|
||||
func FormatMemoryBytes(length int64) string {
|
||||
return humanize.MemoryBytes(uint64(length))
|
||||
return byteformats.FormatMemoryBytes(uint64(length))
|
||||
}
|
||||
|
||||
func FormatDuration(duration int64) string {
|
||||
|
@@ -26,13 +26,13 @@ require (
|
||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
|
||||
github.com/sagernet/quic-go v0.49.0-beta.1
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a
|
||||
github.com/sagernet/sing v0.6.8-0.20250428152347-91813c8e3960
|
||||
github.com/sagernet/sing-mux v0.3.1
|
||||
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056
|
||||
github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a
|
||||
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210
|
||||
github.com/sagernet/sing-vmess v0.2.1
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/tailscale v1.80.3-mod.4
|
||||
|
@@ -178,8 +178,8 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a h1:oE67hmp5rzLlE6clE7FpK4Hg6yLXsa1Zu3A01vcazb0=
|
||||
github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing v0.6.8-0.20250428152347-91813c8e3960 h1:bBSVJ0d5YtbhhwugBXH8GrVaKi1ZIHYdL03LN006Fng=
|
||||
github.com/sagernet/sing v0.6.8-0.20250428152347-91813c8e3960/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
|
||||
github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
|
||||
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 h1:iwpCX6H3nZEOGUGwx0q5azcgYOA9f6v9YssihXoRKHk=
|
||||
@@ -190,8 +190,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 h1:GFNJQAHhSXqAfxAw1wDG/QWbdpGH5Na3k8qUynqWnEA=
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056/go.mod h1:HyacBPIFiKihJQR8LQp56FM4hBtd/7MZXnRxxQIOPsc=
|
||||
github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a h1:2aLxZFD2HPCLrnFGpH+KBuPqMOk0cuaDE2dgEvANuMk=
|
||||
github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210 h1:6H4BZaTqKI3YcDMyTV3E576LuJM4S4wY99xoq2T1ECw=
|
||||
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||
github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug=
|
||||
github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
|
@@ -1,9 +1,6 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
)
|
||||
import "github.com/sagernet/sing/common/byteformats"
|
||||
|
||||
type DebugOptions struct {
|
||||
Listen string `json:"listen,omitempty"`
|
||||
@@ -12,32 +9,6 @@ type DebugOptions struct {
|
||||
MaxThreads *int `json:"max_threads,omitempty"`
|
||||
PanicOnFault *bool `json:"panic_on_fault,omitempty"`
|
||||
TraceBack string `json:"trace_back,omitempty"`
|
||||
MemoryLimit MemoryBytes `json:"memory_limit,omitempty"`
|
||||
MemoryLimit *byteformats.MemoryBytes `json:"memory_limit,omitempty"`
|
||||
OOMKiller *bool `json:"oom_killer,omitempty"`
|
||||
}
|
||||
|
||||
type MemoryBytes uint64
|
||||
|
||||
func (l MemoryBytes) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(humanize.MemoryBytes(uint64(l)))
|
||||
}
|
||||
|
||||
func (l *MemoryBytes) UnmarshalJSON(bytes []byte) error {
|
||||
var valueInteger int64
|
||||
err := json.Unmarshal(bytes, &valueInteger)
|
||||
if err == nil {
|
||||
*l = MemoryBytes(valueInteger)
|
||||
return nil
|
||||
}
|
||||
var valueString string
|
||||
err = json.Unmarshal(bytes, &valueString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parsedValue, err := humanize.ParseMemoryBytes(valueString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*l = MemoryBytes(parsedValue)
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,12 +1,15 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
import (
|
||||
"github.com/sagernet/sing/common/byteformats"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type HysteriaInboundOptions struct {
|
||||
ListenOptions
|
||||
Up string `json:"up,omitempty"`
|
||||
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
|
||||
UpMbps int `json:"up_mbps,omitempty"`
|
||||
Down string `json:"down,omitempty"`
|
||||
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`
|
||||
DownMbps int `json:"down_mbps,omitempty"`
|
||||
Obfs string `json:"obfs,omitempty"`
|
||||
Users []HysteriaUser `json:"users,omitempty"`
|
||||
@@ -28,9 +31,9 @@ type HysteriaOutboundOptions struct {
|
||||
ServerOptions
|
||||
ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"`
|
||||
HopInterval badoption.Duration `json:"hop_interval,omitempty"`
|
||||
Up string `json:"up,omitempty"`
|
||||
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
|
||||
UpMbps int `json:"up_mbps,omitempty"`
|
||||
Down string `json:"down,omitempty"`
|
||||
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`
|
||||
DownMbps int `json:"down_mbps,omitempty"`
|
||||
Obfs string `json:"obfs,omitempty"`
|
||||
Auth []byte `json:"auth,omitempty"`
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/sagernet/sing-quic/hysteria"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
@@ -56,19 +54,13 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
var sendBps, receiveBps uint64
|
||||
if len(options.Up) > 0 {
|
||||
sendBps, err = humanize.ParseBytes(options.Up)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "invalid up speed format: ", options.Up)
|
||||
}
|
||||
if options.Up.Value() > 0 {
|
||||
sendBps = options.Up.Value()
|
||||
} else {
|
||||
sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps
|
||||
}
|
||||
if len(options.Down) > 0 {
|
||||
receiveBps, err = humanize.ParseBytes(options.Down)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "invalid down speed format: ", options.Down)
|
||||
}
|
||||
if options.Down.Value() > 0 {
|
||||
receiveBps = options.Down.Value()
|
||||
} else {
|
||||
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -60,19 +59,13 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
password = string(options.Auth)
|
||||
}
|
||||
var sendBps, receiveBps uint64
|
||||
if len(options.Up) > 0 {
|
||||
sendBps, err = humanize.ParseBytes(options.Up)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "invalid up speed format: ", options.Up)
|
||||
}
|
||||
if options.Up.Value() > 0 {
|
||||
sendBps = options.Up.Value()
|
||||
} else {
|
||||
sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps
|
||||
}
|
||||
if len(options.Down) > 0 {
|
||||
receiveBps, err = humanize.ParseBytes(options.Down)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "invalid down speed format: ", options.Down)
|
||||
}
|
||||
if options.Down.Value() > 0 {
|
||||
receiveBps = options.Down.Value()
|
||||
} else {
|
||||
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ local split = api.split
|
||||
|
||||
local local_version = api.get_app_version("sing-box")
|
||||
local version_ge_1_11_0 = api.compare_versions(local_version:match("[^v]+"), ">=", "1.11.0")
|
||||
local version_ge_1_12_0 = api.compare_versions(local_version:match("[^v]+"), ">=", "1.12.0")
|
||||
|
||||
local geosite_all_tag = {}
|
||||
local geoip_all_tag = {}
|
||||
@@ -126,6 +127,15 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
detour = node.detour,
|
||||
}
|
||||
|
||||
if version_ge_1_12_0 then
|
||||
--https://sing-box.sagernet.org/migration/#migrate-outbound-domain-strategy-option-to-domain-resolver
|
||||
result.domain_strategy = nil
|
||||
result.domain_resolver = {
|
||||
server = "direct",
|
||||
strategy = (node.domain_strategy and node.domain_strategy ~="") and node.domain_strategy or nil
|
||||
}
|
||||
end
|
||||
|
||||
local tls = nil
|
||||
if node.tls == "1" then
|
||||
local alpn = nil
|
||||
@@ -841,6 +851,7 @@ function gen_config(var)
|
||||
local direct_dns_tcp_server = var["-direct_dns_tcp_server"]
|
||||
local direct_dns_dot_server = var["-direct_dns_dot_server"]
|
||||
local direct_dns_query_strategy = var["-direct_dns_query_strategy"]
|
||||
local remote_dns_server = var["-remote_dns_server"]
|
||||
local remote_dns_port = var["-remote_dns_port"]
|
||||
local remote_dns_udp_server = var["-remote_dns_udp_server"]
|
||||
local remote_dns_tcp_server = var["-remote_dns_tcp_server"]
|
||||
@@ -1420,10 +1431,12 @@ function gen_config(var)
|
||||
fakeip = nil,
|
||||
}
|
||||
|
||||
if not version_ge_1_12_0 then --Migrate to new DNS server formats
|
||||
table.insert(dns.servers, {
|
||||
tag = "block",
|
||||
address = "rcode://success",
|
||||
})
|
||||
end
|
||||
|
||||
if dns_socks_address and dns_socks_port then
|
||||
default_outTag = "dns_socks_out"
|
||||
@@ -1444,7 +1457,11 @@ function gen_config(var)
|
||||
remote_strategy = "ipv6_only"
|
||||
end
|
||||
|
||||
local remote_server = {
|
||||
local remote_server = {}
|
||||
local fakedns_tag = "remote_fakeip"
|
||||
|
||||
if not version_ge_1_12_0 then --Migrate to new DNS server formats
|
||||
remote_server = {
|
||||
tag = "remote",
|
||||
address_strategy = "prefer_ipv4",
|
||||
strategy = remote_strategy,
|
||||
@@ -1470,7 +1487,6 @@ function gen_config(var)
|
||||
table.insert(dns.servers, remote_server)
|
||||
end
|
||||
|
||||
local fakedns_tag = "remote_fakeip"
|
||||
if remote_dns_fake then
|
||||
dns.fakeip = {
|
||||
enabled = true,
|
||||
@@ -1493,8 +1509,66 @@ function gen_config(var)
|
||||
path = api.CACHE_PATH .. "/singbox_" .. flag .. ".db"
|
||||
}
|
||||
end
|
||||
else -- Migrate to 1.12 DNS
|
||||
remote_server = {
|
||||
tag = "remote",
|
||||
domain_strategy = remote_strategy,
|
||||
domain_resolver = "direct",
|
||||
detour = default_outTag,
|
||||
}
|
||||
|
||||
if remote_dns_udp_server then
|
||||
local server_port = tonumber(remote_dns_port) or 53
|
||||
remote_server.type = "udp"
|
||||
remote_server.server = remote_dns_udp_server
|
||||
remote_server.server_port = server_port
|
||||
end
|
||||
|
||||
if remote_dns_tcp_server then
|
||||
local server_port = tonumber(remote_dns_port) or 53
|
||||
remote_server.type = "tcp"
|
||||
remote_server.server = remote_dns_server
|
||||
remote_server.server_port = server_port
|
||||
end
|
||||
|
||||
if remote_dns_doh_url and remote_dns_doh_host then
|
||||
local server_port = tonumber(remote_dns_port) or 443
|
||||
remote_server.type = "https"
|
||||
remote_server.server = remote_dns_doh_host
|
||||
remote_server.server_port = server_port
|
||||
end
|
||||
|
||||
if remote_server.server then
|
||||
table.insert(dns.servers, remote_server)
|
||||
end
|
||||
|
||||
if remote_dns_fake then
|
||||
table.insert(dns.servers, {
|
||||
tag = fakedns_tag,
|
||||
type = "fakeip",
|
||||
inet4_range = "198.18.0.0/15",
|
||||
inet6_range = "fc00::/18",
|
||||
})
|
||||
|
||||
if not experimental then
|
||||
experimental = {}
|
||||
end
|
||||
experimental.cache_file = {
|
||||
enabled = true,
|
||||
store_fakeip = true,
|
||||
path = api.CACHE_PATH .. "/singbox_" .. flag .. ".db"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local direct_strategy = "prefer_ipv6"
|
||||
if direct_dns_udp_server or direct_dns_tcp_server or direct_dns_dot_server then
|
||||
if direct_dns_query_strategy == "UseIPv4" then
|
||||
direct_strategy = "ipv4_only"
|
||||
elseif direct_dns_query_strategy == "UseIPv6" then
|
||||
direct_strategy = "ipv6_only"
|
||||
end
|
||||
|
||||
local domain = {}
|
||||
local nodes_domain_text = sys.exec('uci show passwall | grep ".address=" | cut -d "\'" -f 2 | grep "[a-zA-Z]$" | sort -u')
|
||||
string.gsub(nodes_domain_text, '[^' .. "\r\n" .. ']+', function(w)
|
||||
@@ -1503,17 +1577,12 @@ function gen_config(var)
|
||||
if #domain > 0 then
|
||||
table.insert(dns_domain_rules, 1, {
|
||||
outboundTag = "direct",
|
||||
domain = domain
|
||||
domain = domain,
|
||||
strategy = version_ge_1_12_0 and direct_strategy or nil
|
||||
})
|
||||
end
|
||||
|
||||
local direct_strategy = "prefer_ipv6"
|
||||
if direct_dns_query_strategy == "UseIPv4" then
|
||||
direct_strategy = "ipv4_only"
|
||||
elseif direct_dns_query_strategy == "UseIPv6" then
|
||||
direct_strategy = "ipv6_only"
|
||||
end
|
||||
|
||||
if not version_ge_1_12_0 then --Migrate to new DNS server formats
|
||||
local direct_dns_server, port
|
||||
if direct_dns_udp_server then
|
||||
port = tonumber(direct_dns_port) or 53
|
||||
@@ -1537,6 +1606,32 @@ function gen_config(var)
|
||||
strategy = direct_strategy,
|
||||
detour = "direct",
|
||||
})
|
||||
else -- Migrate to 1.12 DNS
|
||||
local direct_dns_server, port, type
|
||||
if direct_dns_udp_server then
|
||||
port = tonumber(direct_dns_port) or 53
|
||||
direct_dns_server = direct_dns_udp_server
|
||||
type = "udp"
|
||||
elseif direct_dns_tcp_server then
|
||||
port = tonumber(direct_dns_port) or 53
|
||||
direct_dns_server = direct_dns_tcp_server
|
||||
type = "tcp"
|
||||
elseif direct_dns_dot_server then
|
||||
port = tonumber(direct_dns_port) or 853
|
||||
direct_dns_server = direct_dns_dot_server
|
||||
type = "tls"
|
||||
end
|
||||
|
||||
table.insert(dns.servers, {
|
||||
tag = "direct",
|
||||
type = type,
|
||||
server = direct_dns_server,
|
||||
server_port = port,
|
||||
domain_strategy = direct_strategy,
|
||||
detour = "direct",
|
||||
})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local default_dns_flag = "remote"
|
||||
@@ -1561,6 +1656,9 @@ function gen_config(var)
|
||||
end
|
||||
end
|
||||
dns.final = default_dns_flag
|
||||
if version_ge_1_12_0 then -- Migrate to 1.12 DNS
|
||||
dns.strategy = (default_dns_flag == "direct") and direct_strategy or remote_strategy
|
||||
end
|
||||
|
||||
--按分流顺序DNS
|
||||
if dns_domain_rules and #dns_domain_rules > 0 then
|
||||
@@ -1574,15 +1672,24 @@ function gen_config(var)
|
||||
domain_regex = (value.domain_regex and #value.domain_regex > 0) and value.domain_regex or nil,
|
||||
rule_set = (value.rule_set and #value.rule_set > 0) and value.rule_set or nil, --适配srs
|
||||
disable_cache = false,
|
||||
strategy = (version_ge_1_12_0 and value.outboundTag == "direct") and direct_strategy or nil --Migrate to 1.12 DNS
|
||||
}
|
||||
if version_ge_1_12_0 and value.outboundTag == "block" then --Migrate to 1.12 DNS
|
||||
dns_rule.action = "predefined"
|
||||
dns_rule.rcode = "NOERROR"
|
||||
dns_rule.server = nil
|
||||
dns_rule.disable_cache = nil
|
||||
end
|
||||
if value.outboundTag ~= "block" and value.outboundTag ~= "direct" then
|
||||
dns_rule.server = "remote"
|
||||
if value.outboundTag ~= COMMON.default_outbound_tag and remote_server.address then
|
||||
local remote_dns_server = api.clone(remote_server)
|
||||
remote_dns_server.tag = value.outboundTag
|
||||
remote_dns_server.detour = value.outboundTag
|
||||
table.insert(dns.servers, remote_dns_server)
|
||||
dns_rule.server = remote_dns_server.tag
|
||||
dns_rule.strategy = version_ge_1_12_0 and remote_strategy or nil --Migrate to 1.12 DNS
|
||||
dns_rule.client_subnet = (version_ge_1_12_0 and remote_dns_client_ip and remote_dns_client_ip ~= "") and remote_dns_client_ip or nil --Migrate to 1.12 DNS
|
||||
if value.outboundTag ~= COMMON.default_outbound_tag and (remote_server.address or remote_server.server) then
|
||||
local remote_shunt_server = api.clone(remote_server)
|
||||
remote_shunt_server.tag = value.outboundTag
|
||||
remote_shunt_server.detour = value.outboundTag
|
||||
table.insert(dns.servers, remote_shunt_server)
|
||||
dns_rule.server = remote_shunt_server.tag
|
||||
end
|
||||
if remote_dns_fake then
|
||||
local fakedns_dns_rule = api.clone(dns_rule)
|
||||
@@ -1638,12 +1745,44 @@ function gen_config(var)
|
||||
--实验性
|
||||
experimental = experimental,
|
||||
}
|
||||
table.insert(outbounds, {
|
||||
|
||||
local direct_outbound = {
|
||||
type = "direct",
|
||||
tag = "direct",
|
||||
routing_mark = 255,
|
||||
domain_strategy = "prefer_ipv6",
|
||||
})
|
||||
}
|
||||
if not version_ge_1_12_0 then --Migrate to 1.12 DNS
|
||||
direct_outbound.domain_strategy = "prefer_ipv6"
|
||||
else
|
||||
local domain_resolver = {
|
||||
server = "direct",
|
||||
strategy = "prefer_ipv6"
|
||||
}
|
||||
direct_outbound.domain_resolver = domain_resolver
|
||||
|
||||
-- 当没有 direct dns 服务器时添加 local
|
||||
local hasDirect = false
|
||||
if config.dns and config.dns.servers then
|
||||
for _, server in ipairs(config.dns.servers) do
|
||||
if server.tag == "direct" then
|
||||
hasDirect = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not hasDirect then
|
||||
config.dns = {
|
||||
servers = {
|
||||
{
|
||||
type = "local",
|
||||
tag = "direct"
|
||||
}
|
||||
},
|
||||
}
|
||||
end
|
||||
end
|
||||
table.insert(outbounds,direct_outbound)
|
||||
|
||||
table.insert(outbounds, {
|
||||
type = "block",
|
||||
tag = "block"
|
||||
|
@@ -6,12 +6,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=sing-box
|
||||
PKG_VERSION:=1.11.8
|
||||
PKG_VERSION:=1.11.9
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=de797c9e848c9c3a6f99cd779b442a4e856a7f267e514cfc8169f95baf48dadd
|
||||
PKG_HASH:=ee0c183ed9c431a2b6b5f12cad0cfb1d2bccb83147b641d50a619a381fa9d449
|
||||
|
||||
PKG_LICENSE:=GPL-3.0-or-later
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
@@ -21,22 +21,22 @@ define Download/geoip
|
||||
HASH:=735786c00694313090c5d525516463836167422b132ce293873443613b496e92
|
||||
endef
|
||||
|
||||
GEOSITE_VER:=20250422055726
|
||||
GEOSITE_VER:=20250428010409
|
||||
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
|
||||
define Download/geosite
|
||||
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
|
||||
URL_FILE:=dlc.dat
|
||||
FILE:=$(GEOSITE_FILE)
|
||||
HASH:=7a4530370eff2db6265613d9066c2b5eee841e3e7f02288750b03128af6daf7b
|
||||
HASH:=bb0542801286c3efca59305b8fb1b01e808535be3ce1331a9f8709704417ea7d
|
||||
endef
|
||||
|
||||
GEOSITE_IRAN_VER:=202504210040
|
||||
GEOSITE_IRAN_VER:=202504280040
|
||||
GEOSITE_IRAN_FILE:=iran.dat.$(GEOSITE_IRAN_VER)
|
||||
define Download/geosite-ir
|
||||
URL:=https://github.com/bootmortis/iran-hosted-domains/releases/download/$(GEOSITE_IRAN_VER)/
|
||||
URL_FILE:=iran.dat
|
||||
FILE:=$(GEOSITE_IRAN_FILE)
|
||||
HASH:=2d10e5a69771bc7daf612ce31e9dfc2dfd7ea6caa9caecfd88918da0ebd9cba7
|
||||
HASH:=fdad880cfe7dd8bf94e930e2950dd6f8609e2e8543287f411ebe27ec9da4b2c6
|
||||
endef
|
||||
|
||||
define Package/v2ray-geodata/template
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "5.30.0"
|
||||
version = "5.31.0"
|
||||
build = "Custom"
|
||||
codename = "V2Fly, a community-driven edition of V2Ray."
|
||||
intro = "A unified platform for anti-censorship."
|
||||
|
@@ -1,6 +1,6 @@
|
||||
module github.com/v2fly/v2ray-core/v5
|
||||
|
||||
go 1.23.0
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.2
|
||||
|
||||
@@ -24,8 +24,8 @@ require (
|
||||
github.com/pion/dtls/v2 v2.2.12
|
||||
github.com/pion/transport/v2 v2.2.10
|
||||
github.com/pires/go-proxyproto v0.8.0
|
||||
github.com/quic-go/quic-go v0.50.1
|
||||
github.com/refraction-networking/utls v1.6.7
|
||||
github.com/quic-go/quic-go v0.51.0
|
||||
github.com/refraction-networking/utls v1.7.1
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08
|
||||
@@ -41,7 +41,7 @@ require (
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/sync v0.13.0
|
||||
golang.org/x/sys v0.32.0
|
||||
google.golang.org/grpc v1.71.1
|
||||
google.golang.org/grpc v1.72.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gvisor.dev/gvisor v0.0.0-20231020174304-b8a429915ff1
|
||||
@@ -56,7 +56,7 @@ require (
|
||||
github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d // indirect
|
||||
github.com/bufbuild/protocompile v0.14.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cloudflare/circl v1.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||
@@ -92,6 +92,6 @@ require (
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.30.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
nhooyr.io/websocket v1.8.6 // indirect
|
||||
)
|
||||
|
@@ -65,8 +65,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
@@ -442,11 +442,11 @@ github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q=
|
||||
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
|
||||
github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||
github.com/refraction-networking/utls v1.7.1 h1:dxg+jla3uocgN8HtX+ccwDr68uCBBO3qLrkZUbqkcw0=
|
||||
github.com/refraction-networking/utls v1.7.1/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
@@ -807,8 +807,8 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
@@ -823,8 +823,8 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
|
||||
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.11.3</Version>
|
||||
<Version>7.12.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@@ -9,7 +9,6 @@ public sealed class AppHandler
|
||||
private int? _statePort;
|
||||
private int? _statePort2;
|
||||
private Job? _processJob;
|
||||
private bool? _isAdministrator;
|
||||
public static AppHandler Instance => _instance.Value;
|
||||
public Config Config => _config;
|
||||
|
||||
|
@@ -10,7 +10,6 @@ public class CoreAdminHandler
|
||||
public static CoreAdminHandler Instance => _instance.Value;
|
||||
private Config _config;
|
||||
private Action<bool, string>? _updateFunc;
|
||||
private const string _tag = "CoreAdminHandler";
|
||||
private int _linuxSudoPid = -1;
|
||||
|
||||
public async Task Init(Config config, Action<bool, string> updateFunc)
|
||||
@@ -29,8 +28,6 @@ public class CoreAdminHandler
|
||||
}
|
||||
|
||||
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
|
||||
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
|
||||
@@ -87,13 +84,6 @@ public class CoreAdminHandler
|
||||
|
||||
return proc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
UpdateFunc(false, ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task KillProcessAsLinuxSudo()
|
||||
{
|
||||
@@ -102,23 +92,15 @@ public class CoreAdminHandler
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}";
|
||||
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
|
||||
|
||||
var result = await Cli.Wrap(shFilePath)
|
||||
await Cli.Wrap(shFilePath)
|
||||
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
|
||||
.ExecuteAsync();
|
||||
|
||||
_linuxSudoPid = -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
UpdateFunc(false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
|
||||
{
|
||||
|
@@ -238,6 +238,8 @@ public class CoreHandler
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (mayNeedSudo
|
||||
&& _config.TunModeItem.EnableTun
|
||||
&& coreInfo.CoreType == ECoreType.sing_box
|
||||
@@ -248,7 +250,17 @@ public class CoreHandler
|
||||
return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
|
||||
}
|
||||
|
||||
try
|
||||
return await RunProcessNormal(fileName, coreInfo, configPath, displayLog);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
UpdateFunc(mayNeedSudo, ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Process?> RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
|
||||
{
|
||||
Process proc = new()
|
||||
{
|
||||
@@ -291,7 +303,7 @@ public class CoreHandler
|
||||
proc.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
await Task.Delay(500);
|
||||
await Task.Delay(100);
|
||||
AppHandler.Instance.AddProcess(proc.Handle);
|
||||
if (proc is null or { HasExited: true })
|
||||
{
|
||||
@@ -299,13 +311,6 @@ public class CoreHandler
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
UpdateFunc(mayNeedSudo, ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Process
|
||||
}
|
||||
|
@@ -18,6 +18,9 @@
|
||||
<UserControl.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
|
||||
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
||||
<Style x:Key="AccessibleMyChipListBoxItem" TargetType="ListBoxItem" BasedOn="{StaticResource MyChipListBoxItem}">
|
||||
<Setter Property="AutomationProperties.Name" Value="{Binding Remarks}" />
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<DockPanel>
|
||||
@@ -26,8 +29,9 @@
|
||||
x:Name="lstGroup"
|
||||
MaxHeight="200"
|
||||
FontSize="{DynamicResource StdFontSize}"
|
||||
ItemContainerStyle="{StaticResource MyChipListBoxItem}"
|
||||
Style="{StaticResource MaterialDesignChoiceChipPrimaryOutlineListBox}">
|
||||
ItemContainerStyle="{StaticResource AccessibleMyChipListBoxItem}"
|
||||
Style="{StaticResource MaterialDesignChoiceChipPrimaryOutlineListBox}"
|
||||
AutomationProperties.Name="{x:Static resx:ResUI.menuSubscription}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Remarks}" />
|
||||
|
@@ -92,7 +92,13 @@
|
||||
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"
|
||||
DisplayMemberPath="Remarks"
|
||||
FontSize="{DynamicResource StdFontSize}"
|
||||
Style="{StaticResource MaterialDesignFloatingHintComboBox}" />
|
||||
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="ComboBoxItem">
|
||||
<Setter Property="AutomationProperties.Name" Value="{Binding Remarks}" />
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="{StaticResource MarginLeftRight8}" VerticalAlignment="Center">
|
||||
@@ -184,7 +190,13 @@
|
||||
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"
|
||||
DisplayMemberPath="Remarks"
|
||||
FontSize="{DynamicResource StdFontSize}"
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}" />
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="ComboBoxItem">
|
||||
<Setter Property="AutomationProperties.Name" Value="{Binding Remarks}" />
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
</DockPanel>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
@@ -198,7 +210,13 @@
|
||||
AutomationProperties.Name="{x:Static resx:ResUI.menuServers}"
|
||||
DisplayMemberPath="Text"
|
||||
FontSize="{DynamicResource StdFontSize}"
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}" />
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="ComboBoxItem">
|
||||
<Setter Property="AutomationProperties.Name" Value="{Binding Text}" />
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
</DockPanel>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
|
@@ -97,7 +97,7 @@
|
||||
}
|
||||
],
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"domainStrategy": "AsIs",
|
||||
"rules": []
|
||||
},
|
||||
"dns": {
|
||||
|
@@ -286,7 +286,7 @@ object V2rayConfigManager {
|
||||
|
||||
v2rayConfig.routing.domainStrategy =
|
||||
MmkvManager.decodeSettingsString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY)
|
||||
?: "IPIfNonMatch"
|
||||
?: "AsIs"
|
||||
|
||||
val rulesetItems = MmkvManager.decodeRoutingRulesets()
|
||||
rulesetItems?.forEach { key ->
|
||||
|
@@ -33,23 +33,21 @@ type cachedReader struct {
|
||||
cache buf.MultiBuffer
|
||||
}
|
||||
|
||||
func (r *cachedReader) Cache(b *buf.Buffer) {
|
||||
mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
|
||||
func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error {
|
||||
mb, err := r.reader.ReadMultiBufferTimeout(deadline)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Lock()
|
||||
if !mb.IsEmpty() {
|
||||
r.cache, _ = buf.MergeMulti(r.cache, mb)
|
||||
}
|
||||
cacheLen := r.cache.Len()
|
||||
if cacheLen <= b.Cap() {
|
||||
b.Clear()
|
||||
} else {
|
||||
b.Release()
|
||||
*b = *buf.NewWithSize(cacheLen)
|
||||
}
|
||||
rawBytes := b.Extend(cacheLen)
|
||||
rawBytes := b.Extend(b.Cap())
|
||||
n := r.cache.Copy(rawBytes)
|
||||
b.Resize(0, int32(n))
|
||||
r.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *cachedReader) readInternal() buf.MultiBuffer {
|
||||
@@ -355,7 +353,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
||||
}
|
||||
|
||||
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
|
||||
payload := buf.New()
|
||||
payload := buf.NewWithSize(32767)
|
||||
defer payload.Release()
|
||||
|
||||
sniffer := NewSniffer(ctx)
|
||||
@@ -367,26 +365,33 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
|
||||
}
|
||||
|
||||
contentResult, contentErr := func() (SniffResult, error) {
|
||||
cacheDeadline := 200 * time.Millisecond
|
||||
totalAttempt := 0
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
totalAttempt++
|
||||
if totalAttempt > 2 {
|
||||
return nil, errSniffingTimeout
|
||||
}
|
||||
cachingStartingTimeStamp := time.Now()
|
||||
cacheErr := cReader.Cache(payload, cacheDeadline)
|
||||
cachingTimeElapsed := time.Since(cachingStartingTimeStamp)
|
||||
cacheDeadline -= cachingTimeElapsed
|
||||
|
||||
cReader.Cache(payload)
|
||||
if !payload.IsEmpty() {
|
||||
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
|
||||
if err != common.ErrNoClue {
|
||||
switch err {
|
||||
case common.ErrNoClue: // No Clue: protocol not matches, and sniffer cannot determine whether there will be a match or not
|
||||
totalAttempt++
|
||||
case protocol.ErrProtoNeedMoreData: // Protocol Need More Data: protocol matches, but need more data to complete sniffing
|
||||
if cacheErr != nil { // Cache error (e.g. timeout) counts for failed attempt
|
||||
totalAttempt++
|
||||
}
|
||||
default:
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
if payload.IsFull() {
|
||||
return nil, errUnknownContent
|
||||
if totalAttempt >= 2 || cacheDeadline <= 0 {
|
||||
return nil, errSniffingTimeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/protocol/bittorrent"
|
||||
"github.com/xtls/xray-core/common/protocol/http"
|
||||
"github.com/xtls/xray-core/common/protocol/quic"
|
||||
@@ -58,14 +59,17 @@ var errUnknownContent = errors.New("unknown content")
|
||||
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
|
||||
var pendingSniffer []protocolSnifferWithMetadata
|
||||
for _, si := range s.sniffer {
|
||||
s := si.protocolSniffer
|
||||
protocolSniffer := si.protocolSniffer
|
||||
if si.metadataSniffer || si.network != network {
|
||||
continue
|
||||
}
|
||||
result, err := s(c, payload)
|
||||
result, err := protocolSniffer(c, payload)
|
||||
if err == common.ErrNoClue {
|
||||
pendingSniffer = append(pendingSniffer, si)
|
||||
continue
|
||||
} else if err == protocol.ErrProtoNeedMoreData { // Sniffer protocol matched, but need more data to complete sniffing
|
||||
s.sniffer = []protocolSnifferWithMetadata{si}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err == nil && result != nil {
|
||||
|
@@ -15,6 +15,15 @@ const (
|
||||
|
||||
var pool = bytespool.GetPool(Size)
|
||||
|
||||
// ownership represents the data owner of the buffer.
|
||||
type ownership uint8
|
||||
|
||||
const (
|
||||
managed ownership = iota
|
||||
unmanaged
|
||||
bytespools
|
||||
)
|
||||
|
||||
// Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles
|
||||
// the buffer into an internal buffer pool, in order to recreate a buffer more
|
||||
// quickly.
|
||||
@@ -22,11 +31,11 @@ type Buffer struct {
|
||||
v []byte
|
||||
start int32
|
||||
end int32
|
||||
unmanaged bool
|
||||
ownership ownership
|
||||
UDP *net.Destination
|
||||
}
|
||||
|
||||
// New creates a Buffer with 0 length and 8K capacity.
|
||||
// New creates a Buffer with 0 length and 8K capacity, managed.
|
||||
func New() *Buffer {
|
||||
buf := pool.Get().([]byte)
|
||||
if cap(buf) >= Size {
|
||||
@@ -40,7 +49,7 @@ func New() *Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
// NewExisted creates a managed, standard size Buffer with an existed bytearray
|
||||
// NewExisted creates a standard size Buffer with an existed bytearray, managed.
|
||||
func NewExisted(b []byte) *Buffer {
|
||||
if cap(b) < Size {
|
||||
panic("Invalid buffer")
|
||||
@@ -57,16 +66,16 @@ func NewExisted(b []byte) *Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
// FromBytes creates a Buffer with an existed bytearray
|
||||
// FromBytes creates a Buffer with an existed bytearray, unmanaged.
|
||||
func FromBytes(b []byte) *Buffer {
|
||||
return &Buffer{
|
||||
v: b,
|
||||
end: int32(len(b)),
|
||||
unmanaged: true,
|
||||
ownership: unmanaged,
|
||||
}
|
||||
}
|
||||
|
||||
// StackNew creates a new Buffer object on stack.
|
||||
// StackNew creates a new Buffer object on stack, managed.
|
||||
// This method is for buffers that is released in the same function.
|
||||
func StackNew() Buffer {
|
||||
buf := pool.Get().([]byte)
|
||||
@@ -81,9 +90,17 @@ func StackNew() Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
// NewWithSize creates a Buffer with 0 length and capacity with at least the given size, bytespool's.
|
||||
func NewWithSize(size int32) *Buffer {
|
||||
return &Buffer{
|
||||
v: bytespool.Alloc(size),
|
||||
ownership: bytespools,
|
||||
}
|
||||
}
|
||||
|
||||
// Release recycles the buffer into an internal buffer pool.
|
||||
func (b *Buffer) Release() {
|
||||
if b == nil || b.v == nil || b.unmanaged {
|
||||
if b == nil || b.v == nil || b.ownership == unmanaged {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -91,9 +108,14 @@ func (b *Buffer) Release() {
|
||||
b.v = nil
|
||||
b.Clear()
|
||||
|
||||
switch b.ownership {
|
||||
case managed:
|
||||
if cap(p) == Size {
|
||||
pool.Put(p)
|
||||
}
|
||||
case bytespools:
|
||||
bytespool.Free(p)
|
||||
}
|
||||
b.UDP = nil
|
||||
}
|
||||
|
||||
@@ -215,13 +237,6 @@ func (b *Buffer) Cap() int32 {
|
||||
return int32(len(b.v))
|
||||
}
|
||||
|
||||
// NewWithSize creates a Buffer with 0 length and capacity with at least the given size.
|
||||
func NewWithSize(size int32) *Buffer {
|
||||
return &Buffer{
|
||||
v: bytespool.Alloc(size),
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the buffer is empty.
|
||||
func (b *Buffer) IsEmpty() bool {
|
||||
return b.Len() == 0
|
||||
|
@@ -1 +1,7 @@
|
||||
package protocol // import "github.com/xtls/xray-core/common/protocol"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var ErrProtoNeedMoreData = errors.New("protocol matches, but need more data to complete sniffing")
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/tls"
|
||||
@@ -13,6 +12,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/bytespool"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
ptls "github.com/xtls/xray-core/common/protocol/tls"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
@@ -47,22 +47,17 @@ var (
|
||||
errNotQuicInitial = errors.New("not initial packet")
|
||||
)
|
||||
|
||||
func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||
// In extremely rare cases, this sniffer may cause slice error
|
||||
// and we set recover() here to prevent crash.
|
||||
// TODO: Thoroughly fix this panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
errors.LogError(context.Background(), "Failed to sniff QUIC: ", r)
|
||||
resultReturn = nil
|
||||
errorReturn = common.ErrNoClue
|
||||
func SniffQUIC(b []byte) (*SniffHeader, error) {
|
||||
if len(b) == 0 {
|
||||
return nil, common.ErrNoClue
|
||||
}
|
||||
}()
|
||||
|
||||
// Crypto data separated across packets
|
||||
cryptoLen := 0
|
||||
cryptoData := bytespool.Alloc(int32(len(b)))
|
||||
cryptoData := bytespool.Alloc(32767)
|
||||
defer bytespool.Free(cryptoData)
|
||||
cache := buf.New()
|
||||
defer cache.Release()
|
||||
|
||||
// Parse QUIC packets
|
||||
for len(b) > 0 {
|
||||
@@ -105,6 +100,7 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||
return nil, errNotQuic
|
||||
}
|
||||
|
||||
if isQuicInitial { // Only initial packets have token, see https://datatracker.ietf.org/doc/html/rfc9000#section-17.2.2
|
||||
tokenLen, err := quicvarint.Read(buffer)
|
||||
if err != nil || tokenLen > uint64(len(b)) {
|
||||
return nil, errNotQuic
|
||||
@@ -113,6 +109,7 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||
if _, err = buffer.ReadBytes(int32(tokenLen)); err != nil {
|
||||
return nil, errNotQuic
|
||||
}
|
||||
}
|
||||
|
||||
packetLen, err := quicvarint.Read(buffer)
|
||||
if err != nil {
|
||||
@@ -130,9 +127,6 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||
continue
|
||||
}
|
||||
|
||||
origPNBytes := make([]byte, 4)
|
||||
copy(origPNBytes, b[hdrLen:hdrLen+4])
|
||||
|
||||
var salt []byte
|
||||
if versionNumber == version1 {
|
||||
salt = quicSalt
|
||||
@@ -147,44 +141,34 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cache := buf.New()
|
||||
defer cache.Release()
|
||||
|
||||
cache.Clear()
|
||||
mask := cache.Extend(int32(block.BlockSize()))
|
||||
block.Encrypt(mask, b[hdrLen+4:hdrLen+4+16])
|
||||
b[0] ^= mask[0] & 0xf
|
||||
for i := range b[hdrLen : hdrLen+4] {
|
||||
packetNumberLength := int(b[0]&0x3 + 1)
|
||||
for i := range packetNumberLength {
|
||||
b[hdrLen+i] ^= mask[i+1]
|
||||
}
|
||||
packetNumberLength := b[0]&0x3 + 1
|
||||
if packetNumberLength != 1 {
|
||||
return nil, errNotQuicInitial
|
||||
}
|
||||
var packetNumber uint32
|
||||
{
|
||||
n, err := buffer.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetNumber = uint32(n)
|
||||
}
|
||||
|
||||
extHdrLen := hdrLen + int(packetNumberLength)
|
||||
copy(b[extHdrLen:hdrLen+4], origPNBytes[packetNumberLength:])
|
||||
data := b[extHdrLen : int(packetLen)+hdrLen]
|
||||
|
||||
key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16)
|
||||
iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12)
|
||||
cipher := AEADAESGCMTLS13(key, iv)
|
||||
|
||||
nonce := cache.Extend(int32(cipher.NonceSize()))
|
||||
binary.BigEndian.PutUint64(nonce[len(nonce)-8:], uint64(packetNumber))
|
||||
_, err = buffer.Read(nonce[len(nonce)-packetNumberLength:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extHdrLen := hdrLen + packetNumberLength
|
||||
data := b[extHdrLen : int(packetLen)+hdrLen]
|
||||
decrypted, err := cipher.Open(b[extHdrLen:extHdrLen], nonce, data, b[:extHdrLen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffer = buf.FromBytes(decrypted)
|
||||
for i := 0; !buffer.IsEmpty(); i++ {
|
||||
frameType := byte(0x0) // Default to PADDING frame
|
||||
for !buffer.IsEmpty() {
|
||||
frameType, _ := buffer.ReadByte()
|
||||
for frameType == 0x0 && !buffer.IsEmpty() {
|
||||
frameType, _ = buffer.ReadByte()
|
||||
}
|
||||
@@ -234,13 +218,12 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
if cryptoLen < int(offset+length) {
|
||||
cryptoLen = int(offset + length)
|
||||
if len(cryptoData) < cryptoLen {
|
||||
newCryptoData := bytespool.Alloc(int32(cryptoLen))
|
||||
copy(newCryptoData, cryptoData)
|
||||
bytespool.Free(cryptoData)
|
||||
cryptoData = newCryptoData
|
||||
newCryptoLen := int(offset + length)
|
||||
if len(cryptoData) < newCryptoLen {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
wipeBytes(cryptoData[cryptoLen:newCryptoLen])
|
||||
cryptoLen = newCryptoLen
|
||||
}
|
||||
if _, err := buffer.Read(cryptoData[offset : offset+length]); err != nil { // Field: Crypto Data
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
@@ -276,7 +259,14 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) {
|
||||
}
|
||||
return &SniffHeader{domain: tlsHdr.Domain()}, nil
|
||||
}
|
||||
return nil, common.ErrNoClue
|
||||
// All payload is parsed as valid QUIC packets, but we need more packets for crypto data to read client hello.
|
||||
return nil, protocol.ErrProtoNeedMoreData
|
||||
}
|
||||
|
||||
func wipeBytes(b []byte) {
|
||||
for i := range len(b) {
|
||||
b[i] = 0x0
|
||||
}
|
||||
}
|
||||
|
||||
func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {
|
||||
|
File diff suppressed because one or more lines are too long
@@ -3,9 +3,9 @@ package tls
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
type SniffHeader struct {
|
||||
@@ -59,9 +59,6 @@ func ReadClientHello(data []byte, h *SniffHeader) error {
|
||||
}
|
||||
data = data[1+compressionMethodsLen:]
|
||||
|
||||
if len(data) == 0 {
|
||||
return errNotClientHello
|
||||
}
|
||||
if len(data) < 2 {
|
||||
return errNotClientHello
|
||||
}
|
||||
@@ -104,13 +101,21 @@ func ReadClientHello(data []byte, h *SniffHeader) error {
|
||||
return errNotClientHello
|
||||
}
|
||||
if nameType == 0 {
|
||||
serverName := string(d[:nameLen])
|
||||
// QUIC separated across packets
|
||||
// May cause the serverName to be incomplete
|
||||
b := byte(0)
|
||||
for _, b = range d[:nameLen] {
|
||||
if b <= ' ' {
|
||||
return protocol.ErrProtoNeedMoreData
|
||||
}
|
||||
}
|
||||
// An SNI value may not include a
|
||||
// trailing dot. See
|
||||
// https://tools.ietf.org/html/rfc6066#section-3.
|
||||
if strings.HasSuffix(serverName, ".") {
|
||||
if b == '.' {
|
||||
return errNotClientHello
|
||||
}
|
||||
serverName := string(d[:nameLen])
|
||||
h.domain = serverName
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user