From 21d70bf50e3bf8ac2ff719630a71d533ed08542f Mon Sep 17 00:00:00 2001 From: "github-action[bot]" Date: Mon, 28 Apr 2025 20:38:09 +0200 Subject: [PATCH] Update On Mon Apr 28 20:38:09 CEST 2025 --- .github/update.log | 1 + clash-meta/adapter/provider/healthcheck.go | 13 +- clash-meta/component/updater/update_geo.go | 30 +- clash-meta/hub/executor/executor.go | 3 +- clash-nyanpasu/manifest/version.json | 4 +- clash-verge-rev/UPDATELOG.md | 1 + clash-verge-rev/src-tauri/src/core/service.rs | 2 +- ...ment-ignoring-the-hardware-TX_FAULT-.patch | 85 +++++ .../790-SFP-GE-T-ignore-TX_FAULT.patch | 63 ---- mihomo/adapter/provider/healthcheck.go | 13 +- mihomo/component/updater/update_geo.go | 30 +- mihomo/hub/executor/executor.go | 3 +- .../luasrc/passwall/util_sing-box.lua | 317 +++++++++++++----- shadowsocks-rust/Cargo.lock | 4 +- shadowsocks-rust/deny.toml | 3 +- sing-box/common/humanize/bytes.go | 158 --------- sing-box/debug.go | 6 +- sing-box/debug_http.go | 8 +- sing-box/dns/transport/https.go | 7 +- sing-box/dns/transport/quic/http3.go | 7 +- sing-box/dns/transport/quic/quic.go | 7 +- sing-box/dns/transport/tcp.go | 7 +- sing-box/dns/transport/tls.go | 7 +- sing-box/dns/transport/udp.go | 7 +- sing-box/docs/changelog.md | 2 +- sing-box/docs/configuration/inbound/anytls.md | 22 +- .../docs/configuration/inbound/anytls.zh.md | 22 +- sing-box/experimental/libbox/setup.go | 6 +- sing-box/go.mod | 4 +- sing-box/go.sum | 8 +- sing-box/option/debug.go | 47 +-- sing-box/option/hysteria.go | 51 +-- sing-box/protocol/hysteria/inbound.go | 16 +- sing-box/protocol/hysteria/outbound.go | 15 +- .../luasrc/passwall/util_sing-box.lua | 317 +++++++++++++----- small/sing-box/Makefile | 4 +- small/v2ray-geodata/Makefile | 8 +- v2ray-core/core.go | 2 +- v2ray-core/go.mod | 12 +- v2ray-core/go.sum | 20 +- v2rayn/v2rayN/Directory.Build.props | 2 +- v2rayn/v2rayN/Directory.Packages.props | 2 +- .../v2rayN/ServiceLib/Handler/AppHandler.cs | 1 - .../ServiceLib/Handler/CoreAdminHandler.cs | 126 +++---- .../v2rayN/ServiceLib/Handler/CoreHandler.cs | 115 ++++--- v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml | 8 +- v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml | 24 +- .../app/src/main/assets/v2ray_config.json | 2 +- .../v2ray/ang/handler/V2rayConfigManager.kt | 2 +- xray-core/app/dispatcher/default.go | 43 +-- xray-core/app/dispatcher/sniffer.go | 8 +- xray-core/common/buf/buffer.go | 47 ++- xray-core/common/protocol/protocol.go | 6 + xray-core/common/protocol/quic/sniff.go | 94 +++--- xray-core/common/protocol/quic/sniff_test.go | 251 ++++++++++++++ xray-core/common/protocol/tls/sniff.go | 17 +- 56 files changed, 1227 insertions(+), 863 deletions(-) create mode 100644 lede/target/linux/generic/backport-6.6/730-v6.7-net-sfp-re-implement-ignoring-the-hardware-TX_FAULT-.patch delete mode 100644 lede/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch delete mode 100644 sing-box/common/humanize/bytes.go diff --git a/.github/update.log b/.github/update.log index 3cf84f5acc..bfeee58554 100644 --- a/.github/update.log +++ b/.github/update.log @@ -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 diff --git a/clash-meta/adapter/provider/healthcheck.go b/clash-meta/adapter/provider/healthcheck.go index 8737ff96ae..2bddd8e79e 100644 --- a/clash-meta/adapter/provider/healthcheck.go +++ b/clash-meta/adapter/provider/healthcheck.go @@ -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 }) } } diff --git a/clash-meta/component/updater/update_geo.go b/clash-meta/component/updater/update_geo.go index 719a521515..0778087af0 100644 --- a/clash-meta/component/updater/update_geo.go +++ b/clash-meta/component/updater/update_geo.go @@ -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") diff --git a/clash-meta/hub/executor/executor.go b/clash-meta/hub/executor/executor.go index d046223e72..670fa7b89e 100644 --- a/clash-meta/hub/executor/executor.go +++ b/clash-meta/hub/executor/executor.go @@ -375,9 +375,8 @@ func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) { } }() } - } - + wg.Wait() } func updateSniffer(snifferConfig *sniffer.Config) { diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 47a771e106..f6ae9a1ef0 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -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" } diff --git a/clash-verge-rev/UPDATELOG.md b/clash-verge-rev/UPDATELOG.md index a397098923..217f6b58de 100644 --- a/clash-verge-rev/UPDATELOG.md +++ b/clash-verge-rev/UPDATELOG.md @@ -16,6 +16,7 @@ - Windows 错误的全局快捷键 'Ctrl+Q' 注册 - Vless URL 解码时网络类型错误 - 切换自定义代理地址导致系统代理状态异常 + - Macos TUN 默认无效网卡名称 #### 新增了: - 允许代理主机地址设置为非 127.0.0.1 对 WSL 代理友好 diff --git a/clash-verge-rev/src-tauri/src/core/service.rs b/clash-verge-rev/src-tauri/src/core/service.rs index bc9df585fb..03e6993961 100644 --- a/clash-verge-rev/src-tauri/src/core/service.rs +++ b/clash-verge-rev/src-tauri/src/core/service.rs @@ -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分钟冷却期 diff --git a/lede/target/linux/generic/backport-6.6/730-v6.7-net-sfp-re-implement-ignoring-the-hardware-TX_FAULT-.patch b/lede/target/linux/generic/backport-6.6/730-v6.7-net-sfp-re-implement-ignoring-the-hardware-TX_FAULT-.patch new file mode 100644 index 0000000000..9cb491b2d8 --- /dev/null +++ b/lede/target/linux/generic/backport-6.6/730-v6.7-net-sfp-re-implement-ignoring-the-hardware-TX_FAULT-.patch @@ -0,0 +1,85 @@ +From e184e8609f8c1cd9fef703f667245b6ebd89c2ed Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Tested-by: Christian Marangi +Link: https://lore.kernel.org/r/E1qnfXc-008UDY-91@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + 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)) diff --git a/lede/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch b/lede/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch deleted file mode 100644 index b125e93aaf..0000000000 --- a/lede/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 7cc39a6bedbd85f3ff7e16845f310e4ce8d9833f Mon Sep 17 00:00:00 2001 -From: Daniel Golle -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 , - Andrew Lunn , - Heiner Kallweit -Cc: David S. Miller , - Eric Dumazet , - Jakub Kicinski , - Paolo Abeni , - Josef Schlehofer - -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 -Signed-off-by: Daniel Golle ---- - 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)) diff --git a/mihomo/adapter/provider/healthcheck.go b/mihomo/adapter/provider/healthcheck.go index 8737ff96ae..2bddd8e79e 100644 --- a/mihomo/adapter/provider/healthcheck.go +++ b/mihomo/adapter/provider/healthcheck.go @@ -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 }) } } diff --git a/mihomo/component/updater/update_geo.go b/mihomo/component/updater/update_geo.go index 719a521515..0778087af0 100644 --- a/mihomo/component/updater/update_geo.go +++ b/mihomo/component/updater/update_geo.go @@ -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") diff --git a/mihomo/hub/executor/executor.go b/mihomo/hub/executor/executor.go index d046223e72..670fa7b89e 100644 --- a/mihomo/hub/executor/executor.go +++ b/mihomo/hub/executor/executor.go @@ -375,9 +375,8 @@ func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) { } }() } - } - + wg.Wait() } func updateSniffer(snifferConfig *sniffer.Config) { diff --git a/openwrt-passwall/luci-app-passwall/luasrc/passwall/util_sing-box.lua b/openwrt-passwall/luci-app-passwall/luasrc/passwall/util_sing-box.lua index 9453b0aed9..5372fbea6f 100644 --- a/openwrt-passwall/luci-app-passwall/luasrc/passwall/util_sing-box.lua +++ b/openwrt-passwall/luci-app-passwall/luasrc/passwall/util_sing-box.lua @@ -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, } - table.insert(dns.servers, { - tag = "block", - address = "rcode://success", - }) + 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,57 +1457,118 @@ function gen_config(var) remote_strategy = "ipv6_only" end - local remote_server = { - tag = "remote", - address_strategy = "prefer_ipv4", - strategy = remote_strategy, - address_resolver = "direct", - detour = default_outTag, - client_subnet = (remote_dns_client_ip and remote_dns_client_ip ~= "") and remote_dns_client_ip or nil, - } - - if remote_dns_udp_server then - local server_port = tonumber(remote_dns_port) or 53 - remote_server.address = "udp://" .. remote_dns_udp_server .. ":" .. server_port - end - - if remote_dns_tcp_server then - remote_server.address = remote_dns_tcp_server - end - - if remote_dns_doh_url and remote_dns_doh_host then - remote_server.address = remote_dns_doh_url - end - - if remote_server.address then - table.insert(dns.servers, remote_server) - end - + local remote_server = {} local fakedns_tag = "remote_fakeip" - if remote_dns_fake then - dns.fakeip = { - enabled = true, - inet4_range = "198.18.0.0/15", - inet6_range = "fc00::/18", - } - - table.insert(dns.servers, { - tag = fakedns_tag, - address = "fakeip", - strategy = remote_strategy, - }) - if not experimental then - experimental = {} - end - experimental.cache_file = { - enabled = true, - store_fakeip = true, - path = api.CACHE_PATH .. "/singbox_" .. flag .. ".db" + 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, + address_resolver = "direct", + detour = default_outTag, + client_subnet = (remote_dns_client_ip and remote_dns_client_ip ~= "") and remote_dns_client_ip or nil, } + + if remote_dns_udp_server then + local server_port = tonumber(remote_dns_port) or 53 + remote_server.address = "udp://" .. remote_dns_udp_server .. ":" .. server_port + end + + if remote_dns_tcp_server then + remote_server.address = remote_dns_tcp_server + end + + if remote_dns_doh_url and remote_dns_doh_host then + remote_server.address = remote_dns_doh_url + end + + if remote_server.address then + table.insert(dns.servers, remote_server) + end + + if remote_dns_fake then + dns.fakeip = { + enabled = true, + inet4_range = "198.18.0.0/15", + inet6_range = "fc00::/18", + } + + table.insert(dns.servers, { + tag = fakedns_tag, + address = "fakeip", + strategy = remote_strategy, + }) + + if not experimental then + experimental = {} + end + experimental.cache_file = { + enabled = true, + store_fakeip = true, + 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,40 +1577,61 @@ 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" + + 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 + direct_dns_server = "udp://" .. direct_dns_udp_server .. ":" .. port + elseif direct_dns_tcp_server then + port = tonumber(direct_dns_port) or 53 + direct_dns_server = "tcp://" .. direct_dns_tcp_server .. ":" .. port + elseif direct_dns_dot_server then + port = tonumber(direct_dns_port) or 853 + if direct_dns_dot_server:find(":") == nil then + direct_dns_server = "tls://" .. direct_dns_dot_server .. ":" .. port + else + direct_dns_server = "tls://[" .. direct_dns_dot_server .. "]:" .. port + end + end + + table.insert(dns.servers, { + tag = "direct", + address = direct_dns_server, + address_strategy = "prefer_ipv6", + 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 - local direct_dns_server, port - if direct_dns_udp_server then - port = tonumber(direct_dns_port) or 53 - direct_dns_server = "udp://" .. direct_dns_udp_server .. ":" .. port - elseif direct_dns_tcp_server then - port = tonumber(direct_dns_port) or 53 - direct_dns_server = "tcp://" .. direct_dns_tcp_server .. ":" .. port - elseif direct_dns_dot_server then - port = tonumber(direct_dns_port) or 853 - if direct_dns_dot_server:find(":") == nil then - direct_dns_server = "tls://" .. direct_dns_dot_server .. ":" .. port - else - direct_dns_server = "tls://[" .. direct_dns_dot_server .. "]:" .. port - end - end - - table.insert(dns.servers, { - tag = "direct", - address = direct_dns_server, - address_strategy = "prefer_ipv6", - strategy = direct_strategy, - detour = "direct", - }) 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" diff --git a/shadowsocks-rust/Cargo.lock b/shadowsocks-rust/Cargo.lock index 8bcc8d3dfd..5ce399d89c 100644 --- a/shadowsocks-rust/Cargo.lock +++ b/shadowsocks-rust/Cargo.lock @@ -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", ] diff --git a/shadowsocks-rust/deny.toml b/shadowsocks-rust/deny.toml index 6795b00196..e32d47d753 100644 --- a/shadowsocks-rust/deny.toml +++ b/shadowsocks-rust/deny.toml @@ -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, diff --git a/sing-box/common/humanize/bytes.go b/sing-box/common/humanize/bytes.go deleted file mode 100644 index 6ee4d26898..0000000000 --- a/sing-box/common/humanize/bytes.go +++ /dev/null @@ -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) -} diff --git a/sing-box/debug.go b/sing-box/debug.go index 2fa962d642..1726c10e92 100644 --- a/sing-box/debug.go +++ b/sing-box/debug.go @@ -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 diff --git a/sing-box/debug_http.go b/sing-box/debug_http.go index 3215977821..e51a073143 100644 --- a/sing-box/debug_http.go +++ b/sing-box/debug_http.go @@ -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()) diff --git a/sing-box/dns/transport/https.go b/sing-box/dns/transport/https.go index 6e71dae120..a13d911630 100644 --- a/sing-box/dns/transport/https.go +++ b/sing-box/dns/transport/https.go @@ -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, diff --git a/sing-box/dns/transport/quic/http3.go b/sing-box/dns/transport/quic/http3.go index 6d79657631..fd1591a379 100644 --- a/sing-box/dns/transport/quic/http3.go +++ b/sing-box/dns/transport/quic/http3.go @@ -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, diff --git a/sing-box/dns/transport/quic/quic.go b/sing-box/dns/transport/quic/quic.go index f48412a414..515aff58e3 100644 --- a/sing-box/dns/transport/quic/quic.go +++ b/sing-box/dns/transport/quic/quic.go @@ -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, diff --git a/sing-box/dns/transport/tcp.go b/sing-box/dns/transport/tcp.go index 3277c36d40..3039c57422 100644 --- a/sing-box/dns/transport/tcp.go +++ b/sing-box/dns/transport/tcp.go @@ -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, diff --git a/sing-box/dns/transport/tls.go b/sing-box/dns/transport/tls.go index a584bff8ee..b6ac294aa8 100644 --- a/sing-box/dns/transport/tls.go +++ b/sing-box/dns/transport/tls.go @@ -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, diff --git a/sing-box/dns/transport/udp.go b/sing-box/dns/transport/udp.go index d43bc21fe0..a9c1d4d98d 100644 --- a/sing-box/dns/transport/udp.go +++ b/sing-box/dns/transport/udp.go @@ -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 } diff --git a/sing-box/docs/changelog.md b/sing-box/docs/changelog.md index 50ab6a6430..628dad4d10 100644 --- a/sing-box/docs/changelog.md +++ b/sing-box/docs/changelog.md @@ -2,7 +2,7 @@ icon: material/alert-decagram --- -#### 1.12.0-beta.6 +#### 1.12.0-beta.7 * Fixes and improvements diff --git a/sing-box/docs/configuration/inbound/anytls.md b/sing-box/docs/configuration/inbound/anytls.md index 55790810f6..f3780119f6 100644 --- a/sing-box/docs/configuration/inbound/anytls.md +++ b/sing-box/docs/configuration/inbound/anytls.md @@ -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 diff --git a/sing-box/docs/configuration/inbound/anytls.zh.md b/sing-box/docs/configuration/inbound/anytls.zh.md index 099da77724..55b6749ed1 100644 --- a/sing-box/docs/configuration/inbound/anytls.zh.md +++ b/sing-box/docs/configuration/inbound/anytls.zh.md @@ -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 diff --git a/sing-box/experimental/libbox/setup.go b/sing-box/experimental/libbox/setup.go index 184d525012..ad898fee7c 100644 --- a/sing-box/experimental/libbox/setup.go +++ b/sing-box/experimental/libbox/setup.go @@ -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 { diff --git a/sing-box/go.mod b/sing-box/go.mod index d4dd9fba9d..a29a3e95e9 100644 --- a/sing-box/go.mod +++ b/sing-box/go.mod @@ -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 diff --git a/sing-box/go.sum b/sing-box/go.sum index c67128b88d..4b46a0479c 100644 --- a/sing-box/go.sum +++ b/sing-box/go.sum @@ -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= diff --git a/sing-box/option/debug.go b/sing-box/option/debug.go index 0b0b825a82..3dfef7b0ca 100644 --- a/sing-box/option/debug.go +++ b/sing-box/option/debug.go @@ -1,43 +1,14 @@ 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"` - GCPercent *int `json:"gc_percent,omitempty"` - MaxStack *int `json:"max_stack,omitempty"` - 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"` - 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 + Listen string `json:"listen,omitempty"` + GCPercent *int `json:"gc_percent,omitempty"` + MaxStack *int `json:"max_stack,omitempty"` + MaxThreads *int `json:"max_threads,omitempty"` + PanicOnFault *bool `json:"panic_on_fault,omitempty"` + TraceBack string `json:"trace_back,omitempty"` + MemoryLimit *byteformats.MemoryBytes `json:"memory_limit,omitempty"` + OOMKiller *bool `json:"oom_killer,omitempty"` } diff --git a/sing-box/option/hysteria.go b/sing-box/option/hysteria.go index c3dc78ef4b..186759010e 100644 --- a/sing-box/option/hysteria.go +++ b/sing-box/option/hysteria.go @@ -1,19 +1,22 @@ 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"` - UpMbps int `json:"up_mbps,omitempty"` - Down string `json:"down,omitempty"` - DownMbps int `json:"down_mbps,omitempty"` - Obfs string `json:"obfs,omitempty"` - Users []HysteriaUser `json:"users,omitempty"` - ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"` - ReceiveWindowClient uint64 `json:"recv_window_client,omitempty"` - MaxConnClient int `json:"max_conn_client,omitempty"` - DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"` + Up *byteformats.NetworkBytesCompat `json:"up,omitempty"` + UpMbps int `json:"up_mbps,omitempty"` + Down *byteformats.NetworkBytesCompat `json:"down,omitempty"` + DownMbps int `json:"down_mbps,omitempty"` + Obfs string `json:"obfs,omitempty"` + Users []HysteriaUser `json:"users,omitempty"` + ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"` + ReceiveWindowClient uint64 `json:"recv_window_client,omitempty"` + MaxConnClient int `json:"max_conn_client,omitempty"` + DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"` InboundTLSOptionsContainer } @@ -26,18 +29,18 @@ type HysteriaUser struct { type HysteriaOutboundOptions struct { DialerOptions ServerOptions - ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"` - HopInterval badoption.Duration `json:"hop_interval,omitempty"` - Up string `json:"up,omitempty"` - UpMbps int `json:"up_mbps,omitempty"` - Down string `json:"down,omitempty"` - DownMbps int `json:"down_mbps,omitempty"` - Obfs string `json:"obfs,omitempty"` - Auth []byte `json:"auth,omitempty"` - AuthString string `json:"auth_str,omitempty"` - ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"` - ReceiveWindow uint64 `json:"recv_window,omitempty"` - DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"` - Network NetworkList `json:"network,omitempty"` + ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"` + HopInterval badoption.Duration `json:"hop_interval,omitempty"` + Up *byteformats.NetworkBytesCompat `json:"up,omitempty"` + UpMbps int `json:"up_mbps,omitempty"` + Down *byteformats.NetworkBytesCompat `json:"down,omitempty"` + DownMbps int `json:"down_mbps,omitempty"` + Obfs string `json:"obfs,omitempty"` + Auth []byte `json:"auth,omitempty"` + AuthString string `json:"auth_str,omitempty"` + ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"` + ReceiveWindow uint64 `json:"recv_window,omitempty"` + DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"` + Network NetworkList `json:"network,omitempty"` OutboundTLSOptionsContainer } diff --git a/sing-box/protocol/hysteria/inbound.go b/sing-box/protocol/hysteria/inbound.go index 872e4dd770..5afc440d37 100644 --- a/sing-box/protocol/hysteria/inbound.go +++ b/sing-box/protocol/hysteria/inbound.go @@ -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 } diff --git a/sing-box/protocol/hysteria/outbound.go b/sing-box/protocol/hysteria/outbound.go index d3035c714d..42a37ee6c1 100644 --- a/sing-box/protocol/hysteria/outbound.go +++ b/sing-box/protocol/hysteria/outbound.go @@ -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 } diff --git a/small/luci-app-passwall/luasrc/passwall/util_sing-box.lua b/small/luci-app-passwall/luasrc/passwall/util_sing-box.lua index 9453b0aed9..5372fbea6f 100644 --- a/small/luci-app-passwall/luasrc/passwall/util_sing-box.lua +++ b/small/luci-app-passwall/luasrc/passwall/util_sing-box.lua @@ -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, } - table.insert(dns.servers, { - tag = "block", - address = "rcode://success", - }) + 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,57 +1457,118 @@ function gen_config(var) remote_strategy = "ipv6_only" end - local remote_server = { - tag = "remote", - address_strategy = "prefer_ipv4", - strategy = remote_strategy, - address_resolver = "direct", - detour = default_outTag, - client_subnet = (remote_dns_client_ip and remote_dns_client_ip ~= "") and remote_dns_client_ip or nil, - } - - if remote_dns_udp_server then - local server_port = tonumber(remote_dns_port) or 53 - remote_server.address = "udp://" .. remote_dns_udp_server .. ":" .. server_port - end - - if remote_dns_tcp_server then - remote_server.address = remote_dns_tcp_server - end - - if remote_dns_doh_url and remote_dns_doh_host then - remote_server.address = remote_dns_doh_url - end - - if remote_server.address then - table.insert(dns.servers, remote_server) - end - + local remote_server = {} local fakedns_tag = "remote_fakeip" - if remote_dns_fake then - dns.fakeip = { - enabled = true, - inet4_range = "198.18.0.0/15", - inet6_range = "fc00::/18", - } - - table.insert(dns.servers, { - tag = fakedns_tag, - address = "fakeip", - strategy = remote_strategy, - }) - if not experimental then - experimental = {} - end - experimental.cache_file = { - enabled = true, - store_fakeip = true, - path = api.CACHE_PATH .. "/singbox_" .. flag .. ".db" + 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, + address_resolver = "direct", + detour = default_outTag, + client_subnet = (remote_dns_client_ip and remote_dns_client_ip ~= "") and remote_dns_client_ip or nil, } + + if remote_dns_udp_server then + local server_port = tonumber(remote_dns_port) or 53 + remote_server.address = "udp://" .. remote_dns_udp_server .. ":" .. server_port + end + + if remote_dns_tcp_server then + remote_server.address = remote_dns_tcp_server + end + + if remote_dns_doh_url and remote_dns_doh_host then + remote_server.address = remote_dns_doh_url + end + + if remote_server.address then + table.insert(dns.servers, remote_server) + end + + if remote_dns_fake then + dns.fakeip = { + enabled = true, + inet4_range = "198.18.0.0/15", + inet6_range = "fc00::/18", + } + + table.insert(dns.servers, { + tag = fakedns_tag, + address = "fakeip", + strategy = remote_strategy, + }) + + if not experimental then + experimental = {} + end + experimental.cache_file = { + enabled = true, + store_fakeip = true, + 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,40 +1577,61 @@ 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" + + 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 + direct_dns_server = "udp://" .. direct_dns_udp_server .. ":" .. port + elseif direct_dns_tcp_server then + port = tonumber(direct_dns_port) or 53 + direct_dns_server = "tcp://" .. direct_dns_tcp_server .. ":" .. port + elseif direct_dns_dot_server then + port = tonumber(direct_dns_port) or 853 + if direct_dns_dot_server:find(":") == nil then + direct_dns_server = "tls://" .. direct_dns_dot_server .. ":" .. port + else + direct_dns_server = "tls://[" .. direct_dns_dot_server .. "]:" .. port + end + end + + table.insert(dns.servers, { + tag = "direct", + address = direct_dns_server, + address_strategy = "prefer_ipv6", + 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 - local direct_dns_server, port - if direct_dns_udp_server then - port = tonumber(direct_dns_port) or 53 - direct_dns_server = "udp://" .. direct_dns_udp_server .. ":" .. port - elseif direct_dns_tcp_server then - port = tonumber(direct_dns_port) or 53 - direct_dns_server = "tcp://" .. direct_dns_tcp_server .. ":" .. port - elseif direct_dns_dot_server then - port = tonumber(direct_dns_port) or 853 - if direct_dns_dot_server:find(":") == nil then - direct_dns_server = "tls://" .. direct_dns_dot_server .. ":" .. port - else - direct_dns_server = "tls://[" .. direct_dns_dot_server .. "]:" .. port - end - end - - table.insert(dns.servers, { - tag = "direct", - address = direct_dns_server, - address_strategy = "prefer_ipv6", - strategy = direct_strategy, - detour = "direct", - }) 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" diff --git a/small/sing-box/Makefile b/small/sing-box/Makefile index bce7e74939..981bcf3a69 100644 --- a/small/sing-box/Makefile +++ b/small/sing-box/Makefile @@ -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 diff --git a/small/v2ray-geodata/Makefile b/small/v2ray-geodata/Makefile index 03dbf178fd..7ab4c57157 100644 --- a/small/v2ray-geodata/Makefile +++ b/small/v2ray-geodata/Makefile @@ -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 diff --git a/v2ray-core/core.go b/v2ray-core/core.go index a41aafca9d..418679da60 100644 --- a/v2ray-core/core.go +++ b/v2ray-core/core.go @@ -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." diff --git a/v2ray-core/go.mod b/v2ray-core/go.mod index 930a2fda54..957bb8017f 100644 --- a/v2ray-core/go.mod +++ b/v2ray-core/go.mod @@ -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 ) diff --git a/v2ray-core/go.sum b/v2ray-core/go.sum index 18e123493c..e46a10954b 100644 --- a/v2ray-core/go.sum +++ b/v2ray-core/go.sum @@ -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= diff --git a/v2rayn/v2rayN/Directory.Build.props b/v2rayn/v2rayN/Directory.Build.props index 8dcfcaaa22..d96f5a3720 100644 --- a/v2rayn/v2rayN/Directory.Build.props +++ b/v2rayn/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.11.3 + 7.12.0 diff --git a/v2rayn/v2rayN/Directory.Packages.props b/v2rayn/v2rayN/Directory.Packages.props index 5777d1bb16..d96899fb21 100644 --- a/v2rayn/v2rayN/Directory.Packages.props +++ b/v2rayn/v2rayN/Directory.Packages.props @@ -27,4 +27,4 @@ - \ No newline at end of file + diff --git a/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs index 906e2ba3bd..ad9a4029e7 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -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; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs index 85d90fc3cb..7fd0726267 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs @@ -10,7 +10,6 @@ public class CoreAdminHandler public static CoreAdminHandler Instance => _instance.Value; private Config _config; private Action? _updateFunc; - private const string _tag = "CoreAdminHandler"; private int _linuxSudoPid = -1; public async Task Init(Config config, Action updateFunc) @@ -30,69 +29,60 @@ public class CoreAdminHandler public async Task 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"); + + Process proc = new() { - var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; - var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh"); - - Process proc = new() + StartInfo = new() { - StartInfo = new() - { - FileName = shFilePath, - Arguments = "", - WorkingDirectory = Utils.GetBinConfigPath(), - UseShellExecute = false, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - StandardInputEncoding = Encoding.UTF8, - StandardOutputEncoding = Encoding.UTF8, - StandardErrorEncoding = Encoding.UTF8, - } - }; - - proc.OutputDataReceived += (sender, e) => - { - if (e.Data.IsNotEmpty()) - { - UpdateFunc(false, e.Data + Environment.NewLine); - } - }; - proc.ErrorDataReceived += (sender, e) => - { - if (e.Data.IsNotEmpty()) - { - UpdateFunc(false, e.Data + Environment.NewLine); - } - }; - - proc.Start(); - proc.BeginOutputReadLine(); - proc.BeginErrorReadLine(); - - await Task.Delay(10); - await proc.StandardInput.WriteLineAsync(); - await Task.Delay(10); - await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd); - - await Task.Delay(100); - if (proc is null or { HasExited: true }) - { - throw new Exception(ResUI.FailedToRunCore); + FileName = shFilePath, + Arguments = "", + WorkingDirectory = Utils.GetBinConfigPath(), + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardInputEncoding = Encoding.UTF8, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8, } + }; - _linuxSudoPid = proc.Id; - - return proc; - } - catch (Exception ex) + proc.OutputDataReceived += (sender, e) => { - Logging.SaveLog(_tag, ex); - UpdateFunc(false, ex.Message); - return null; + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + proc.ErrorDataReceived += (sender, e) => + { + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + + proc.Start(); + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(); + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd); + + await Task.Delay(100); + if (proc is null or { HasExited: true }) + { + throw new Exception(ResUI.FailedToRunCore); } + + _linuxSudoPid = proc.Id; + + return proc; } public async Task KillProcessAsLinuxSudo() @@ -102,22 +92,14 @@ public class CoreAdminHandler return; } - try - { - var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}"; - var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh"); + var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}"; + var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh"); - var result = await Cli.Wrap(shFilePath) - .WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd)) - .ExecuteAsync(); + await Cli.Wrap(shFilePath) + .WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd)) + .ExecuteAsync(); - _linuxSudoPid = -1; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - UpdateFunc(false, ex.Message); - } + _linuxSudoPid = -1; } private async Task CreateLinuxShellFile(string cmdLine, string fileName) diff --git a/v2rayn/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/CoreHandler.cs index 979186938f..92c3017147 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -238,66 +238,19 @@ public class CoreHandler return null; } - if (mayNeedSudo - && _config.TunModeItem.EnableTun - && coreInfo.CoreType == ECoreType.sing_box - && Utils.IsNonWindows()) - { - _linuxSudo = true; - await CoreAdminHandler.Instance.Init(_config, _updateFunc); - return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath); - } - try { - Process proc = new() + if (mayNeedSudo + && _config.TunModeItem.EnableTun + && coreInfo.CoreType == ECoreType.sing_box + && Utils.IsNonWindows()) { - StartInfo = new() - { - FileName = fileName, - Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath), - WorkingDirectory = Utils.GetBinConfigPath(), - UseShellExecute = false, - RedirectStandardOutput = displayLog, - RedirectStandardError = displayLog, - CreateNoWindow = true, - StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, - StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, - } - }; - - if (displayLog) - { - proc.OutputDataReceived += (sender, e) => - { - if (e.Data.IsNotEmpty()) - { - UpdateFunc(false, e.Data + Environment.NewLine); - } - }; - proc.ErrorDataReceived += (sender, e) => - { - if (e.Data.IsNotEmpty()) - { - UpdateFunc(false, e.Data + Environment.NewLine); - } - }; - } - proc.Start(); - - if (displayLog) - { - proc.BeginOutputReadLine(); - proc.BeginErrorReadLine(); + _linuxSudo = true; + await CoreAdminHandler.Instance.Init(_config, _updateFunc); + return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath); } - await Task.Delay(500); - AppHandler.Instance.AddProcess(proc.Handle); - if (proc is null or { HasExited: true }) - { - throw new Exception(ResUI.FailedToRunCore); - } - return proc; + return await RunProcessNormal(fileName, coreInfo, configPath, displayLog); } catch (Exception ex) { @@ -307,5 +260,57 @@ public class CoreHandler } } + private async Task RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog) + { + Process proc = new() + { + StartInfo = new() + { + FileName = fileName, + Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath), + WorkingDirectory = Utils.GetBinConfigPath(), + UseShellExecute = false, + RedirectStandardOutput = displayLog, + RedirectStandardError = displayLog, + CreateNoWindow = true, + StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, + StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, + } + }; + + if (displayLog) + { + proc.OutputDataReceived += (sender, e) => + { + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + proc.ErrorDataReceived += (sender, e) => + { + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + } + proc.Start(); + + if (displayLog) + { + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + } + + await Task.Delay(100); + AppHandler.Instance.AddProcess(proc.Handle); + if (proc is null or { HasExited: true }) + { + throw new Exception(ResUI.FailedToRunCore); + } + return proc; + } + #endregion Process } diff --git a/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml b/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml index 871572c1c7..18431152cb 100644 --- a/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml +++ b/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml @@ -18,6 +18,9 @@ + @@ -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}"> diff --git a/v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml b/v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml index d337c4cdba..61c6c10590 100644 --- a/v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml +++ b/v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml @@ -92,7 +92,13 @@ AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}" DisplayMemberPath="Remarks" FontSize="{DynamicResource StdFontSize}" - Style="{StaticResource MaterialDesignFloatingHintComboBox}" /> + Style="{StaticResource MaterialDesignFloatingHintComboBox}"> + + + + @@ -184,7 +190,13 @@ AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}" DisplayMemberPath="Remarks" FontSize="{DynamicResource StdFontSize}" - Style="{StaticResource MaterialDesignFilledComboBox}" /> + Style="{StaticResource MaterialDesignFilledComboBox}"> + + + + @@ -198,7 +210,13 @@ AutomationProperties.Name="{x:Static resx:ResUI.menuServers}" DisplayMemberPath="Text" FontSize="{DynamicResource StdFontSize}" - Style="{StaticResource MaterialDesignFilledComboBox}" /> + Style="{StaticResource MaterialDesignFilledComboBox}"> + + + + diff --git a/v2rayng/V2rayNG/app/src/main/assets/v2ray_config.json b/v2rayng/V2rayNG/app/src/main/assets/v2ray_config.json index 90abcee0fa..4f8c3d7ebd 100644 --- a/v2rayng/V2rayNG/app/src/main/assets/v2ray_config.json +++ b/v2rayng/V2rayNG/app/src/main/assets/v2ray_config.json @@ -97,7 +97,7 @@ } ], "routing": { - "domainStrategy": "IPIfNonMatch", + "domainStrategy": "AsIs", "rules": [] }, "dns": { diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt index 3ba5f0a8f1..2e06f82332 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt @@ -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 -> diff --git a/xray-core/app/dispatcher/default.go b/xray-core/app/dispatcher/default.go index 7bc580565f..34a59fa5f7 100644 --- a/xray-core/app/dispatcher/default.go +++ b/xray-core/app/dispatcher/default.go @@ -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) + b.Clear() + 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 } } } diff --git a/xray-core/app/dispatcher/sniffer.go b/xray-core/app/dispatcher/sniffer.go index c138447dc0..d6acf0d934 100644 --- a/xray-core/app/dispatcher/sniffer.go +++ b/xray-core/app/dispatcher/sniffer.go @@ -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 { diff --git a/xray-core/common/buf/buffer.go b/xray-core/common/buf/buffer.go index 63779586fd..facf6812df 100644 --- a/xray-core/common/buf/buffer.go +++ b/xray-core/common/buf/buffer.go @@ -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,8 +108,13 @@ func (b *Buffer) Release() { b.v = nil b.Clear() - if cap(p) == Size { - pool.Put(p) + 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 diff --git a/xray-core/common/protocol/protocol.go b/xray-core/common/protocol/protocol.go index 28b5e51b3a..61c963c5c6 100644 --- a/xray-core/common/protocol/protocol.go +++ b/xray-core/common/protocol/protocol.go @@ -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") diff --git a/xray-core/common/protocol/quic/sniff.go b/xray-core/common/protocol/quic/sniff.go index 779e291bd3..61171c0166 100644 --- a/xray-core/common/protocol/quic/sniff.go +++ b/xray-core/common/protocol/quic/sniff.go @@ -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,13 +100,15 @@ func SniffQUIC(b []byte) (resultReturn *SniffHeader, errorReturn error) { return nil, errNotQuic } - tokenLen, err := quicvarint.Read(buffer) - if err != nil || tokenLen > uint64(len(b)) { - 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 + } - if _, err = buffer.ReadBytes(int32(tokenLen)); err != nil { - return nil, errNotQuic + if _, err = buffer.ReadBytes(int32(tokenLen)); err != nil { + return nil, errNotQuic + } } packetLen, err := quicvarint.Read(buffer) @@ -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 { diff --git a/xray-core/common/protocol/quic/sniff_test.go b/xray-core/common/protocol/quic/sniff_test.go index cddb4c878b..121279b5d6 100644 --- a/xray-core/common/protocol/quic/sniff_test.go +++ b/xray-core/common/protocol/quic/sniff_test.go @@ -2,9 +2,11 @@ package quic_test import ( "encoding/hex" + "errors" "testing" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol/quic" ) @@ -16,3 +18,252 @@ func TestSniffQUIC(t *testing.T) { t.Error("failed") } } + +func TestSniffQUICComplex(t *testing.T) { + tests := []struct { + name string + hexData string + domain string + wantErr bool + needsMoreData bool + }{ + { + name: "EmptyPacket", + hexData: "0000000000000000000000000000000000000000000000000000000000000000", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "NTP Packet Client", + hexData: "23000000000000000000000000000000000000000000000000000000000000000000000000000000acb84a797d4044c9", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "NTP Packet Server", + hexData: "240106ec000000000000000e47505373ea4dcaef2f4b4c31acb84a797d4044c9eb58b8693dd70c27eb58b8693dd7dde2", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "DNS Packet Client", + hexData: "4500004a8e2d40003f1146392a2a2d03080808081eea00350036a8175ad4010000010000000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000010001", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "DNS Packet Client", + hexData: "4500004a667a40003f116dec2a2a2d030808080866980035003605d9b524010000010000000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000410001", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "DNS Packet Server", + hexData: "b524818000010006000100000675706461746504636f64650c76697375616c73747564696f03636f6d0000410001c00c00050001000000ec00301e7673636f64652d7570646174652d67366763623667676474686b63746439037a303107617a7572656664036e657400c03a000500010000000b002311737461722d617a75726566642d70726f640e747261666669636d616e61676572c065c076000500010000003c002c0473686564086475616c2d6c6f770b732d706172742d3030313706742d3030303908742d6d7365646765c065c0a5000500010000006c001411617a75726566642d742d66622d70726f64c088c0dd000500010000003c0026046475616c0b732d706172742d3030313706742d303030390b66622d742d6d7365646765c065c0fd00050001000000300002c102c1150006000100000030002d036e7331c115066d736e687374096d6963726f736f6674c0257848b78d00000708000003840024ea000000003c", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "DNS Packet Server", + hexData: "5ad4818000010007000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000010001c00c000500010000008400301e7673636f64652d7570646174652d67366763623667676474686b63746439037a303107617a7572656664036e657400c03a000500010000001e002311737461722d617a75726566642d70726f640e747261666669636d616e61676572c065c076000500010000003c002c0473686564086475616c2d6c6f770b732d706172742d3030313706742d3030303908742d6d7365646765c065c0a50005000100000010001411617a75726566642d742d66622d70726f64c088c0dd000500010000003c0026046475616c0b732d706172742d3030313706742d303030390b66622d742d6d7365646765c065c0fd00050001000000100002c102c102000100010000001000040d6bfd2d", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "QUIC, NonHandshake Packet", + hexData: "548439ba3a0cffd27dabe08ebf9e603dd4801781e133b1a0276d29a047c3b8856adcced0067c4b11a08985bf93c05863305bd4b43ee9168cd5fdae0c392ff74ae06ce13e8d97dabec81ee927a844fa840f781edf9deb22f3162bf77009b3f5800c5e45539ac104368e7df8ba", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "QUIC, NonHandshake Packet", + hexData: "53f4144825dab3ba251b83d0089e910210bec1a6507cca92ad9ff539cc21f6c75e3551ca44003d9a", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "QUIC, NonHandshake Packet", + hexData: "528dc5524c03e7517949422cc3f6ffbfff74b2ec30a87654a71a", + domain: "", + wantErr: true, + needsMoreData: false, + }, + { + name: "QUIC Chromebook Handshake[1]; packet 1", + hexData: "cb00000001088ca3be26059ca269000044d088950f316207d551c91c88d791557c440a19184322536d2c900034358c1b3964f2d2935337b8d044d35bf62b4eea9ceaac64121aa634c7cd28630722d169fa0f215b940d47d7996ca56f0d463dbf97a4a1b5818c5297a26fe58f5553dfb513ad589750a61682f229996555c7121c8bf48b06b68ab06427b01af485d832f9894099a20d3baadcff7b1cf07e2c059d3e7ba88d4ad35ef0ffea1fdc6ac3db271dfcca892a41ab25284936225c9bc593ce242b11b8abed4a8df902987eef0c6d90669e3606f47dd6ad05f44ba3a0cd356854261bbb1e2d8f6b83cc57cfa57eda3e5d7181b6ec418f6eeca81c259a33e4b0a913de720f2f8782764766ac9602a7f52a1082ec3da30dbefcf38c781a3e033810c4f2babf9b72adf7164159d98142181492e4468c0e10ab29013bf238e7360e09767ca49d59a9eb18f06a372bad711fefa90295f8e0839b1080570648212b321e5bd6f614bf0d3dc2817628b0c052a32820c16cb7f531c49244c48eb1429625246f9c164ae4ee1e83eaa8ff0eef1acf5a3d8ca88f1e4597db5ba5c0cb23d6100dd53da4f439ae64c4d3d43d1fbb5677f4fdc3bd2c2948dfc7e0be1a33c842033da15529cfd3cae00da68343d835db867f746854804410ba68f0dd7711b0fe55817b83f6ce1a12ad38acf2a3156f819f0dc68ea799c05583d9728f2856577811b260dba40d6c5e82c9e558c5b8f3f4599caf05ea591118e0b80ad621e0a76e4926047593a896752cb168420cb1b02d4211de5e5b7c891f319b5c0cf687e1d261a01f2acbade6bd73cd1ade0a02e240e9351384e1a6868c21a4878f39f0fa94ee1e36c5a46449241a3fe0147ff50176787eca7f3a936c901aeef56770bff74feecb985e6670d20dfd8ed17952dca5a5292213345c61db09bb5bcf5bf74565f61f9dccab51a289c3160ffe4a9b29cc76ea46778d9317a890efea2ad905f4219463a3baca3c02f5c3682634be7c2e86e366272a8263fec8e871644a79299d4aa74f1b1414b2f963cce6e059978faf813625af7869c1dec92035478c0e46dc66d938d4131aca27a59b2103b8cefa8e08aeb44b53b205b932902aea8d519faaaa12e354a6f532b4f716d7929e655dc2e98b494a99153854af5732a2659f2c21e4069896a1835ad05c5e53781cab16599cf4af47c196deeff9115c80d13f93aeb28b08023e6a1d3cf7da2a4457a9e443176bcdfef8f8de630c02bd0efdc5ddda56ad8f6b47edbda6353205e6e655f690092a48deb7f8a5254a7d778e07216cd97dfefcf740c1acd2977ef0fa17f798ea9752bae46e3aa3ec9b13f4c95c20a7839b8409000fa1f17e8dc46cc05c41bff696ee03c0371cae8638e8018ff4ebedd9f27d56443e534a72dd3d18a64790b676ddd060376759fa4a12ffc17f4be83492126ec1dc0fcd4aefef73a0b9c443ec3532b9a66b1a60daacf45e6557115edc0cc4d08758754a44beffedaa0d1265e50beed1a01752904ee3f7e706ed290b1a79071b142105b7c02e692ff318710e3ce9c3b9ec557cdecef173796417341ada414faa06b52adf645db454b56468ccf0da50a942ebc09487797cb45a085ec1e2e06fcd1f5b72eac291955a62e5aa379a374aea3a0dec3e4e0ba1dde350a94c72dbea7505922e26e99d62f751c2b301413a73fb6b20a36052151473ebecd04d0a771ec326957bc28c2020fdf6f01d9abed69b3c3e73168b404a1748b15310b167396da01c7d", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[1]; packet 1 - 2", + hexData: "cb00000001088ca3be26059ca269000044d088950f316207d551c91c88d791557c440a19184322536d2c900034358c1b3964f2d2935337b8d044d35bf62b4eea9ceaac64121aa634c7cd28630722d169fa0f215b940d47d7996ca56f0d463dbf97a4a1b5818c5297a26fe58f5553dfb513ad589750a61682f229996555c7121c8bf48b06b68ab06427b01af485d832f9894099a20d3baadcff7b1cf07e2c059d3e7ba88d4ad35ef0ffea1fdc6ac3db271dfcca892a41ab25284936225c9bc593ce242b11b8abed4a8df902987eef0c6d90669e3606f47dd6ad05f44ba3a0cd356854261bbb1e2d8f6b83cc57cfa57eda3e5d7181b6ec418f6eeca81c259a33e4b0a913de720f2f8782764766ac9602a7f52a1082ec3da30dbefcf38c781a3e033810c4f2babf9b72adf7164159d98142181492e4468c0e10ab29013bf238e7360e09767ca49d59a9eb18f06a372bad711fefa90295f8e0839b1080570648212b321e5bd6f614bf0d3dc2817628b0c052a32820c16cb7f531c49244c48eb1429625246f9c164ae4ee1e83eaa8ff0eef1acf5a3d8ca88f1e4597db5ba5c0cb23d6100dd53da4f439ae64c4d3d43d1fbb5677f4fdc3bd2c2948dfc7e0be1a33c842033da15529cfd3cae00da68343d835db867f746854804410ba68f0dd7711b0fe55817b83f6ce1a12ad38acf2a3156f819f0dc68ea799c05583d9728f2856577811b260dba40d6c5e82c9e558c5b8f3f4599caf05ea591118e0b80ad621e0a76e4926047593a896752cb168420cb1b02d4211de5e5b7c891f319b5c0cf687e1d261a01f2acbade6bd73cd1ade0a02e240e9351384e1a6868c21a4878f39f0fa94ee1e36c5a46449241a3fe0147ff50176787eca7f3a936c901aeef56770bff74feecb985e6670d20dfd8ed17952dca5a5292213345c61db09bb5bcf5bf74565f61f9dccab51a289c3160ffe4a9b29cc76ea46778d9317a890efea2ad905f4219463a3baca3c02f5c3682634be7c2e86e366272a8263fec8e871644a79299d4aa74f1b1414b2f963cce6e059978faf813625af7869c1dec92035478c0e46dc66d938d4131aca27a59b2103b8cefa8e08aeb44b53b205b932902aea8d519faaaa12e354a6f532b4f716d7929e655dc2e98b494a99153854af5732a2659f2c21e4069896a1835ad05c5e53781cab16599cf4af47c196deeff9115c80d13f93aeb28b08023e6a1d3cf7da2a4457a9e443176bcdfef8f8de630c02bd0efdc5ddda56ad8f6b47edbda6353205e6e655f690092a48deb7f8a5254a7d778e07216cd97dfefcf740c1acd2977ef0fa17f798ea9752bae46e3aa3ec9b13f4c95c20a7839b8409000fa1f17e8dc46cc05c41bff696ee03c0371cae8638e8018ff4ebedd9f27d56443e534a72dd3d18a64790b676ddd060376759fa4a12ffc17f4be83492126ec1dc0fcd4aefef73a0b9c443ec3532b9a66b1a60daacf45e6557115edc0cc4d08758754a44beffedaa0d1265e50beed1a01752904ee3f7e706ed290b1a79071b142105b7c02e692ff318710e3ce9c3b9ec557cdecef173796417341ada414faa06b52adf645db454b56468ccf0da50a942ebc09487797cb45a085ec1e2e06fcd1f5b72eac291955a62e5aa379a374aea3a0dec3e4e0ba1dde350a94c72dbea7505922e26e99d62f751c2b301413a73fb6b20a36052151473ebecd04d0a771ec326957bc28c2020fdf6f01d9abed69b3c3e73168b404a1748b15310b167396da01c7dc700000001088ca3be26059ca269000044d00a7e7a252620d0fdfb63c0c193d6a9fe6a36aa9ce1b29dfa5f11f2567850b88384a2cc682eca2e292749365b833e5f7540019cd4f3143ed078aec07990b0d6ece18310403e73e1fe2975a8f9cb05796fa6196faaba3ee12a22b63a28a624cf4f7bedd44de000dc5ea698c65664df995b7d5fade0aab1cf0ecc5afd5ecb8fb80deecae3a8c97c20171f00ac3b5dc9a9027ca9c25571c72bb32070f6e3fb583560b0da6041b72e0a9601b8ad17d3c45e9dcc059f9f4758e8c35a839a9f6f4c501cb64e32e886fc733bc51069fbe4406f04d908285974c387d5b3e5f0f674941d05993bf8bda0d5ffd8c4fb528e150ff4bf37e38bd9c6346816fe360d4a206da81e815c1f7905184b6146b33427c6e38f1179981c18b82a3544442dd997c182d956037ae8f106eaf67ba133e7f15f1550b257d431f01ba0472659c6a5c2e6ff5e4ce9e692f4ef9fb169a75df4eb13f0b20e1994f3f8687bdca300c7e749af7b7a3b6597a6b950fe378a68c77766fdabe95248ed41d37805756b7ffa9cee0898bd661f6657cbf1af9aa8c7e437d432ca854c95307e6a7dfb6504ee3f7852fb3c246d168a03810b6c3d4e3d40bdee3def579effb66563f5bac98cfa1b071cd6f33e425e016bb3514a183b72cb3a393e9e519ba60e2177c98f530835e3b6eab78cdcb8abdbc769bc07e10c8e38bea710d5de1bdb2fa8d0d9b19e8cc31d16725a696e55342c89b667497e3d7f90e48f8503d8ead2a32a1930c3b24a4a9dcf2d8ec781705dd97d7df6e26828712fe42114419d5b8346bd86c239bd02f34e55f71400cb10c1fac7d8efa1a2ab258c17ace4288c8576ab92447b648fd15f4e038ec1c81a135e3bbb6f581a994c6a4902aeb1b5588cb1b5b53c8540296d96b6d2eccd67bae9609233f36304b5186d4698b88bb3ce8b1191a62b990436cf10718fd5759cb2281ac122f49ccbef8a3206348c1a930e7fc4bb498a11d89374e1480c7b8725b5f65e8c8d6f58da17f9134abce77eb9a6fcda514e7d3ab2e3610f86945f0dca519a3844da1b3a4b0e03c80528a2f79be478d07ff26166e30294bf0e69bf07a5bbd6d879adf6d618a1ec8365023408980bf67f0525a2fdee97fccc38fe104d4f58ed15e3671dfedf684856a27fbe286adba40ff0336def93f0174e9e35d341f5de73190d330d72227db9a866b69418e17e8e19ec884c1ffe2f0ad6deec37c9d49d536d0242fab282b0cf86cc9b15341757e0d361bddcbe5cbb062b3148d7c3c62af5c5dd5922a49920f351647030f62ed16929a404aa514fcbc38e67ba4f275e02a04c486b1a8e5b5efda197fd63e6f41fdeffa652c690dd6b00ca65df3688672ead9744f7d631e42e3b42f3ed1bff51b30f89211a7467cde65eab3659af7690cf307420a5823f31999d8f63c6c6ba0296ed4a46d5df6404f8db33e7252cc6bfcf7f55fee1f1e3b0573b6c6615793ff0691b7cfd23c195f66eb333d7efb0cfb74cf159787f87ad01fc131c6763bb1117bbfb8c2e8197ffba6b8c747565b1332bdbd6553b840939c2f98aa8eb1c549491c640e012fc549852fa7a93f81e5db152c761fc7d01bce0325619965c09f6730a162e7be53af7d9ce4b5ac0f4eb487361d2ac231d4ce92e5d9a084bc7b609ccf60056ecc82cd0c06a088cfbcf7d764b3109331c42f989da82b05cfe4c134a6784e664fa67a89c0624e3cc73ccfdea3f292db28f7c7b1b109f680f6b537f135c62f764", + domain: "dns.google", + wantErr: false, + needsMoreData: false, + }, + { + name: "QUIC Chromebook Handshake[2]; packet 1", + hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaa", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[2]; packet 1-2", + hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaac40000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e4489522d29bb5c84749f83c8e1edfd9da8d1738164a8a9c59e37a5c9994d90bb982dcfa69b20f868960dc139618f1adc2546d34340ae13d826260c54a456bbf7469ee37b1be1d7177004468d7e92cac62a0b165d6a114ad479861dd58959e094b5a6250359301d4a614d529660760e3d1cdec9bf444a3761309bab40e4a977bc749e0dae431952f5f7e6b1ebc1383d343359a387da4301f7fa4b400475e9b82367e56278376dd1c80349f083988945a13649008109cc12a3acf569ffcc5481fbcd86b544e7dc8434e9dd42bd8e5716a844d37879568db046857389d36cc7550c75f94e314db6749aa987f0fc730fae0fcf465d01c2fb745269dfc10132ddb5404dda2f9455780f5818730834aa9db4740793359884b9927b0bd1a5ca96052b4f17397d8b78aa891401bb8bed6726ea2229d919798c50e24d5f40576ac204847be9244aadbe5c773684c37475036541d209c177d4e9c22a1253292ce4ffb886b925b6cf83cc251976a68887eda2777590f51804b790b51eee77e717b7ef0eea71634594df36e6ae9e7574d65c51ac3196f0b2a3b0f023c81f05f7807f958dda03418ed49e14b645e814b9aa55b37c809be3172ca21fe4c7a78e17e9ece8def2dd2949310ecaa41b1b477f4e85db5288aa144e333f47ef291d0e822941181c13859d9fd6d640904ee764c9276125228c932dff3fb12f564f039b52f5ba1ab4d119641df8fe13f784802b99347f0046da63f471e34b1d12d3111cffe7b5d90cf5999879f6f23e7785f09cb10df32821bb68dd8fdcfcdbedd63f2428b2292b9f0e76ff36403c9644fd43e01112ee6218d0ec1c86f6d147e4b802293e906750c7046f53bf05a144e321d3b45e08e4064fd3828fdd1b5d1ceed74081f61319dc0ad9a6e8a3b9cc802e952d24e2271712e2c2cda7daca2f835e6c804feeef8d918404cc82a1aa9534bddff68a472b208a0d0a7fd68a08fbc411132af47a6b67a32617b7b9991524c21599e8e3cb9395cdab87a3f5bf5d1833a9c7ea021b29cf428c877c6b21d62f99340ac7f85ae721acc10968e7d79f111ca40c75e14060d07cffa046d71151a0b00eab657300344b04bd1a8871650c34ceda8610d7c1ba8d37673da6aaa580400e0230c69fba8ba21927de2f5897656144694550d1df3d268804adc707e7b236501734aeabb2e61cb08012bd96eca5a486d7a55f996992c36233815abd71c30e263ba0c5d9456fe0828df16f6af7929390bb143c426d9dfeaf4bb373554479ebe609b36b4bc3dd08ce216b9cdc5726edb458c5e4036d0edc688d3e39d20f8254b5d1f174518f15b344efc27fc56572c0159aa593d5b46bc33818f986e3df8caebd4c7b702ef50dd582714a2b94ecd1c4e90af37d388445c478a32ff6e8f5852ee115966b708eed04da322b98813a69423e95f90b89ce85518e39bcef36fdd5bd312b2c6c5ee85962675274c18f39ee35155517f70fd74b31bb2de6b5108d369252e6fb289e453833132ef7960da1cc0934790c039b9a1b0c74f23eb3b61fe9b4d0ea67de757b93af451eef303b1373199af446a0fa98d5991bbd4771ee63317e6da86efbe213dfff595c41b98e0e89f4f2df110104e760feebf4cb3361171c9fceb1e1c809a268", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[2]; packet 1-3", + hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaac40000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e4489522d29bb5c84749f83c8e1edfd9da8d1738164a8a9c59e37a5c9994d90bb982dcfa69b20f868960dc139618f1adc2546d34340ae13d826260c54a456bbf7469ee37b1be1d7177004468d7e92cac62a0b165d6a114ad479861dd58959e094b5a6250359301d4a614d529660760e3d1cdec9bf444a3761309bab40e4a977bc749e0dae431952f5f7e6b1ebc1383d343359a387da4301f7fa4b400475e9b82367e56278376dd1c80349f083988945a13649008109cc12a3acf569ffcc5481fbcd86b544e7dc8434e9dd42bd8e5716a844d37879568db046857389d36cc7550c75f94e314db6749aa987f0fc730fae0fcf465d01c2fb745269dfc10132ddb5404dda2f9455780f5818730834aa9db4740793359884b9927b0bd1a5ca96052b4f17397d8b78aa891401bb8bed6726ea2229d919798c50e24d5f40576ac204847be9244aadbe5c773684c37475036541d209c177d4e9c22a1253292ce4ffb886b925b6cf83cc251976a68887eda2777590f51804b790b51eee77e717b7ef0eea71634594df36e6ae9e7574d65c51ac3196f0b2a3b0f023c81f05f7807f958dda03418ed49e14b645e814b9aa55b37c809be3172ca21fe4c7a78e17e9ece8def2dd2949310ecaa41b1b477f4e85db5288aa144e333f47ef291d0e822941181c13859d9fd6d640904ee764c9276125228c932dff3fb12f564f039b52f5ba1ab4d119641df8fe13f784802b99347f0046da63f471e34b1d12d3111cffe7b5d90cf5999879f6f23e7785f09cb10df32821bb68dd8fdcfcdbedd63f2428b2292b9f0e76ff36403c9644fd43e01112ee6218d0ec1c86f6d147e4b802293e906750c7046f53bf05a144e321d3b45e08e4064fd3828fdd1b5d1ceed74081f61319dc0ad9a6e8a3b9cc802e952d24e2271712e2c2cda7daca2f835e6c804feeef8d918404cc82a1aa9534bddff68a472b208a0d0a7fd68a08fbc411132af47a6b67a32617b7b9991524c21599e8e3cb9395cdab87a3f5bf5d1833a9c7ea021b29cf428c877c6b21d62f99340ac7f85ae721acc10968e7d79f111ca40c75e14060d07cffa046d71151a0b00eab657300344b04bd1a8871650c34ceda8610d7c1ba8d37673da6aaa580400e0230c69fba8ba21927de2f5897656144694550d1df3d268804adc707e7b236501734aeabb2e61cb08012bd96eca5a486d7a55f996992c36233815abd71c30e263ba0c5d9456fe0828df16f6af7929390bb143c426d9dfeaf4bb373554479ebe609b36b4bc3dd08ce216b9cdc5726edb458c5e4036d0edc688d3e39d20f8254b5d1f174518f15b344efc27fc56572c0159aa593d5b46bc33818f986e3df8caebd4c7b702ef50dd582714a2b94ecd1c4e90af37d388445c478a32ff6e8f5852ee115966b708eed04da322b98813a69423e95f90b89ce85518e39bcef36fdd5bd312b2c6c5ee85962675274c18f39ee35155517f70fd74b31bb2de6b5108d369252e6fb289e453833132ef7960da1cc0934790c039b9a1b0c74f23eb3b61fe9b4d0ea67de757b93af451eef303b1373199af446a0fa98d5991bbd4771ee63317e6da86efbe213dfff595c41b98e0e89f4f2df110104e760feebf4cb3361171c9fceb1e1c809a268c60000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e44892ff5e6b16d8a259a9128c2c0c3c525462781a344c3df7f19a747e0e79ca8714995c867fc697a3cb87b35e769465a8e966bcb35b7e897ad036aa23a6c021e2445a0eb79962151cd20dbb43ae1231847de01caf4e5589dfebf026e95f7d1d742e140d9dda849396a70cc0798f1eef06fd5f4cfbc9a190ddf04cc332c5b7b15e53af311190ced92a1291c12b8799f2b50e076539a8370ee667e1791a78f38e565a48acbaa1c78ba941dba8b0d040f8fb8bbcc9f6bf5705efa613a24b12d6ac9cebb4f3fac1b09a07b49d8a3a62808eb0a324629f13a012e6ad0feb11ad97c1572983c713b62f27584809ba43e64e4af9845af807c0783104838f4e2ac33fa848866f3cc64a7b6203a5c09e8ad231f0f06ae2fb7b39a64cedd823b0ff297ad9be1ccac436777ccb3e22ef6b9c12e6d5e34926f50e8ca8c8c0532c810b074d001c11791a01bf25786b57a5da54065dcee4962822e929f47ee44d3b8c83d45a8b7a936dc2a6fa396e4194fa032d1627eca59f69857fc40dab5835d3613dade1c74b09c345bd32c509e9545d2330b157a7acb76409f3ac8eaa22802414f38c5422fe4c5189caaf5c1b93ce7c0892f0cfc477490d335aa78961d632a973cf106bd974c2714176fb0f98cf12f2887a0d7bd491756dd374331eb3e6adb9f2bd0d6b273403fd14b314eb27ebbb6f6e78ce310437004b757c048149cf04429ae4a6d6e65c9b3e0b9c9c4d4ef52007eaaad9670320f10cd5317b3d3edc374d45c98b217dd28fb3c2c2fb6e74a3aced143e3242084b192ba6df24e69fdb883e850714fe27a45f43883486a986574fd1fc10f259fe90786441554514c8dade1f3b86fdaf5f54ab655e2d803c98aa56073b00c32148a1ed367dff3a2bd934ecba55141389990b661bbd9ce1ef1def13747d45500daf92cec9e60908274703e761cd46affd46622f2a2192a79425ebf51c875fc7ba3598e15e0ba2465fc3e87c8a5da1915d3b8abe4b16d21259f311183eee1e7d2b808a91a7c89b284df0eb6a2a79c610bbe47722b3e04d5a6c0e574816a94d97349b6976010eb8c7debf42210982f78de482b7cd068051bf57908dbf46b5ceaf64f5fb33ede4412c1ce81eb1dfb4e99e10dd9b57ebc6e62ecbf4ee2db04d9e48c62bd45f8fa51704d414296a2d51d25ced6a192034a44c67e09d8985b573f98e03fa36dd8dcce2c04b4d5b1f276b6a642aadbdcafcf09de1d234bf8bbbf64aeadf01519ddafb419b3e62d204e04c3d7ebaf54b09e387ac3e9c4781c11625a2f44fddb7a1886f21929bd01c283f64903b6ccbb463984dfadc00f6af2a421517da023fd319f528195cac5fe907624b70c0172479d07d78e266dbf20ab8fc302228f279ffdba7395a839c4a9d7a4e001a260e1702393968f1e9722f023b204cf09cfee9a7bba045e4a2a449ee9fbb5c36e93028cfc87a2e34914b1b4f01beeded175ac0fa73fea9292f2bc3b1247164d8e05cddc3981bdc24e5f596571c418f6fa00fd9d4d0898cbf0d2f5413bed5f100f1854903017b6bc88bd7e303b5e0e2417bbcc984731128eda550d31f9af0e6e743eb6916466bbd435617d56fa60b05cd7dca66a9f6f4be23d3c5ff5d900822c6d1d8d71b0bab24f57d9682381a87c", + domain: "signaler-pa.clients6.google.com", + wantErr: false, + needsMoreData: false, + }, + { + name: "QUIC Chromebook Handshake[3]; packet 1", + hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948f", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[3]; packet 1-2", + hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948fc90000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd448983eee52163a177650f57b2cd8404bc619b9b59e796f9808bcd549ae6ae30d448c90f2783978bf9314a8038f45c0da5983163bd26f38f559f59447e8cf004f93b6b5c8af7b09603db021d4bdfa641bd83926eae1709a7a427add14df90cb258c6d4663d4d29709da89c90613d2ff9334637d53ca89407804eb863f78e110b866af2734c980705d9f969730a41132e788fc9e426d0f68ed24157aaff0383438d2715262e9b8b03cff850ba88127a05a8b68ac9a5ae5b098bb9ba5eaadd71ae846b3c0f68db728361eb8c8ed899c77725afbdfabf93812c49cbf4ee64047a96ea71258dbe5be3f988029d005fa2d9fc6e1e53fbeb6888074521b972e2ac71b4f22b754fc743e0de21af1e2ab416b2481e03227a1c7d7ea6cac5bc37ee3597d3bf11bf13a688dfa3d9aeff1eb1a7fdfcf8b6c722a4853f7c2b2d31e0b2b691f4273d4793fbb7a00f27a25577bfcba95e60699c9d2a926e71d64f535b633f2fd03320b28fe86c6619c54b34e6caf8f5a71b8a144c9236bf07edaacb486ff8ac63173af099efe7c9d006a5bb756449fb32b1fbff2e315fd5e96b586bb922a9795e29ffe6ead037c556e1bf30e24afb344cf873201007096b6f687f157588e236b71ade4d9245d8f065f2e23b36fad798d0f5504ddf25b828698d0cbdc28478b20d692d2ab605797a67232b0795927d886de798f00b4e7c69517d62b748e62e01d53dd1e77ac9a1605c0408713ff309ad53ff8f2bef17f9074f01134374068bf1f5dc07125180b5ea6902ec2d55c7d6d5f7ed4ef8732f9d34b4627678611fc9579e4321cea012c4e457dee6a11c41bdd1eb965056e885757af389079a558434eb3d59ae56a232302759431172ecc88de1c5400265f0f47e21396e3c38e0ba022c3e55ee4b85527cf49dece94445adc740cd26c18004a1cb984cc1732a138844da1ab003f89c589b6f3cc10c99a1b0d87be763f83e1b12c6fa6938ebc55d2ba33c25ca816dde207f7186f0c70b56b33feb538eb31175fdcfb036e365087f1b630628affdbbdee20d1976cb009f32db5f35aadb8117aa02ff2da9bdeaffbcc8bf3412efefeb00365e5f1ea577afd6e1c3585c67ffe1fa120382aa54028dcae9bbd624432a6256687d05483f2611f1ddd14b40f66fdf547e7eba904a79bd27733c9a8fbfb01154dda3457c4eacff8116941777ec570ff040e217d648ea5076588a6417462481eba68ebc59af04ba49b92f70b68a007977fde48b94b0af35475ea19cbec92df6449b065880bf03452cb3b3582f3d1a010e585be6506f3e067226471a94ce46c515f20502b3866553c10f037d9be89ad5858d6b2d2d94c70159247f66958d0e841d1c5b4254809d52475fdf96d087c3c6647b86006147a9ebb3f52ea6f4b89d886725b9e9243efd95e434bd8dd785143c57c06863b68df8f832987eb0c730c8b96634c1f888da2ef420cb0ebacf81f4b25c65962ae40c09ac4b0b2d440e3bdaa7309d87a1fa6af1c2e13e7a63c253fae027ceb2067cef8421b62d205f5d37c7204eaf594b1b43f9d9b67509a6709df48769ab9e1078f9e59d7656ec2132b5ebccf297e757a052835fffe94ae073131ac49c4f4374a1904cd4bf3041b236b73ea19eaa583db577fe35", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[3]; packet 1-3", + hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948fc90000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd448983eee52163a177650f57b2cd8404bc619b9b59e796f9808bcd549ae6ae30d448c90f2783978bf9314a8038f45c0da5983163bd26f38f559f59447e8cf004f93b6b5c8af7b09603db021d4bdfa641bd83926eae1709a7a427add14df90cb258c6d4663d4d29709da89c90613d2ff9334637d53ca89407804eb863f78e110b866af2734c980705d9f969730a41132e788fc9e426d0f68ed24157aaff0383438d2715262e9b8b03cff850ba88127a05a8b68ac9a5ae5b098bb9ba5eaadd71ae846b3c0f68db728361eb8c8ed899c77725afbdfabf93812c49cbf4ee64047a96ea71258dbe5be3f988029d005fa2d9fc6e1e53fbeb6888074521b972e2ac71b4f22b754fc743e0de21af1e2ab416b2481e03227a1c7d7ea6cac5bc37ee3597d3bf11bf13a688dfa3d9aeff1eb1a7fdfcf8b6c722a4853f7c2b2d31e0b2b691f4273d4793fbb7a00f27a25577bfcba95e60699c9d2a926e71d64f535b633f2fd03320b28fe86c6619c54b34e6caf8f5a71b8a144c9236bf07edaacb486ff8ac63173af099efe7c9d006a5bb756449fb32b1fbff2e315fd5e96b586bb922a9795e29ffe6ead037c556e1bf30e24afb344cf873201007096b6f687f157588e236b71ade4d9245d8f065f2e23b36fad798d0f5504ddf25b828698d0cbdc28478b20d692d2ab605797a67232b0795927d886de798f00b4e7c69517d62b748e62e01d53dd1e77ac9a1605c0408713ff309ad53ff8f2bef17f9074f01134374068bf1f5dc07125180b5ea6902ec2d55c7d6d5f7ed4ef8732f9d34b4627678611fc9579e4321cea012c4e457dee6a11c41bdd1eb965056e885757af389079a558434eb3d59ae56a232302759431172ecc88de1c5400265f0f47e21396e3c38e0ba022c3e55ee4b85527cf49dece94445adc740cd26c18004a1cb984cc1732a138844da1ab003f89c589b6f3cc10c99a1b0d87be763f83e1b12c6fa6938ebc55d2ba33c25ca816dde207f7186f0c70b56b33feb538eb31175fdcfb036e365087f1b630628affdbbdee20d1976cb009f32db5f35aadb8117aa02ff2da9bdeaffbcc8bf3412efefeb00365e5f1ea577afd6e1c3585c67ffe1fa120382aa54028dcae9bbd624432a6256687d05483f2611f1ddd14b40f66fdf547e7eba904a79bd27733c9a8fbfb01154dda3457c4eacff8116941777ec570ff040e217d648ea5076588a6417462481eba68ebc59af04ba49b92f70b68a007977fde48b94b0af35475ea19cbec92df6449b065880bf03452cb3b3582f3d1a010e585be6506f3e067226471a94ce46c515f20502b3866553c10f037d9be89ad5858d6b2d2d94c70159247f66958d0e841d1c5b4254809d52475fdf96d087c3c6647b86006147a9ebb3f52ea6f4b89d886725b9e9243efd95e434bd8dd785143c57c06863b68df8f832987eb0c730c8b96634c1f888da2ef420cb0ebacf81f4b25c65962ae40c09ac4b0b2d440e3bdaa7309d87a1fa6af1c2e13e7a63c253fae027ceb2067cef8421b62d205f5d37c7204eaf594b1b43f9d9b67509a6709df48769ab9e1078f9e59d7656ec2132b5ebccf297e757a052835fffe94ae073131ac49c4f4374a1904cd4bf3041b236b73ea19eaa583db577fe35ca0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489ea77dbb530c7ba127c66c3d7bbc00c336fd4e09e1775c646dffaa8696f7b8b00bf91261fc5164d57a4b9652b7cff4e301d32224b4e48cbfca535b2070ac46181615358d87e244ba6e369f6719bd5a551ac05dc78c222fd0969d0d943cbfaa3570ec25ab2768e9679d1cd1a3528659d010c409a0719526c44e4d9915dc5b0618ebc9e35f06b31bfd8e01fad99dabe32f6bfa00b3a5db5a01920d6685c34efb958729ffc5acfe46b3605715149b65b2f638007885a0866bbdde6765992b9acce2f527de906443f8643845489f1224fd3bbbb3fa78ca4848fe0167ec7cff8a05a17eb7c7a05a80c3106647e5d9aae350f33d10f3a60ab1c705858323a8f610d98cc68ef3cea66eedbb788b9a3da873bfd44ed632aa952ed7bb2004f4502260cef0596ec6e82e7683bdd2cb1f63b01b3f928ffa86b89cbeee922f1fd192fea0bdd17cf62d14f06f9e27bf5cafec90ab26f103e1dcb96ae4335e444b1fbad294cc395c5dc3a1c0c1fb4078d362eb229c42bc42ff53115c51f137d75596b3e6d3d28974720d6935430054c6b630bade51ad508d31fdfd572bd37f70e3dc06021d2b0ccf91a7975aa501e152d62980f02ce0ee94b547a2fbede47cf1f5c0a541ccc8992dd006f77437ce6a6b1f4f91833914a1cc51acf9336a620c4a22073966cde3ecac3224941dec004e741e05c11b43796dc531ce33e7c9a4fa68fa689880842e37a3a04fb75f3fcee86813388df74d443d1c35d7adea290effa98309b22ceca9bc252ccb4c443733db691adf0af559a5b7565043f84e91c5ed9f79ebf49f0bd60b68a7b8730032574e8e21548204c75321a374bbbf822efb1281ddf32feeced4bfe22bcf7c1a309954c1e356175a8a1a1a074a22f4561acd872d813c88ea9f0f22ac8d7b7b2088bc8565e1c56dfbc84f57aa38c2600ee20e8736076a91ee73f3137e8da3fe3871587b8bdb0a08af40babbe5493f036b45eea837dc15f761d9475d27a512a2d9dfc1ccdb81e2b581f91a5d7fe67cf6955427315a4e9c158806e651e4acff40051cd8a44b0108876c82f7b4d69033bfd8216234de545bf8fb58e489bc74d366db5e48711ba7f317dcdd1708ed5de97468a6026e15bc68ab11efc90f5465b4466bf384a8cc95f9c7fff91d776cccceeae5badebc31c3516c93a7b4682212e5a4902a9fd0327234749d83c141db2eee9688a76f4361f8b6213c88ebde69ebb84488c9ab8f42737da123eaf39373ac687df65f817939296f5477f92ad3fda0effbdd5d0594eb59d80265eef6cfcaf81b386c9d03c205c1b6714bf31be15e8f871b4791aec10884938285d6b8c18a0dfe750b753de88a2d2d855b9d1a0068ac4d2ce3a259bfdd30414380bf8b287abf2a28de442552f1d70a0aeb0867d9c7ed4e9717565ebc6aca21d85d2845faacfd8fadb1a76d9a2cd619413e631666009085bc7dad7492654ec20431e37ddd55588d2cd4d256021547cac768dfe3f7dcc9a18f0c72a743de799e98398b9bb2f216aea727d240b2f52ee269f4df4b8d7bdb439f074e3ef179ae2ae44daba64864fe427574b659a5e79defaf43e45e1357e1ff48e28ba6384c559cd036c9229151f917865b5575cda11e1ee0690bfbdb628b9a17e9c37190102", + domain: "signaler-pa.clients6.google.com", + wantErr: false, + needsMoreData: false, + }, + { + name: "QUIC Chromebook Handshake[4]; packet 1", + hexData: "cc0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944894c6153ddd936992f160ae349757507f79fd0485766987d986518d9f19270a0021c52bff14e594c074f3c5664cf3de3b761cd36c16acf68565aaaeebf196da581533f19a464b22404b13b46ae3e4819bd4a7a85db6ddf379bb84f8dffcbb412c9ce405d8b4c98d303cb13df2e80a88ca0f09e2e2489c8d0b6d5ba9262b85869f8f989e9b82c4a270592894fda96bd27ce03e0cb4d4a4e130d9655e6da02f4348c949bc9b2aa609fdb4b9a0a3e45be25b18fbde569bb996d6419e98f2d9b7a0ca63f52f054b50771dc7c580596d86b4a94642be9a9b78a01ad2deae4607d8e0e25641aa8ed46b20fe027f4b9f77d1736aa0fec4f837cf5d878b3dcefdf3c6e82eb943dd022e98d623403950e6ea3addc0f93f92a422ed686b7beee437a4040f4a440dfd071d8c09f1344c28545b4488765a455e33e13d17434b9642dedadcb6a13ec35d51c3e7a03ae9a76cca6b1cba8312b7d8bae703e0a378300a17b7483d07893ddd941dd3e545a66faa0fabb4dc967c807665ebf4562fede176719d1a126f228acc0902e7235972a6db4eedd6547c38705629ee2574c5d2dd6c76c5c82e741f33291506e66a8df65ef6d1e7e6628fe4a4f5e141d482fc5f9d26609e64da8061eef5c0fca421d5334b199ca8270612074a1f9fbaad8b98ff7b81f8871f4ada6976f254e47c51e03d4c628beb3471ba375642ae0b41d65dec1419cd31f20ec779f5666717c1e7b4240fdcfa46a774234961083e1915e938ae0d41a66868b91949d856065c4e6813e0cbd9680a916b5eb78655dea9ad0f9c0ad0f5c244a72fcb8b589321519e34f0e6e25e6bb43abfa84a7241bc02555fa9060b9ef55f1e3a9dc3a575e16b23a36aabbd3ddeaf8f2516224179a4039a891e7f29631c2a08745bb184c66ffe98bc960e6c08a14524ae34444433591cdf7adc14419310b74594305f67c3087a2c21733e6e0be748e7af6fb6717946c313fbc0935ad3559e2d6323979cdc3bd48753b5a438605e15832efb8a0c4144060f41ed27a82dd067f2caaea3830abc97d9e080b3fd762aecbd58e8b2b17dae553dacdf3ee44198d2f19c0522b6a1b17923a210cf24902c5590afe808fd22e54e586399665d588a7febd0b402a4e6283679e1f95a2d4d7d3945e2bb8f44225ad8aa07cd07d3323ce94f39ae4c9466c05ceeb0a30981cea022d1bcab8a4b0c8e42e08211ee727728c74d7945f2350a149eb9cb7eb3a280954b64e612b53b19016a4c07427945345ffb86982c113ed797172ded4428d6b95b9ce64b48e98ab96421a179983c4a74b986f3f52d9a2d7fac8ea0955835d241bf4817a42950e2b298e51de20c026df81fdb0d28c68841bf62dfcfb4684def62c13ceddce9a25b446043056006ec8582aea14eee602eb2963f575dedfd2313d7d561c6ceac0d08c94645a222b25b7493542fee52c316f06f583612ab2ec3d420a01a61fe80b099386c2fe647292769d4571239592fe7e27f4324456ef894643f72ac450628cdcc9f376607b85f369a092c64d7d5a0559193e29cbc48e9ed77fa3fa05776d6169fbdbafa507db1e1d33a4550003a3a1a794b266e886f483eba76a8629d17d9596574068ffce61a52b209c21c77f5e7337e5541755c9f1c6b4", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[4]; packet 1-2", + hexData: "cc0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944894c6153ddd936992f160ae349757507f79fd0485766987d986518d9f19270a0021c52bff14e594c074f3c5664cf3de3b761cd36c16acf68565aaaeebf196da581533f19a464b22404b13b46ae3e4819bd4a7a85db6ddf379bb84f8dffcbb412c9ce405d8b4c98d303cb13df2e80a88ca0f09e2e2489c8d0b6d5ba9262b85869f8f989e9b82c4a270592894fda96bd27ce03e0cb4d4a4e130d9655e6da02f4348c949bc9b2aa609fdb4b9a0a3e45be25b18fbde569bb996d6419e98f2d9b7a0ca63f52f054b50771dc7c580596d86b4a94642be9a9b78a01ad2deae4607d8e0e25641aa8ed46b20fe027f4b9f77d1736aa0fec4f837cf5d878b3dcefdf3c6e82eb943dd022e98d623403950e6ea3addc0f93f92a422ed686b7beee437a4040f4a440dfd071d8c09f1344c28545b4488765a455e33e13d17434b9642dedadcb6a13ec35d51c3e7a03ae9a76cca6b1cba8312b7d8bae703e0a378300a17b7483d07893ddd941dd3e545a66faa0fabb4dc967c807665ebf4562fede176719d1a126f228acc0902e7235972a6db4eedd6547c38705629ee2574c5d2dd6c76c5c82e741f33291506e66a8df65ef6d1e7e6628fe4a4f5e141d482fc5f9d26609e64da8061eef5c0fca421d5334b199ca8270612074a1f9fbaad8b98ff7b81f8871f4ada6976f254e47c51e03d4c628beb3471ba375642ae0b41d65dec1419cd31f20ec779f5666717c1e7b4240fdcfa46a774234961083e1915e938ae0d41a66868b91949d856065c4e6813e0cbd9680a916b5eb78655dea9ad0f9c0ad0f5c244a72fcb8b589321519e34f0e6e25e6bb43abfa84a7241bc02555fa9060b9ef55f1e3a9dc3a575e16b23a36aabbd3ddeaf8f2516224179a4039a891e7f29631c2a08745bb184c66ffe98bc960e6c08a14524ae34444433591cdf7adc14419310b74594305f67c3087a2c21733e6e0be748e7af6fb6717946c313fbc0935ad3559e2d6323979cdc3bd48753b5a438605e15832efb8a0c4144060f41ed27a82dd067f2caaea3830abc97d9e080b3fd762aecbd58e8b2b17dae553dacdf3ee44198d2f19c0522b6a1b17923a210cf24902c5590afe808fd22e54e586399665d588a7febd0b402a4e6283679e1f95a2d4d7d3945e2bb8f44225ad8aa07cd07d3323ce94f39ae4c9466c05ceeb0a30981cea022d1bcab8a4b0c8e42e08211ee727728c74d7945f2350a149eb9cb7eb3a280954b64e612b53b19016a4c07427945345ffb86982c113ed797172ded4428d6b95b9ce64b48e98ab96421a179983c4a74b986f3f52d9a2d7fac8ea0955835d241bf4817a42950e2b298e51de20c026df81fdb0d28c68841bf62dfcfb4684def62c13ceddce9a25b446043056006ec8582aea14eee602eb2963f575dedfd2313d7d561c6ceac0d08c94645a222b25b7493542fee52c316f06f583612ab2ec3d420a01a61fe80b099386c2fe647292769d4571239592fe7e27f4324456ef894643f72ac450628cdcc9f376607b85f369a092c64d7d5a0559193e29cbc48e9ed77fa3fa05776d6169fbdbafa507db1e1d33a4550003a3a1a794b266e886f483eba76a8629d17d9596574068ffce61a52b209c21c77f5e7337e5541755c9f1c6b4cd0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944892d4ba45357f2ca515d03b90820bb91c531a4be27266fda6022856da650cfb9c34139e8a3180e93cb73a6864471f849bdfa26c03e30c0e4d00309207cd46fb48887f60d7c51b208c247d1b311b35da70dd682cb1f7ae6a64215e5fefe25249daf308083837a3898e6052ebcf6cef3cb8e987ee1eb5ea797642d76391ae363b8eb2409d7486dd4a67c9e9b755376ca61009cb853835850e4fc1844f8e9eebca73e89317003482f70c4795ce9e2724c6d62172e010233e7bf203dde6eea9976f29896df562e8640a4ed88b5b3dff50296d0db43885f162c588d72de357c2ee049d9532642576de64d4e13cce77208e0aa9cf9838166f3375a968a5a6a01cf066ea0ca27fe4471cf0bf7eb36227867928076985588d05692d3f81d9a1158d150b2701399ee0a32693aaac43c27b76c657343b2a307e7018c2e9fe6317ec09f9afb762075430140b15016ae44acffc7467f4b1cec619942e916047c2db27f89742e53856d8c7c098beaba710340674a3f8455ab38fa2a4156fa3a45dffca1e20ec86ae792988dcb52bbf2ac97ea878e80511d3e4e70ece4b2816401ad450b9d13a1fecd7a5a363dd120285a972d52c06b632362cb8f897f799fb8342850b6670eb5083347347bd48b559f118839aa627598379963ecf18c2a900399ed936ab77ebdf95bdc5eaa75a903005e38dd99362b3d99f07ee2aea1a0ada77ec5ba76a7da2a80b672f4709bd32fad36787e37467e75fe594a24b402a6fa7e858c2abe9cffd9e885cc7091c035e4354779fc113bc084b5f6fcbd7bc618e9cc15205538cf781c96b658565cd8e39d95001d085d52f87a2970eb8f72149f4061d629ca0a928442b586aef9105326e13c015ce2b987c442b574180550302c48c2cf763b45492e25adee42d23bb2608e284caea4b3a28b77a20768dee6c0002b16e5d714eca22ca0c58195e03951b9564079ffa61c81afb2c5783ea2b8c0605d3994cfaed3c33af2279469c771269174fc5879b67617f571eb376161241ca5a89332074635413661b7fc5f925d86afb56b296dedce33d2b5011fc3b85cfb769c4a1cf5d6217ba3db367ead2159a310cd398b31df83f48f722a1df6c4b6604b2e288aa57cbc9afcf42764ccbc718a3ca42895b8e8287d632d2dd47d933a7472fd7be596c4241220917e636a44a139ec16600d74104fa6055a77c34bdbe14fb1ee56b4084d1d2dc1d56f8d636a8393385fcb4916670eaf8553b2126b12c8f187f58f00ba9bdbe8f06662688b9b8c7327b2d2c2f1443c8b87930d0948c3db62c8becb5b38cc7e484be184400f8c6293568abf1714e2832ad2c0aeb88dde64686fa7b4cd8c452b7365e70221e62dd971db057d4f8eed86e6802bbe8f56f75a8fe65d8216c81265e514042dca73f20a50373fb32e9bd62b741021337a3200b5f0a1b1329e99c57c75a60850f6c2f82cbeb9001edb54d985b7d5cd5957b31f79ee77dd8e1ce5e70d50806273885886b93a12c3dc0f211383d180a475d65bb54b0c5f761f456bfc7d045acd2c7ac648f3bf27bdb52218be48cdef7aa1ddc93e3ce11067ef4819796a4e2a30459c879841789b358b9430368b0910d6e6eaa14894f36b12fe9dd2bd1a20ddb6b12ab8fcec7d0629c9a7", + domain: "play.google.com", + wantErr: false, + needsMoreData: false, + }, + { + name: "QUIC Chromebook Handshake[5]; packet 1", + hexData: "c200000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac824704489c14d28b48268df096f2db0c040c0ffdfb1817c4e1dd32005e87a74104515069ccd1b12f6fa3f657891bca8f0d27f4bb42b23c8310897ac09241f511888ac2e431e3ba8d9114b3f8bb1470324c8a0815dd1589686d058d03fa2669ad7367dc8877abce4700ae999112eb7cba755b20b7db66918021a36a66c60ecaaeb3e0859e3f50d14c1f8c3f56966043f437bfc16098deeb02e2236cc42d2f2802f4082bec30afc347a75f78c5e7337c52502e8e8d945bb4d792ef69419e57ee4e8f3465de4d0a49955787dff0756f1bcd98268919c112ae5e87ec333d590988e111724627143bf5a0aa69c77ee322b1240a65718e0033c4b70c6a5dc2d3248aef71598d73effa0eb397b79279ce846d9c2556fca28595eb3b196f40f052f0127711abbbc334571b8de67f7d39bd9437f9196bfe0650c4972462473776eccae42418b16edd5f45414e1a5e13300ae6705a49ef68e1748e876ce04fe611c973f94fcbe18b0c1a0ae506ce6eac268021e9744899c33ffe44dabdf1ea5413c20ab1013326255ae6df2d6861a7e914001e231ce4065e0d89fb439afb928d660cea26f99eafe65c9f89c5639f3ba8ffdcdf7a777d7859ca948f123a5c4f1491e22803754373ab0f1b605ac09ca6018c6acca7f52aad786ce96d9ae28d09a77dd88d77f23e2e33475973a23dea914fb049662800cfbed416b61835bd3bc22dace817b3b271c816ccf9603de2209067def5b8747112c49659cf6c5281402bed38a4176c0331e0841592c503d342188ac97266866a102864f76b0ff2adf781e0af84ae725fb6db418545dbb70adde5d4ea6d87f8c9b64b579d4f969830056dc0007cdad648035ccdf2c24264905422dfc1a85ac7f519f475351e046a27268312d4ab6a66ea55be01226fd5dfce06f804cafe85ad997946db3d029ef28ebdd36103c8fc05b522c1d4debe034523595cef576c771f0848a221d76c63dd044ea61a3adcbe5d4f0ebcd0bb631f4db1d88a609a1c2a6cc3de29ccd8a40e8a770d806abdae300cc178a32b4ba979652d0b8849dff252571f0b09935744c5b33628a190ae2eb43543ed8dee8539fd464abfcd3cf826aa18c4e84cd11975c5e3bfd0827f7cd211a2084c8626ed4e32bb9877f4801dabe695132b835e335563cb2f4c3e9377cac57f7766a10620f1c57c9f485d66918613daf8b257035ec91481d0076899c45abdbec38b63745428dee481c1cba81b0ab6e7efd6b0c431e018b6503cb13d4df18dbbff195bad1063d59ca5066e0733d3f499109111d22304066e7656e755518ceb6d862095ae54e532fad82f6c21c0c4d776dfecc516b00fa59e117e79481d3fd386c9b0c4d6fa4ec295a5b5b67207c3db20a206e0e31dad2c8dafb38e35dde331730bea33af32893653763e82565689009265db76b3b142d60f302a9545d82900aa6a017643c5c60583aefae43066ea3908e299612b078d06f102280dce4429461a42d80acdb6279c7aa190f9a8f61b485ffa430cbef199e1732da3d9b96cfac7ecc3b4959b86260eba763abb74c249180f67997967e716c95788bb6c04f182f40b795a929370c7fc72f1785f94cd36e3ddaddc15c7adb226cc131abc1863352caceb541d3924094db9c8a1c21c21640", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[5]; packet 1-2", + hexData: "c200000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac824704489c14d28b48268df096f2db0c040c0ffdfb1817c4e1dd32005e87a74104515069ccd1b12f6fa3f657891bca8f0d27f4bb42b23c8310897ac09241f511888ac2e431e3ba8d9114b3f8bb1470324c8a0815dd1589686d058d03fa2669ad7367dc8877abce4700ae999112eb7cba755b20b7db66918021a36a66c60ecaaeb3e0859e3f50d14c1f8c3f56966043f437bfc16098deeb02e2236cc42d2f2802f4082bec30afc347a75f78c5e7337c52502e8e8d945bb4d792ef69419e57ee4e8f3465de4d0a49955787dff0756f1bcd98268919c112ae5e87ec333d590988e111724627143bf5a0aa69c77ee322b1240a65718e0033c4b70c6a5dc2d3248aef71598d73effa0eb397b79279ce846d9c2556fca28595eb3b196f40f052f0127711abbbc334571b8de67f7d39bd9437f9196bfe0650c4972462473776eccae42418b16edd5f45414e1a5e13300ae6705a49ef68e1748e876ce04fe611c973f94fcbe18b0c1a0ae506ce6eac268021e9744899c33ffe44dabdf1ea5413c20ab1013326255ae6df2d6861a7e914001e231ce4065e0d89fb439afb928d660cea26f99eafe65c9f89c5639f3ba8ffdcdf7a777d7859ca948f123a5c4f1491e22803754373ab0f1b605ac09ca6018c6acca7f52aad786ce96d9ae28d09a77dd88d77f23e2e33475973a23dea914fb049662800cfbed416b61835bd3bc22dace817b3b271c816ccf9603de2209067def5b8747112c49659cf6c5281402bed38a4176c0331e0841592c503d342188ac97266866a102864f76b0ff2adf781e0af84ae725fb6db418545dbb70adde5d4ea6d87f8c9b64b579d4f969830056dc0007cdad648035ccdf2c24264905422dfc1a85ac7f519f475351e046a27268312d4ab6a66ea55be01226fd5dfce06f804cafe85ad997946db3d029ef28ebdd36103c8fc05b522c1d4debe034523595cef576c771f0848a221d76c63dd044ea61a3adcbe5d4f0ebcd0bb631f4db1d88a609a1c2a6cc3de29ccd8a40e8a770d806abdae300cc178a32b4ba979652d0b8849dff252571f0b09935744c5b33628a190ae2eb43543ed8dee8539fd464abfcd3cf826aa18c4e84cd11975c5e3bfd0827f7cd211a2084c8626ed4e32bb9877f4801dabe695132b835e335563cb2f4c3e9377cac57f7766a10620f1c57c9f485d66918613daf8b257035ec91481d0076899c45abdbec38b63745428dee481c1cba81b0ab6e7efd6b0c431e018b6503cb13d4df18dbbff195bad1063d59ca5066e0733d3f499109111d22304066e7656e755518ceb6d862095ae54e532fad82f6c21c0c4d776dfecc516b00fa59e117e79481d3fd386c9b0c4d6fa4ec295a5b5b67207c3db20a206e0e31dad2c8dafb38e35dde331730bea33af32893653763e82565689009265db76b3b142d60f302a9545d82900aa6a017643c5c60583aefae43066ea3908e299612b078d06f102280dce4429461a42d80acdb6279c7aa190f9a8f61b485ffa430cbef199e1732da3d9b96cfac7ecc3b4959b86260eba763abb74c249180f67997967e716c95788bb6c04f182f40b795a929370c7fc72f1785f94cd36e3ddaddc15c7adb226cc131abc1863352caceb541d3924094db9c8a1c21c21640c900000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac8247044898c283466f3b200164ad9b30e17b425e07f6722df94b9a77dd555fbf25e5b0bae4fef254daf03f156b78afd967614c78208deaadef3552040c055487804c047604c5d6846e67d33e5fb5f743a81f688220d6f4c87091a860885af95d2db27e9c5dd2361f8196f5c1ade8fb37e159980547c38c6a6ddd0e055fbbd52bd7615615ffca15a0c144899d25f21156c53cb3922d2ccb83073e26074025a3c39f64a67dc02044ce0d630d9120041b2233bd26282bd2d7d1a81d486b64cab6dba7fb4375e200f53523f714a066b96769f9b1dc7c14353fc1faa51c0aeb99507ff3ae90ad6f4bfbc9b9ea00a87f8bf8e213a84a9efe2ce624e629261d93c83642df97fe146bdef478cc92bba387c9b524ac83cde55ad8f4a4d8fb3c09c8245a50ac16ebb67d651110a10ad1f7dc74ed32b9d644bc4b229c56942072aae5c311059165ef6839e7cfa0717c7032b667b8618527722fdc10a4c0ea900e9b414521b89ca83f253ea414a410a8b0b13da25c4b618f2ccd79e5d2a7579b4c431107d56ab8df16125bc25673181a6bd5abb09941515806fda32659e5281457d8c093314da0087169781b306f348d3994d84bdef936bb11355c41ce02c359174dc2a366509c130c96ddf5f156e0eb7912ac56611cf4f59ff3a8785034e81738a53ebc7fb60aaed709d78980d39822aae7e9a9d985aee6c11252a5e984ff1d167b9d4dc5d02ca8ba1167422dfc0f68b3b5902f3fd032204a5020b14a288ecd845816575fca7ef13c85765385e9964a9f0f6e2e5c4e600b190b275cd91ee8a38ecf73d35bad7371c15fdb73059afb3178786e76750845232ef787767c6985ea9b9ed9ff73430b7afb8e0b66aa210ab1943b606021bb8f50c55534e1014003d947533c1c0bd0c16dfaabf4914497583b818e643a2d8bc32d33a07133b4eecf8bcbd802dfaae894ebd473338e074af1b3672c6f03c55e6c3885848a4379cdb5873d9ff3015f9ddf8d954b3cb4eb9f56f2e8ccb519dcba72c5832146011068d3074520370fc8008b62f3fdd65978865df1a85e58fb62315dc617a3367e6c9564fbf74f5d5015932d435f3aaaaed37bc7d14d974c0e15d899c684f17db3b4790bdeba076624f4f45a0cc1c6c078a527695110842e837ded310f8c6ddf69a18e455f09130d4b4136ca6d02eee0f103b9923adefe8cb36b8fd554a27fa7300c4a6173835ca2c4d38cc11e3849ebbf73c475b7d0713caed6e70f55b34ccffe03978f231d5f92bc1e2da7ce589e2cc2cd76183394469fa537cb927db14a383dc589cf10819eb90dbdb7981cd1eeb3a284c9ce55ed859fbfbe73ca22328073b534bc81d319b5fdb97e66d532b5ffc19516b4c70ecb68f963b053a82915f3f74c8809f082bbc89d97dff9e8c82a65ed4be8e2975b82f0b7c73f34797c61db4ea8ee273116564288563a71747cf31568317363dffdff1d9e159b2f9bcd3fe5be96a9c50ac885493cc16234c58cdf471381664c1e3b54d147ea5c779c9c9b06d5c493231d9c03b50c6cba315a5ee766a6e8578105306e0911270d347e8ade2b354037f507e4993708191d32b51a79f023f4fccabb7d2ac262004745cc528b8a6dc1d8849e9d4a0152ab326b1eab8a505eef65076aca156f7b9", + domain: "optimizationguide-pa.googleapis.com", + wantErr: false, + needsMoreData: false, + }, + { + name: "QUIC Chromebook Handshake[6]; packet 1", + hexData: "c500000001087e13dee82c592724000044d0a17c2a188dadf8c82a018cabf25d25211d9987189b2900ad36a8fa366990353230bc8d41ec5543e91e119a0fd35f72d320d670074d02f412a5418c9a3e25489db73b1f5e5ddb8fc8d6c51fd6a12dcade71e1fc8e89d2663e206185247847d218d695793a8a1054afd18d42a4feaa9bb5fae7651eab0bc5e9aa91539c311cbfd17f46d8894ac45eade657bd456ee87b4475f5e84a2c07571b082ecfdafd1a660075692ae22048d163067a60e06cf879eab20a464c6e44b242da94cc6b92e064ee6a52cb34c115056925d4615e51157da4e88f019ae144040827cf2cd2806b3f4018cde5f42e041ef55eab35fe65ff79dba1ddad5aba6221e93d4555510176463888666b1d1f53a2f3c71e8872b05717002717edd3a9c5c651be47a663ee877962623ee92310b2b1dbcd75ec2f31d9e2f0c87affe3ee083aaa0868c03c9b4e78660d8afa4e936f81dfcb2fc33dbc1ed3828195c8de3130a2757641652de23d270c8a25976546c46460d9635f499a650475a4288d1c62241f65ddd346e2bde3f4fb5fec76646f51d829b55e16a0975494c11101abb504f5398a761d59355a4843e14bfeced024239ca06717d27e932df5e050d7c54c9b3cc937f6b0731f0b1dc312b14cf4388bbc0c601abf8ca16ed7d9b597dd414156d75b486ac256f8989a072fe680961ac7446201aca359337ef0e58ffa2aa12bb3c9bd1a9f6237b5c37880cd450d8a95db7c1463c53ac4bca174f278be1a99fb75ded9fe282e0e44a03b51ad943cf8970e4ca7f8567d9a3883d07bc0ff6c11639ebc7614fa88496488cb2a49a158d0f008a6a03caf97d77c3d03a98c5a611b04f865d134e258f4094bd220e00bde789c3b3158e622d509073179c0725c7a14dde1cd76b4c6b61fc7ba87f05742f081a5c42dbe83445132b8ad1539ee36e221a9dc700ccab55e34688b082a1a8edc48335760f3d5fb7e92b548e7a09d11f15b97bda08ebb5ea355e274587720d7c1189e9ffbb00dba3c13da94d646e7742e3641d59aee50a5bd400cd992c5614325d8f1ff3529637c3f60adc1682fa6b5e26f3d52de73236680abde87ead74368e05c600a2fd291a592be00fba64eea78c9e1f5e55e61f691b3eb0ef17b7bd11f06427c89c4c930d18c0b28221a6c918322cae56397c7e2978bf253e7f2d987f4cbe66c44a96b45cddb9d5f6dae47bbc9e1f9dcbe6280e50c5dfc91efb469f408b28fed49c583800009dc8bfb4bc42174175df987a3be833582abd9aa09ba0425973de2ea9a4149a81ae1863e0c9f1b1075c26bf965dcbec2bea47ff6042495ed715b65fdd3266800994463c95960dfb6ceadfa07d58910d329fa7ef7a8f14da4a6d3b09faa5b17cbac8481ea46cbfcfb54f660929e268bcf2f86cd88a1b065dcc27f18110db9efb6fcf1eec62874ed3657b1d43419f39e785c510d239c021b7e97d258d789c90d39b434f1667495bd4f5dc5e0eb97df376d801cced0da3a85aab6ca12893d8622314b5d530f28ead33075891d0ee553d5bedcffb20fce9933ab5e117df816c96398f6a60c9e6f5b9182fd7d58869de01635b2c178ae7738beb81e318934ecd752393129fda6833718d6984d8e1a8d7bf52e9d93ad0902c0fbe3e66ae8305e43363d8996626646a684bebfb1809ac9823be750e84308ef573243b884d09ef294094ef256cfc13cb0fedb1e095ff71687c09a767bff308e562e1a6ce9964014a7afc8db2481d8d07486", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[6]; packet 1-2", + hexData: "c500000001087e13dee82c592724000044d0a17c2a188dadf8c82a018cabf25d25211d9987189b2900ad36a8fa366990353230bc8d41ec5543e91e119a0fd35f72d320d670074d02f412a5418c9a3e25489db73b1f5e5ddb8fc8d6c51fd6a12dcade71e1fc8e89d2663e206185247847d218d695793a8a1054afd18d42a4feaa9bb5fae7651eab0bc5e9aa91539c311cbfd17f46d8894ac45eade657bd456ee87b4475f5e84a2c07571b082ecfdafd1a660075692ae22048d163067a60e06cf879eab20a464c6e44b242da94cc6b92e064ee6a52cb34c115056925d4615e51157da4e88f019ae144040827cf2cd2806b3f4018cde5f42e041ef55eab35fe65ff79dba1ddad5aba6221e93d4555510176463888666b1d1f53a2f3c71e8872b05717002717edd3a9c5c651be47a663ee877962623ee92310b2b1dbcd75ec2f31d9e2f0c87affe3ee083aaa0868c03c9b4e78660d8afa4e936f81dfcb2fc33dbc1ed3828195c8de3130a2757641652de23d270c8a25976546c46460d9635f499a650475a4288d1c62241f65ddd346e2bde3f4fb5fec76646f51d829b55e16a0975494c11101abb504f5398a761d59355a4843e14bfeced024239ca06717d27e932df5e050d7c54c9b3cc937f6b0731f0b1dc312b14cf4388bbc0c601abf8ca16ed7d9b597dd414156d75b486ac256f8989a072fe680961ac7446201aca359337ef0e58ffa2aa12bb3c9bd1a9f6237b5c37880cd450d8a95db7c1463c53ac4bca174f278be1a99fb75ded9fe282e0e44a03b51ad943cf8970e4ca7f8567d9a3883d07bc0ff6c11639ebc7614fa88496488cb2a49a158d0f008a6a03caf97d77c3d03a98c5a611b04f865d134e258f4094bd220e00bde789c3b3158e622d509073179c0725c7a14dde1cd76b4c6b61fc7ba87f05742f081a5c42dbe83445132b8ad1539ee36e221a9dc700ccab55e34688b082a1a8edc48335760f3d5fb7e92b548e7a09d11f15b97bda08ebb5ea355e274587720d7c1189e9ffbb00dba3c13da94d646e7742e3641d59aee50a5bd400cd992c5614325d8f1ff3529637c3f60adc1682fa6b5e26f3d52de73236680abde87ead74368e05c600a2fd291a592be00fba64eea78c9e1f5e55e61f691b3eb0ef17b7bd11f06427c89c4c930d18c0b28221a6c918322cae56397c7e2978bf253e7f2d987f4cbe66c44a96b45cddb9d5f6dae47bbc9e1f9dcbe6280e50c5dfc91efb469f408b28fed49c583800009dc8bfb4bc42174175df987a3be833582abd9aa09ba0425973de2ea9a4149a81ae1863e0c9f1b1075c26bf965dcbec2bea47ff6042495ed715b65fdd3266800994463c95960dfb6ceadfa07d58910d329fa7ef7a8f14da4a6d3b09faa5b17cbac8481ea46cbfcfb54f660929e268bcf2f86cd88a1b065dcc27f18110db9efb6fcf1eec62874ed3657b1d43419f39e785c510d239c021b7e97d258d789c90d39b434f1667495bd4f5dc5e0eb97df376d801cced0da3a85aab6ca12893d8622314b5d530f28ead33075891d0ee553d5bedcffb20fce9933ab5e117df816c96398f6a60c9e6f5b9182fd7d58869de01635b2c178ae7738beb81e318934ecd752393129fda6833718d6984d8e1a8d7bf52e9d93ad0902c0fbe3e66ae8305e43363d8996626646a684bebfb1809ac9823be750e84308ef573243b884d09ef294094ef256cfc13cb0fedb1e095ff71687c09a767bff308e562e1a6ce9964014a7afc8db2481d8d07486cd00000001087e13dee82c592724000044d0b2c403d66eaa310a954540668e9edc4b17a321446ac931672ca3d9097b2854efdfa7a8f610be76592b56ef9154b9ab42f4dafc575a9b9572433fa2bba180194a32a72a92a42560ae840508317cf34015b1d7d88a633588ce4333cd12bec1f9d53fe905130eb136852d4f405ef66254a0807640fe415e6d667b9f5e2148826d5a6c36b3c44b822cfb7a1e76954610a522e1bc9703e155f8f1e0c705e411d09c5dec1183c61608174767408558da03290f5536682373a09c762deb829dfe3ee061ac9f509a2b3d20dbb77f262e9ba8a5ff50f895165742c90c4ff48eb0438d5ff8491700f97fbfced20e8e95ab9b67078ece6664527eb8a4e78944c07c6bc5c48776e141418c3b5a0e58d24114b7d65a83619525f5a10be53a55f6e3e65080cf42109aa2ec166fbb9079444ea6c809b5062227fed7cc81609a6d7ea471bc82cd0759a354aa896c7a8242c3d8c845aa52225bbaf7546a0de6510189cd2852f7b70a29687eee6a3a99a3a288f3c3672a72dba2843fcee320e18ea906adf1629cc7f8220b6be890a8d37f4093022717d8c6d76080d07798ba74e9cda1a4c26189c367a6d12da14621b93fbfd2d2365465350b5864e4981955750c6d4038b74644827cd60c8cad0d6a18869f99d61b214becedd8f71a476c2a1a0ea8fade7b15de6ee522b6b90d0cc4e6d40eb85d9b5097e2969e85eae6710285016ae47fbc858d63e13d882721aaef3cbe0a6e4c4910e8b3e465fa66a738d8979a444b4a4804baf5b4f9a5fc8438f4991c32d280c5d273d0a1e0d9eea30d003162cd313780a5b000f70fb7797e6111b354c3deac241cb816328b77d71584852966392cab9b1a3d64ead878a2cfac085452397ba17b5cddf96415c31846a80fa9a5a21fd5f9963324ad75505d4b5c70903ec3b5f91c3460d88a7d1c6b111a5206d11accac8a2115e1ce8e834b01f48e041dcd71585c6fcdd3a2e83ec2ff1a2b85e75134ec966afec023d375a2a8bcd54ef0c42d50e20c6cd9b9fdab63785e62c5a4ceb8451c560b647a6cd87698e56e0be4e2990337f45f1cb4c73f0dbf98da1158d75df6eb60ab7a61d836c7b7f3e5c809b850a5cc5369ddd4e455da5e88179e932311873d177febf6226a378c72324ea710e10ef74f6462e7dba25b7df336cb2947df749ad0b8455f3c5c9ba5c0c8eb1e665fe50451c928ce87cc81a0ae2102e7a4fc297392818db885943112fae3c4547ea48c89ef9cd44c2edba4856dbc72b956e4fc6a875bdac57b1cd2378ddb9322e5a1d1844977ff2a6e94d8a00f4ef0a7ef7949f0840c6cc781252830c70d37df9846a69eb95bc733ec420b7b3724572521496da27bba1fcb73c28eed1eefb094283ab01284ebd82004e0be9977f23fbf135b8162ac41a2bf9e1b941b05cb52c80279775070cf2f851bb0168235c97de47880d2d51f1d5ba8327af33f16f49d44326b0d08d20ca7880ff88a14d3201452d7fd452b24fc3b69b89c5149795bc4d0bf51b6ace2c9146048e5ca0259b4955a069a16d5a73e72248cec44b2ca542dcd326aca3ba53bd48368719f44d53eddbace11ff28c11c7fd90c38966752532d3f81325d1682839b2da3d69acc85275c71ef3b10c4d983cfb0cb464f554d9360b0d6ec8fd25b85824350866207eef9e2c66e43f5d08e86aa6186b36d9ece72f56e06ac8e51a1b53fab4cc496e69307e9810a9b5cf960f306ec54131dd8e917a5861ba92f6885e41993", + domain: "lh3.google.com", + wantErr: false, + needsMoreData: false, + }, + { + name: "QUIC Chromebook Handshake[7]; packet 1", + hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8b", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[7]; packet 1-2", + hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8bc4000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544898bcc839c7364f9518287bbf01c06e9fd67f74096cab78bc885ad264ca9151a48420ea11d5b9e3bc899cc0d663509c926bee5ef2a389a9945f5919677472b3245a2a68ab91654ba5f75026ae738a9e10b6b7439503961bedbfa2494099a9bba3b9595f70cacd8950abd5d3b8a85ac61e6482847e0ac71cbd63df462f4978afc7084ec5c9e65f3b57ad22f802d699c94f260212a61db6e6958ed60356bf991d3af9582cd1724a90f08e869ebd7f29fc3fb9e1198819a967096de1bdeea692fade6229b41c5efe6cdae313014db328e99bae1f323584a308f575b923a11185634b24c8cc1d608303c8ca5c1ae3d54b8b7ecb689d5e0e22323c5e47d8f9093080bcf1553f7fa238e156658ef50281f633f184e47992645c82ab9985b9b13f97f0bbd9db94285c131fff5870ea38691ade2e017ee6417a118a9b31f5b85ba2c76163f8668d90d448976d9d37238605d36b0b7e7abfb0e9c2866bb11b128bb70f003aeb77f439c6148d28719a4f336f4d622140faff82595233cff94906695e402b9115ba7b99f7d832f55d8de481af808ed9d48a493c3301d2b633cdcdc7a64acefaa9408b99a52768edeb9824cdb49257c6d3ea3f23763f70b74fc46252a97f7b3e173a93e81cca50fb12e8b0ea1083f9262e1607156296b5ff85e1531335441c90b442aa5dc616ed7b520bc040502280a0bdb011d6e9dac154587f033b3e9434cef567b724bf5203c315ef5e283028ec72ff33736499b8d326cd643e3a37d53e70b9a2384ac6037f488075eef8b2e507774d3d450ea4b1b65c1587eb2a60c6aad0ae20745a2c50f8acd8bc1e25740b4e7ab4b03a59731ccc487831b46ca0211264f4fd4a498e145aca0d7339663d29a7f32a14fc2b62a32f6354abf2d7de5ed9d7e0a1d6dbbfe14af25ca8c927dc8eeb2618f308eac81405552f1bafcaaede51f560fe5eb6aa78b8650ae97040b46469503c85994ebcf7ffa0222905657111325f861d380935c2a5b9ad26094e85b92327f79f66b4a2f8cd674344f931f0e056ba58a084a1026bc422b80ec11b17de1ca965e8ac9ede1464588303986ba8fabb395b55de5580131ae3bf61822e4e2817dacf765e034542e435c4e9ebc58f21cfa7901dfbcc2b2be98cc55fa6d9e0030e17a32abf84d551a74f7391dd204847c25ee6382f6374b8ecc5b29b598b9ba562e0a6f25ebb932570ae8ab7ef7d06e444fdf79845e88b3132ffb6e1f330e3424272da082486aba357bd254ef0738bb7f5c9db73d2a9ba0c9afeb34e09ff0e20bd44ba1a46ad3081db2f750d00b647756dec1bb41032e1aaf56f58d7046102aef6ae40517f9cbc148692401ad06ea4ff6ed3e5c8a0cff8f9466a3530a99cfb9b5a857a967675df0cfbe3b798fdc2d929fa4c86e3b3e3c45b75fa5db6c15953f25bcd025d7efc3172b2206a200128f6d8caca97154b2608511b35b0cba9a550d8f791552faed64036cfb8498dc60492207ab81c3f3edeafd9d3acf5bca2f2735117a273c70345d3b7e289b9948edda3d5ebb8adde7de2451fe2d942d92ab383dc9a9adb6fb9e8cbc0b73d852f176e0265a6107e6a59746e2f2e7203fd445e5fe278d02cd5dac1b7988e205c37bc5f35dc27572743810453b9b27e55c", + domain: "", + wantErr: true, + needsMoreData: true, + }, + { + name: "QUIC Chromebook Handshake[7]; packet 1-3", + hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8bc4000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544898bcc839c7364f9518287bbf01c06e9fd67f74096cab78bc885ad264ca9151a48420ea11d5b9e3bc899cc0d663509c926bee5ef2a389a9945f5919677472b3245a2a68ab91654ba5f75026ae738a9e10b6b7439503961bedbfa2494099a9bba3b9595f70cacd8950abd5d3b8a85ac61e6482847e0ac71cbd63df462f4978afc7084ec5c9e65f3b57ad22f802d699c94f260212a61db6e6958ed60356bf991d3af9582cd1724a90f08e869ebd7f29fc3fb9e1198819a967096de1bdeea692fade6229b41c5efe6cdae313014db328e99bae1f323584a308f575b923a11185634b24c8cc1d608303c8ca5c1ae3d54b8b7ecb689d5e0e22323c5e47d8f9093080bcf1553f7fa238e156658ef50281f633f184e47992645c82ab9985b9b13f97f0bbd9db94285c131fff5870ea38691ade2e017ee6417a118a9b31f5b85ba2c76163f8668d90d448976d9d37238605d36b0b7e7abfb0e9c2866bb11b128bb70f003aeb77f439c6148d28719a4f336f4d622140faff82595233cff94906695e402b9115ba7b99f7d832f55d8de481af808ed9d48a493c3301d2b633cdcdc7a64acefaa9408b99a52768edeb9824cdb49257c6d3ea3f23763f70b74fc46252a97f7b3e173a93e81cca50fb12e8b0ea1083f9262e1607156296b5ff85e1531335441c90b442aa5dc616ed7b520bc040502280a0bdb011d6e9dac154587f033b3e9434cef567b724bf5203c315ef5e283028ec72ff33736499b8d326cd643e3a37d53e70b9a2384ac6037f488075eef8b2e507774d3d450ea4b1b65c1587eb2a60c6aad0ae20745a2c50f8acd8bc1e25740b4e7ab4b03a59731ccc487831b46ca0211264f4fd4a498e145aca0d7339663d29a7f32a14fc2b62a32f6354abf2d7de5ed9d7e0a1d6dbbfe14af25ca8c927dc8eeb2618f308eac81405552f1bafcaaede51f560fe5eb6aa78b8650ae97040b46469503c85994ebcf7ffa0222905657111325f861d380935c2a5b9ad26094e85b92327f79f66b4a2f8cd674344f931f0e056ba58a084a1026bc422b80ec11b17de1ca965e8ac9ede1464588303986ba8fabb395b55de5580131ae3bf61822e4e2817dacf765e034542e435c4e9ebc58f21cfa7901dfbcc2b2be98cc55fa6d9e0030e17a32abf84d551a74f7391dd204847c25ee6382f6374b8ecc5b29b598b9ba562e0a6f25ebb932570ae8ab7ef7d06e444fdf79845e88b3132ffb6e1f330e3424272da082486aba357bd254ef0738bb7f5c9db73d2a9ba0c9afeb34e09ff0e20bd44ba1a46ad3081db2f750d00b647756dec1bb41032e1aaf56f58d7046102aef6ae40517f9cbc148692401ad06ea4ff6ed3e5c8a0cff8f9466a3530a99cfb9b5a857a967675df0cfbe3b798fdc2d929fa4c86e3b3e3c45b75fa5db6c15953f25bcd025d7efc3172b2206a200128f6d8caca97154b2608511b35b0cba9a550d8f791552faed64036cfb8498dc60492207ab81c3f3edeafd9d3acf5bca2f2735117a273c70345d3b7e289b9948edda3d5ebb8adde7de2451fe2d942d92ab383dc9a9adb6fb9e8cbc0b73d852f176e0265a6107e6a59746e2f2e7203fd445e5fe278d02cd5dac1b7988e205c37bc5f35dc27572743810453b9b27e55cc5000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544894fb2a6f735bea394066ea4ab5d3ef6f419c4bb099bcea8fd7e36d8ec14fae027d0c84ba8c38a5ea1e2e9ff24ec334dee8085d1fa9d88df2c5ce3dddd981f7b2a68011f2d4034f8e4c6f43e645fc71e19927aec56045f0ef72d9da1942dbe3e42248c2f695525c8d8fcf6dbc970c2eb5d607e07dce5c8c66d4de07de6b2c35bd3bfa12cd4e4fcd4cdda3e0b2ff7575d406db96502f1ec4d9b5748215fe2a4018c7dbdb5ead1ddfc06da5233ce359e4f3924f2af1b80c7af9d13437e0107f7e240e485458a3a656e31a543bc5a80f6a598dcbbd87ff9cb4ce6842abadb72d62ae03e6d12b7f43ac1805e408d738148fa3a5c34deae7378d7d7309a7e09f5bac848f4c031693fbe3382a2de66a9d007420512e24b8a78a9489b30794c7cc51dd751c1cdeb2870b7c4a9b9606547f843c1a16d9be986b2f3e3d2f73f89c8e15da9de3dfdf7a667ced977332c5c62fd83d3c9a5e9718643e716db8f1b6fb58904ca17c24c9a4a191ec42be4c405fb00e443429582ff712dfdbadf1bb7e2d2abb0cc14f39c13a3b7013c62fd99488f043a6aec3ed7998b943ee24905fc915e3144c54393fa67ff34fdce2e48bba044f13ee1cd9c4f59a1a41f7fb7bdd8daee73461234f7cce5d54b078c3ad2b0aa37850ed4bb24f4d310d4ce75ede546ed6e73c0ee495ff8ce4b7256e8f43949539bf9df6e8d0fbf066fb506020c5a72e5e8b686641b78b365474c65fe9d8dd41133e27326fe82744b45b6ad1170433f3746647a68b824e94a213cda4c02f78465acbccdccb5bad1c70806d5a5c98c94338f88bf980b05b72cb82a4fd5b2a8586a5e5c5f2760115df595091809cfd12829f09622b53ca1d3809060aa7ab5d1f3640b3c2792a55c58fc3e80ac5d7f39ab5774a80fa1fc70461d396fa70dad1f90598244ce4197cb3ea42dce4afd61ca8ef93c8bc254d347872db21edcc3857bcb8dbe627508aa4856bd7d46e512db071905b3db100f425ba9f7181f0cce005cee2a95ffc190ddc1939e7049e58791b0e186433b0409f5a49e4e3262690a5160f8267c9099afdc58182236833fe7f825dfca34b08801345c1592bbab4964b34d7efa6c9d92e0106ede9a10fbf2a1be32f61f914211c3caa8b4c14edec5f9c139ee14789fe7d6634ede9bf9789caa60f5bf30b092e65ff95d3b32cbdc3e5842e3b16b935d31a3a0963bb0fe60f41efb6590f24eaf5e84006b28b3c755203113237e43fa70a37a009f71da49ea3f8097914d6128ee2b18adac49b5111fd3d18db9fd61ef8a2202fac5cfce646ccbea7eaaa81df0f1b7243465de15a3900143f479852f0e40bfad434b96eea3941f527b0d31c3d8f43188b911140766b5d7146feb93bf4da1ec47023dcd8f89863e487ba25c3105a4e43c4ea90f479eb0f774f3aa044b817f7e69b5dd1b3954e7dcdb2a6c4d191d5b9178262449413310f3876dd93145716781cb077025deb37d23c35e6fdf867d5353d10303b9e60efa50e9ecd013cd3f5270fc0e117a19ffb63038c594190018bd9c1c18a799f548c08a3e6f768de0de344cff160689fed73aa7fc4edc26f77413145775745c25fc2c9da5b62e24eab9b21895cfcda6e6457ae9fdfa6c54b49b0d160dad0aa7f8cbd3820a3098", + domain: "ogads-pa.clients6.google.com", + wantErr: false, + needsMoreData: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkt, err := hex.DecodeString(tt.hexData) + if err != nil { + t.Fatalf("failed to decode hex string: %v", err) + } + quicHdr, err := quic.SniffQUIC(pkt) + if (err != nil) != tt.wantErr { + t.Errorf("SniffQUIC() error = %v, wantErr %v", err, tt.wantErr) + return + } + if (errors.Is(err, protocol.ErrProtoNeedMoreData)) != tt.needsMoreData { + t.Errorf("SniffQUIC() error = %v, expectsNoClue %v", err, tt.needsMoreData) + return + } + if err == nil && quicHdr.Domain() != tt.domain { + t.Errorf("SniffQUIC() domain = %v, want %v", quicHdr.Domain(), tt.domain) + } + }) + } +} + +func TestSniffQUICIncompleteServerName(t *testing.T) { + // 2 packets + pkts, err := hex.DecodeString("ca0000000108d799f48baf472fc3004047005411dcdaa24bd4c3cdbef653940a1ea0a671bb8c23222a8502bfcbd0a335af016df330b1190e4b1efd420aa82c4414a7dfd7b1aa63aa7bd0269ec8ed0d24895a467b737fcc514488a8142241b81c4952fb32e9f123f3f8d1be9eecc1340d7fa747c6f4838810209d51ad453fcec084ffaa382f9fed010a0585f8ce73c36dc6c628df940f72fb5b8e3e06f4612128d3b8f87725770d81305489a3a23cd443fcc668fece0ec9a909c82cfdb66819ce031422c3edd49ea72b6216efb940b8c186e2510b0f93d4cf3b6434ed9f5817c5a5a19fa861d24883bd48ba296df81e887fcb9ef18013ab1070df268049f3d35e8ddfffd2b100e4eb687176a02e7975085caa138e9df7cb6f9862356faafba3300f586df09af3b4e3eec15b6d1f14b34fe4eeb8e762ff482f3b7b0595c16d3eb20cf9c892b3f7c6449e51ffa33f3dc877d32e572e10ba4d5a320117110b150bec4a9434103de963aa2de1b22eaa992b59a8583c7a2d4e5743ef98a1b1f5f4aaeb2b49dbe441c331e7cb028e70432ce0890f78d40671f13d5feccc4ab264ae903affaa951897128d598ed8e8542294aa03abb0a2a7ec970ab874387b8777827c4847bb29ff93d0427c1fcc581407465611e4400bfeab39de4c4f65572365156421503706d66138317e514214db27303ad8b5b17eb443e959faad1ccb3d3def5f3843b241dd9a60378cd907a02cc5c6e8527715c7734079cf9143586ed471587690816133fa8f09b8421f2de6ff1f5cecc134ac404e53a367b57fd4f3c85fe7e121e7094fbf93c06d427cec30cd519ee43a6018a8bb8717c05d7660b379101a182bfd3018d485d5b46e81d39445ccd4d8baefdd7590e664f2289f11bbad3d35ea6d70e157aaa7f9e91a692794b85c5b86db33863151468d32792ecfdb2aec52784ded5aa431fa9a7337ddd9e95313a897fe5ca6ef3c7e0cf3d41f7f104506f8695f2ba307715022e5e98472d6ff24125004d14db2c7a4cc71ca39a9874bfb93b64dd54ef451097059bbdfa96aca6c6c01e1f000b8d35bdead1502874e5d80ac00a79593b6d7e2fbee406c2212b4abd7b7e9f0037518de232cd6559443f4e3f0f03c17cef616a74992f65754bb6699b08c0eb2ec59508086e496e070c6239a73f11a9d14a727b10188ca97ebf04851fa475ed1774836a94cfb6543c33888dfc250100e9992b0cca8bd27ae8552541f1b3d81546f15b740e4f07b41af864769fe17290eb7076c0c1f938ce56a45ead6deb6c89164f829543414a1b8cd2361c3d9b22cf85d4ad0d4221dcc87533506e32c1cfdc8240636b669f97f7c6e150ea3753fcb63a7b9eedb5615b257651e090b567a20fdc5c167ad58fa940985572db004db2b14709a6663c1fb79dc045e45cb7228f11eab4c60a48fffc702cd74dcf5aabded8355a5ae1a5e6325a3afff608e53af3aa944437139c438e6200c75bb27c8a8ce7ce9f9ec9723c4423e9e49c807b12484783ed79fc501b544f03e4b29bdf0c74eb055dbac9b78fe87a66efc819f9d2ea637dd478b920b7196232b970ed092050ba0621329aa2b3e26184176137a9c6283e4cc2c03c07e28d2da7b0485da51f131686f4dd6b23735c66316142f48781885c35c9940054a4b9187a20914bedd1c48e94af358994da5c6060dd224b0ba84ddd5e95f395fe941d464e2cccd857a180de756d32407540c3611b649c7a71cbf5a492bf5c8d4e112a743498ce83ea3e94bcaa6c00000000108d799f48baf472fc3004047005411dcdaa24bd4c3cdbef653940a1ea0a671bb8c23222a8502bfcbd0a335af016df330b1190e4b1efd420aa82c4414a7dfd7b1aa63aa7bd0269ec8ed0d24895a467b737fcc514488e7151df796764293639665547941eaaa7d83dfc9d42952583a821611921cba9e0276f9244f197e011b6c2d898255802c81c8792dd68d03cd8982ee6146c8734814e9640e0999312f97e898db60642ceca21700be9e698b5e577062148d1ac840e737f7bc4f18f9ffd13e656fca1517c96d0e0c607b67d7e75795f62131255b44c2178d896192348d5f053060fa21c855236401e853e435fbc71a428c8e126cd8393cb860193fd6304d9576e8a8ce7f6959739afa7d7ffaf86516afb061ade1eef603e8e5c66f002cacbef25546e84b59be707c547837f7b49471a59325ad58a4f189a525a3a354a4be5ffd3db34ce0585ec470f78f0ff6eb807989d5998e2fccc80bd651de654da3581b6c8a858b56c4df47c3041c50d74f0b3a3a5a325882cd356d0c46d6db74559cef934a1655c65a06daff218244f5f4e95b96794e45d371a03afee2c89768fe3d7bc134844f45440620a88d6ddb5d07b7f6c9b2adfbf5743c5c586520504febf14ee4b216f8186ee1bf9802ccebd4dac519bd337a554940a3873cd21ef32f3d4d4fdfd3eaabb172b5314490b3587a9ca2dc12637fa80dcd526859bb83d54f67dedeb173470e052beec9edba4b41444d31253accb3381237179a5381d437fc4d47c0a82f1c9da8c76ad3eee9417bf69acb8d530ac824e65d796b84bee40b14daa9e80c11e466f93233e1768ecc6c0237e71b901f1faa093bbd9e5324b2a160d02f09d4590392279a15c73edc4d1d7501ebad992fc8f1c9dce96c190879225dbbb09d20532f9dcbb845e3484c87cb8c3b06fe892b07bdc60926e3d4b5c9024acde5abed0a72686d546da535a2122cfd6d6488a491dc9612b90f3f6709835e83f59fd612c2259c3ac944fdae87ffd744176a92fbea1efd037565a39d5bc38991cfe6617135b43cd6c6484a3b228e10d3f8184227a7ee63121be20f099c4264461d52aecf4b8ee1a4d0ec41717f9a68e1394544cf3be0a3a171ece35c879b67afdaac49c12731e6dc5ccaa49fa72610901036a7f0752951077cd9f1d77eb6e4f12c16dab08e1a9655fda3ba0e80c34c767f40e61491f706ab9d1b50f48273ca4e37708d075909a3e2c4b173510114d8377b5300994f9a4f794db2781b98c2e9d0056fdbe87b249beb3e7301c86efe6b32242b4c7fabef280a1ca7bdd6abb38624cbebb01b6b75b07d0cd9090b2c296d0e330b9008788dd6f7e1af1dd63b02408f1a67b57be3316152270bf90c36e9f74322c9caba5adab3309e5bd7c44d3c2ac77329e167dafda7395e37313723fb550a6ab96729a9fa8f9e4a3e5957bffa3302617d48fd38196635309928c491ca974a112768de16043fc618001780b49a0bac2ea9fc2607ebd1f25b8ef65a6cab42d16c9184e23af496ac5cfab274d9557006d9c52bb56cd8c019ddf085f260a0582ef465c73f97fedc02271dda53eec20e6aa50503357ed1847f7d976899cb24e5dd42dd87ae48db3c9335b38957fc0f86a6ceee590b1cbee41fc6ace3189886bd86f9b082e66fb263f1a98dd8e564bff9ec412ae79e29fd519ac96e9c5b446b436b78a6ca555c496ca5c5e73c2c1c5837b13f7a953d57738440d4ed60efd4701bbbbcad79e1697b5724695f52dab8bce02e7f") + common.Must(err) + // The first packet ServerName is incomplete + pkt1 := make([]byte, 1250) + copy(pkt1, pkts) + quicHdr, err := quic.SniffQUIC(pkt1) + if !errors.Is(err, protocol.ErrProtoNeedMoreData) { + t.Error("failed") + } else { + quicHdr, err = quic.SniffQUIC(pkts) + } + if err != nil || quicHdr.Domain() != "play.google.com" { + t.Error("failed") + } +} + +func TestSniffQUICPacketNumberLength4(t *testing.T) { + // packetNumberLength = 4 + pkt, err := hex.DecodeString("c60000000108fa6cc47e912693590000449e5ea65fc129945b27f59f0b18f737a53e44f322681ecec36beeb943293db0ed751bd86413a840f9b9e9be718f3e2dba1100a8bab3a21e1541580ffd98e79aa89fa723e8d3a46f7876504c82b9ef3a081b0f8cb551370d9801ee86da77f09eec5aa19b7bf580a80ded3c9c83378285177115fd30b350c2a596ae265b3b538a81c183c0cfd13eabecfbbeb38416a5b19259731b838842c0eb33e646b9bb1f672043e90de33c3442151ee8db7d9cd66238f769f4486432ac28785a5083c616539f8320321060f64f9a0dc6af718754d645892397ff32956c4c1c97d0d9e44cdfa8d1a0ad90c3bbb7810b2196d638fd772a172a9510ea12ef12fe4050c5678851be26ec6ea6ab11824cc86ce071d110f72816166c01622c0207e9d97f8867ec7c63149e974c5a81db9cb5e0061cff2713538daa1c9ad1382ab7d883ecc85158dc76587793b258b4b0aded3f4c12b515a9183ee419b304cf748fc321f15b3f80cc53da1b889d1ac06b996d35e8d01306885851ad253083f37d0d588c9f619da25f6eb8360b846bc26913af616e2c3eabce9dbb61f7dc96b6dcb79e19905ac9ba8f3938d03f8a3647403dc919f37bb585a0d67b7ce955547d15c82eed6d94b04dfa009eaf8b30448e1450043c48845acb4fefcf29bdd55ec14e395d0e8cd8400ffdda5a58747c6e8a66d0dc5fb25f3615081b2b546e004079573e99290f5daf72705ca495707468dd26d94c7f04d7e6f89d8148ecbab67c8c0062984e0ba02539527370a2a157a58eab342ad671641812ed35b4a52ba07d244d9b5d64e29f012133d21fa4afa31645c21f6d836ad937bb75f7177768b5f94bb77e95aaf9a85f8fb7e599d482724f694cf5d7d3f61bfca892794bdcb3de7a5f321db8120560bc32b8839c0a5994917c151cc6bd4c1614c5f117e637c19dab7cff28c4848c3b328eb97e49cefded2d44a824f2705807770c2ef9dd07f0fe0198659ad062e1889e280e5f3d1c52a92ef27d4565ebae9b9a18a803b70f38e5db237ed99583d8952c79492e35e1f1c41664f1f45566126a7ec44a90004b015b893aa805fcd772737fb8dbbe7af56b9eac288ef6cab7cbb6f7a3c0b29a43bc84b6280def0f7d727b3238cba3eda2e2d110de87ad0f10e25a60783cdb0c05116df5359b19b40007812b898d03dc1d697690761856d785b83ac95778db69c3df7a8f0e092ee6ed2c9c189ebe40734b02cee2d02599e931d4cc560d38a7ec355b9f339b932613ee18f8162e8d3cb81301bfc6d726b7c26ec96d5edcaf0de171563482ed2f2de3001dbca2aee1029fdaece4340fd2d5ae8333819d5ece7c9d3f77f99a81fccd1fbcc3ea585c1538e0363141e0fd20338a193695377987afacd0baf1f0dce11b4bbf29965109bddb508e8a0974c08906d4ea340d51af376c3dda55bf97e3ba5d688533980e12704679bb18d0ef4ddd5b3af1b7676528c4bf4a84c84d40892715c0a8808ad51d6ebcc6469da708d6953e9ddcfbd19bae90e9b078f1b6641401b979304b0ef52b1441e1797ed366bb0519ca8bf9c6eba72518647d0a1400ca66a20fdd8e3ff06ee52c199bd8f941b1722a0bbf8c15447452788ba81a68431f735d99e8a80691ef64d1bf470350c8878aed3e2421223d0ba6a3d84928c8e6db0972263df9da49b8f5") + common.Must(err) + quicHdr, err := quic.SniffQUIC(pkt) + if err != nil || quicHdr.Domain() != "www.google.com" { + t.Error("failed") + } +} diff --git a/xray-core/common/protocol/tls/sniff.go b/xray-core/common/protocol/tls/sniff.go index e7661fa7e0..11660b92b3 100644 --- a/xray-core/common/protocol/tls/sniff.go +++ b/xray-core/common/protocol/tls/sniff.go @@ -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 }