Compare commits

..

83 Commits

Author SHA1 Message Date
Sergei Maklagin
05b82f63da Update tailscale 2026-04-26 21:31:13 +03:00
Sergei Maklagin
00c08995c7 Update sing-box core 2026-04-26 21:11:31 +03:00
Sergei Maklagin
cb3131b3d4 Update dependencies 2026-03-11 20:10:06 +03:00
Sergei Maklagin
47a62927f0 Merge branch 'extended' of https://github.com/shtorm-7/sing-box-extended into extended 2026-03-10 09:06:57 +03:00
Sergei Maklagin
f683fcb3cb Update dependencies 2026-03-10 08:37:04 +03:00
Sergei Maklagin
6cd8b832fe Update sing-box core 2026-03-10 04:25:01 +03:00
世界
c0d45aebfa Bump version 2026-03-07 15:55:42 +08:00
Shtorm
f95b34a8a7 Update README.md
Signed-off-by: Shtorm <108103062+shtorm-7@users.noreply.github.com>
2026-02-26 02:43:48 +03:00
Sergei Maklagin
b62000e924 Update README.md 2026-02-25 14:10:09 +03:00
Sergei Maklagin
a03af44c61 Add DONATE.md 2026-02-25 14:03:36 +03:00
Sergei Maklagin
aa103fdfc6 Fix examples 2026-02-22 18:10:15 +03:00
Sergei Maklagin
c82e613c52 Fix typo 2026-02-22 17:59:02 +03:00
Sergei Maklagin
3d16078651 Fix padding 2026-02-22 16:22:52 +03:00
Sergei Maklagin
18b1101fbe Update Dockerfile 2026-02-22 15:50:39 +03:00
Sergei Maklagin
4ebe870306 Update xhttp examples 2026-02-22 15:50:00 +03:00
Sergei Maklagin
50c5e9df0d Fix xhttp options 2026-02-22 15:46:12 +03:00
Sergei Maklagin
c8a993834e Fix Range 2026-02-22 15:45:53 +03:00
Sergei Maklagin
260bbbfb45 Fix examples 2026-02-22 14:51:16 +03:00
Sergei Maklagin
82337299b9 Update xhttp 2026-02-22 14:48:52 +03:00
Sergei Maklagin
c229c79dcc Update sing-box core 2026-02-22 14:46:42 +03:00
世界
f63091d14d Bump version 2026-02-15 21:05:34 +08:00
世界
1c4a01ee90 Fix matching multi predefined 2026-02-15 19:20:31 +08:00
世界
4d7f99310c Fix matching rule-set invert 2026-02-15 19:20:11 +08:00
世界
6fc511f56e wireguard: Fix missing fallback for gso 2026-02-15 19:20:03 +08:00
世界
d18d2b352a Bump version 2026-02-09 13:57:18 +08:00
世界
534128bba9 tuic: Fix udp context 2026-02-09 13:55:09 +08:00
世界
736a7368c6 Fix naive padding 2026-02-09 13:53:32 +08:00
世界
e7a9c90213 Fix DNS cache lock goroutine leak
The cache deduplication in Client.Exchange uses a channel-based lock
per DNS question. Waiting goroutines blocked on <-cond without context
awareness, causing them to accumulate indefinitely when the owning
goroutine's transport call stalls. Add select on ctx.Done() so waiters
respect context cancellation and timeouts.
2026-02-06 22:28:30 +08:00
世界
0f3774e501 Bump version 2026-02-05 17:13:38 +08:00
世界
2f8e656522 Update Go to 1.25.7 2026-02-05 17:12:42 +08:00
世界
3ba30e3f00 Fix route_address_set duplicated IP sets causing route creation failure
The FlatMap calls pre-populated routeAddressSet and routeExcludeAddressSet
before the for-loops which appended the same IP sets again, doubling every
entry. On Windows this caused CreateIpForwardEntry2 to return
ERROR_OBJECT_ALREADY_EXISTS.

Fixes #3725
2026-02-02 17:29:21 +08:00
世界
f2639a5829 Fix random iproute2 table index was incorrectly removed 2026-02-02 14:13:49 +08:00
世界
69bebbda82 Bump version 2026-02-01 10:19:35 +08:00
世界
00b2c042ee Disable rp filter atomically 2026-02-01 10:17:34 +08:00
世界
d9eb8f3ab6 Fix varbin serialization 2026-02-01 10:11:15 +08:00
世界
58025a01f8 Fix auto_redirect fallback rule 2026-01-29 12:07:15 +08:00
世界
99cad72ea8 Bump version 2026-01-28 16:56:08 +08:00
世界
6e96d620fe Minor fixes 2026-01-28 16:56:08 +08:00
Sergei Maklagin
596291567f Update AmneziaWG 2026-01-25 21:24:43 +03:00
Sergei Maklagin
a2a5f46cb6 Resolve conflicts 2026-01-18 21:53:22 +03:00
Sergei Maklagin
f6da8e52b4 Fix logger 2026-01-18 21:51:30 +03:00
世界
b27d707668 Bump version 2026-01-17 04:54:24 +08:00
Sergei Maklagin
287fe834db Update XHTTP 2025-12-11 02:46:57 +03:00
Sergei Maklagin
d7f0cea4ff Fix typo 2025-12-08 23:18:51 +03:00
Sergei Maklagin
d8b470d1ba Add new wireguard options 2025-12-08 22:32:33 +03:00
Sergei Maklagin
984fc295b3 Fix XHTTP TLS 2025-12-08 22:30:58 +03:00
Shtorm
6e4b7ed744 Merge pull request #6 from starifly/extended
fix(xhttp): use download request URL for down leg
2025-11-03 19:13:46 +03:00
starifly
855d400654 fix(xhttp): use download request URL for down leg 2025-11-03 23:18:03 +08:00
Sergei Maklagin
4c5e2c6645 Remove direct detour checking 2025-11-02 17:59:12 +03:00
Sergei Maklagin
725eccdea8 Fix Range 2025-11-02 17:55:18 +03:00
Sergei Maklagin
2ff042abd2 Resolve unnecessary logger 2025-11-02 17:41:18 +03:00
Sergei Maklagin
ffb282e47e Resolve conflicts 2025-11-02 17:39:38 +03:00
Sergei Maklagin
91f9134379 Update README.md 2025-09-15 01:25:46 +03:00
Sergei Maklagin
5a4de7b242 Integrate Amnezia 1.5 2025-09-15 01:25:19 +03:00
Sergei Maklagin
bc91312a73 Update go.sum 2025-09-14 23:45:55 +03:00
Sergei Maklagin
1d603e24fd Resolve conflicts 2025-09-14 23:44:29 +03:00
Sergei Maklagin
9dc526ea1f Resolve conflicts 2025-08-15 12:57:47 +03:00
Sergei Maklagin
93eb435e26 Resolve conflicts 2025-08-15 12:56:52 +03:00
Sergei Maklagin
e22416f0d9 Merge branch 'extended' of https://github.com/shtorm-7/sing-box-extended into extended 2025-07-14 13:27:54 +03:00
Sergei Maklagin
89497dbfd5 Update dependencies 2025-07-13 21:48:31 +03:00
Sergei Maklagin
8388abbb77 Resolve conflicts 2025-07-13 21:44:23 +03:00
Sergei Maklagin
180b7c4134 Fix tunnel client 2025-07-13 21:41:41 +03:00
Sergei Maklagin
deda2cca5e Fix xhttp transport 2025-07-13 21:40:34 +03:00
Shtorm
691ecd45a9 Update README.md
Signed-off-by: Shtorm <108103062+shtorm-7@users.noreply.github.com>
2025-07-06 21:16:54 +03:00
Sergei Maklagin
209b89a4a3 Add tunnel 2025-07-06 18:31:06 +03:00
Sergei Maklagin
765111a552 Merge tag 'v1.11.14' into HEAD 2025-06-19 20:18:51 +03:00
Sergei Maklagin
824eac453b Add new examples 2025-06-15 22:23:42 +03:00
Sergei Maklagin
85a1a8a53b Format examples 2025-06-15 22:23:25 +03:00
Sergei Maklagin
ae9e7aa5f4 Fix Range 2025-06-15 22:21:58 +03:00
Sergei Maklagin
52b71c6f00 Add sdns transport 2025-06-15 20:47:05 +03:00
Sergei Maklagin
5d04673783 Fix XHTTP Fqdn 2025-06-15 19:41:22 +03:00
Sergei Maklagin
307c429715 Fix WARP endpoint error 2025-06-15 19:40:42 +03:00
Sergei Maklagin
6801b58d96 Fix interrupt_exist_connections 2025-06-15 19:32:14 +03:00
Sergei Maklagin
6272596ebc Add unified delay 2025-06-15 19:24:09 +03:00
Sergei Maklagin
7c4c2d5ca8 Add mieru protocol 2025-06-15 18:04:27 +03:00
Sergei Maklagin
6768c77fa0 Merge tag 'v1.11.13' into HEAD 2025-06-08 22:43:21 +03:00
Sergei Maklagin
0dff811977 Add examples 2025-06-08 22:38:32 +03:00
Sergei Maklagin
e1a58d12fe Resolve conflicts 2025-06-08 19:54:17 +03:00
Sergei Maklagin
4e74a4108d refactor: WARP 2025-06-08 19:51:17 +03:00
Sergei Maklagin
0238c54261 Add xhttp transport 2025-06-08 19:35:59 +03:00
Sergei Maklagin
5c911c97d8 Added WARP endpoint 2025-06-01 22:06:34 +03:00
Sergei Maklagin
2cfc8092ad Fix endpoint manager locks 2025-05-29 22:48:15 +03:00
Sergei Maklagin
26bd698462 Integrate AmneziaWG 2025-05-24 23:54:15 +03:00
678 changed files with 3588 additions and 79978 deletions

View File

@@ -1,16 +1,12 @@
-s dir
--name sing-box-extended
--name sing-box
--category net
--license GPL-3.0-or-later
--description "The universal proxy platform (extended)."
--description "The universal proxy platform."
--url "https://sing-box.sagernet.org/"
--maintainer "nekohasekai <contact-git@sekai.icu>"
--no-deb-generate-changes
--provides sing-box
--conflicts sing-box
--replaces sing-box
--config-files /etc/config/sing-box
--config-files /etc/sing-box/config.json

View File

@@ -1 +1 @@
98d539ce67568fb911654e66a14cf4247ed833ec
e4926ba205fae5351e3d3eeafff7e7029654424a

View File

@@ -27,7 +27,10 @@ fi
PROJECT=$(cd "$(dirname "$0")/.."; pwd)
# Convert version to APK format:
APK_VERSION=$(echo "$VERSION" | sed -E 's/-([a-z]+)\.([0-9]+)/_\1\2/' | sed -E 's/-[a-z]+-/./g')
# 1.13.0-beta.8 -> 1.13.0_beta8-r0
# 1.13.0-rc.3 -> 1.13.0_rc3-r0
# 1.13.0 -> 1.13.0-r0
APK_VERSION=$(echo "$VERSION" | sed -E 's/-([a-z]+)\.([0-9]+)/_\1\2/')
APK_VERSION="${APK_VERSION}-r0"
ROOT_DIR=$(mktemp -d)
@@ -75,16 +78,15 @@ done < "$PACKAGES_DIR/.conffiles" > "$PACKAGES_DIR/.conffiles_static"
# Build APK
apk --root "$APK_ROOT_DIR" mkpkg \
--info "name:sing-box-extended" \
--info "name:sing-box" \
--info "version:${APK_VERSION}" \
--info "description:The universal proxy platform (extended)." \
--info "description:The universal proxy platform." \
--info "arch:${ARCHITECTURE}" \
--info "license:GPL-3.0-or-later" \
--info "origin:sing-box-extended" \
--info "origin:sing-box" \
--info "url:https://sing-box.sagernet.org/" \
--info "maintainer:nekohasekai <contact-git@sekai.icu>" \
--info "depends:ca-bundle kmod-inet-diag kmod-tun firewall4 kmod-nft-queue" \
--info "provides:sing-box" \
--info "provider-priority:100" \
--script "pre-deinstall:${PROJECT}/release/config/openwrt.prerm" \
--files "$ROOT_DIR" \

View File

@@ -1,57 +0,0 @@
#!/usr/bin/env bash
set -e -o pipefail
VERSION="$1"
TARGET="$2"
BINARY_PATH="$3"
PROJECT=$(cd "$(dirname "$0")/.."; pwd)
DIST="$PROJECT/dist"
case "$TARGET" in
linux_amd64*) ARCHITECTURES="x86_64" ;;
linux_arm64*) ARCHITECTURES="aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" ;;
linux_386*softfloat) ARCHITECTURES="i386_pentium-mmx" ;;
linux_386*) ARCHITECTURES="i386_pentium4" ;;
linux_arm_7*) ARCHITECTURES="arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" ;;
linux_arm_6*) ARCHITECTURES="arm_arm1176jzf-s_vfp" ;;
linux_arm_5*) ARCHITECTURES="arm_arm926ej-s arm_cortex-a7 arm_cortex-a9 arm_fa526 arm_xscale" ;;
linux_mips64_*) ARCHITECTURES="mips64_mips64r2 mips64_octeonplus" ;;
linux_mips64le*) ARCHITECTURES="mips64el_mips64r2" ;;
linux_mipsle*hardfloat) ARCHITECTURES="mipsel_24kc_24kf" ;;
linux_mipsle*) ARCHITECTURES="mipsel_24kc mipsel_74kc mipsel_mips32" ;;
linux_mips_*) ARCHITECTURES="mips_24kc mips_4kec mips_mips32" ;;
linux_riscv64*) ARCHITECTURES="riscv64_generic" ;;
linux_loong64*) ARCHITECTURES="loongarch64_generic" ;;
*) echo "Unknown target: $TARGET"; exit 1 ;;
esac
PKG_VERSION="${VERSION//-/\~}"
FPM_DIR=$(mktemp -d)
sed "s|release/|$PROJECT/release/|g;s|^LICENSE|$PROJECT/LICENSE|" "$PROJECT/.fpm_openwrt" > "$FPM_DIR/.fpm"
trap 'rm -rf "$FPM_DIR"' EXIT
for ARCH in $ARCHITECTURES; do
TMP_DEB=$(mktemp -p "$DIST" _openwrt_XXXXXX.deb)
rm -f "$TMP_DEB"
(cd "$FPM_DIR" && fpm -t deb \
-v "$PKG_VERSION" \
-p "$TMP_DEB" \
--architecture all \
"$BINARY_PATH=/usr/bin/sing-box")
bash "$PROJECT/.github/deb2ipk.sh" \
"$ARCH" \
"$TMP_DEB" \
"$DIST/sing-box-extended_${VERSION}_openwrt_${ARCH}.ipk"
rm -f "$TMP_DEB"
if command -v apk &>/dev/null; then
bash "$PROJECT/.github/build_openwrt_apk.sh" \
"$ARCH" "$VERSION" "$BINARY_PATH" \
"$DIST/sing-box-extended_${VERSION}_openwrt_${ARCH}.apk"
fi
echo "Built: sing-box-extended_${VERSION}_openwrt_${ARCH} (.ipk/.apk)"
done

View File

@@ -2,7 +2,7 @@
set -euo pipefail
VERSION="1.25.10"
VERSION="1.25.9"
PATCH_COMMITS=(
"afe69d3cec1c6dcf0f1797b20546795730850070"
"1ed289b0cf87dc5aae9c6fe1aa5f200a83412938"

View File

@@ -2,7 +2,7 @@
set -euo pipefail
VERSION="1.25.10"
VERSION="1.25.9"
PATCH_COMMITS=(
"466f6c7a29bc098b0d4c987b803c779222894a11"
"1bdabae205052afe1dadb2ad6f1ba612cdbc532a"

View File

@@ -47,7 +47,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ~1.25.10
go-version: ~1.25.9
- name: Check input version
if: github.event_name == 'workflow_dispatch'
run: |-
@@ -124,7 +124,7 @@ jobs:
if: ${{ ! matrix.legacy_win7 }}
uses: actions/setup-go@v5
with:
go-version: ~1.25.10
go-version: ~1.25.9
- name: Cache Go for Windows 7
if: matrix.legacy_win7
id: cache-go-for-windows7
@@ -240,8 +240,7 @@ jobs:
if: matrix.variant == 'purego' && matrix.naive
run: |
cd ~/cronet-go
CGO_ENABLED=0 go build -v -o "$RUNNER_TEMP/build-naive" ./cmd/build-naive
GOPROXY=direct GOSUMDB=off "$RUNNER_TEMP/build-naive" extract-lib --target ${{ matrix.os }}/${{ matrix.arch }} -o $GITHUB_WORKSPACE/dist
CGO_ENABLED=0 go run -v ./cmd/build-naive extract-lib --target ${{ matrix.os }}/${{ matrix.arch }} -o $GITHUB_WORKSPACE/dist
- name: Build (glibc)
if: matrix.variant == 'glibc'
run: |
@@ -595,15 +594,8 @@ jobs:
if: matrix.naive
run: |
$CRONET_GO_VERSION = Get-Content .github/CRONET_GO_VERSION
git init "$env:RUNNER_TEMP/cronet-go"
git -C "$env:RUNNER_TEMP/cronet-go" remote add origin https://github.com/sagernet/cronet-go.git
git -C "$env:RUNNER_TEMP/cronet-go" fetch --depth=1 origin "$CRONET_GO_VERSION"
git -C "$env:RUNNER_TEMP/cronet-go" checkout FETCH_HEAD
$env:CGO_ENABLED = "0"
go -C "$env:RUNNER_TEMP/cronet-go" build -v -o "$env:RUNNER_TEMP/build-naive.exe" ./cmd/build-naive
$env:GOPROXY = "direct"
$env:GOSUMDB = "off"
& "$env:RUNNER_TEMP/build-naive.exe" extract-lib --target windows/${{ matrix.arch }} -o dist
go run -v "github.com/sagernet/cronet-go/cmd/build-naive@$CRONET_GO_VERSION" extract-lib --target windows/${{ matrix.arch }} -o dist
- name: Archive
if: matrix.naive
run: |
@@ -649,7 +641,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ~1.25.10
go-version: ~1.25.9
- name: Setup Android NDK
id: setup-ndk
uses: nttld/setup-ndk@v1
@@ -681,10 +673,6 @@ jobs:
run: |-
cd clients/android
git checkout dev
- name: Checkout submodule recursive
run: |-
cd clients/android
git submodule update --init --recursive -v
- name: Gradle cache
uses: actions/cache@v4
with:
@@ -743,7 +731,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ~1.25.10
go-version: ~1.25.9
- name: Setup Android NDK
id: setup-ndk
uses: nttld/setup-ndk@v1
@@ -842,7 +830,7 @@ jobs:
if: matrix.if
uses: actions/setup-go@v5
with:
go-version: ~1.25.10
go-version: ~1.25.9
- name: Set tag
if: matrix.if
run: |-

View File

@@ -55,7 +55,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ~1.25.10
go-version: ~1.25.9
- name: Clone cronet-go
if: matrix.naive
run: |

View File

@@ -18,60 +18,21 @@ on:
- testing
- unstable
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}-${{ inputs.build }}
cancel-in-progress: true
jobs:
build:
name: Lint ${{ matrix.goos }}/${{ matrix.goarch }}
name: Build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- goos: windows
goarch: amd64
- goos: windows
goarch: '386'
- goos: windows
goarch: arm64
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: linux
goarch: arm
- goos: linux
goarch: '386'
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
- goos: android
goarch: arm64
# - goos: freebsd
# goarch: amd64
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ^1.25
- name: Cache go module
uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
key: go-${{ hashFiles('**/go.sum') }}
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
with:
version: latest
args: --timeout=30m

View File

@@ -29,7 +29,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ~1.25.10
go-version: ~1.25.9
- name: Check input version
if: github.event_name == 'workflow_dispatch'
run: |-
@@ -72,7 +72,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ~1.25.10
go-version: ~1.25.9
- name: Clone cronet-go
if: matrix.naive
run: |

9
.gitignore vendored
View File

@@ -21,12 +21,3 @@
CLAUDE.md
AGENTS.md
/.claude/
dist
logs
/*.so
/*.log
/*.db-shm
/*.db-wal
/*.db.backup.*
/test_download.bin
/wget-log

6
.gitmodules vendored
View File

@@ -0,0 +1,6 @@
[submodule "clients/apple"]
path = clients/apple
url = https://github.com/SagerNet/sing-box-for-apple.git
[submodule "clients/android"]
path = clients/android
url = https://github.com/SagerNet/sing-box-for-android.git

View File

@@ -1,6 +1,6 @@
version: "2"
run:
go: "1.24"
go: "1.25"
build-tags:
- with_gvisor
- with_quic
@@ -17,29 +17,30 @@ run:
linters:
default: none
enable:
- govet
- ineffassign
- paralleltest
- staticcheck
- unused
- modernize
settings:
modernize:
disable:
- omitzero # nested struct omitempty -> omitzero changes JSON output semantics
staticcheck:
checks:
- all
- -QF1008 # could remove embedded field "<interface>" from selector
- -ST1003 # should not use ALL_CAPS in Go names; use CamelCase instead
- -QF1001 # could apply De Morgan's law
- -S1000
- -S1008
- -S1017
- -ST1003
- -QF1001
- -QF1003
- -QF1008
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- transport/simple-obfs
- \.pb\.go$
- third_party$
- builtin$
- examples$
@@ -54,3 +55,10 @@ formatters:
- prefix(github.com/sagernet/)
- default
custom-order: true
exclusions:
generated: lax
paths:
- transport/simple-obfs
- third_party$
- builtin$
- examples$

View File

@@ -1,563 +0,0 @@
version: 2
project_name: sing-box
builds:
- &template
id: main
main: ./cmd/sing-box
flags:
- -v
- -trimpath
ldflags:
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
- -s
- -buildid=
- -checklinkname=0
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_manager
- with_admin_panel
- with_profiler
- badlinkname
- tfogo_checklinkname0
env:
- CGO_ENABLED=0
- GOTOOLCHAIN=local
targets:
- linux_386
- linux_amd64_v1
- linux_arm64
- linux_arm_6
- linux_arm_7
- linux_s390x
- linux_riscv64
- windows_amd64_v1
- windows_386
- windows_arm64
- darwin_amd64_v1
- darwin_arm64
mod_timestamp: '{{ .CommitTimestamp }}'
- id: mips
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_profiler
- badlinkname
- tfogo_checklinkname0
targets:
- linux_mips
- linux_mips_softfloat
- linux_mipsle
- linux_mipsle_softfloat
- linux_mips64
- linux_mips64le
- id: android
<<: *template
env:
- CGO_ENABLED=1
- GOTOOLCHAIN=local
overrides:
- goos: android
goarch: arm
goarm: 7
env:
- CC=armv7a-linux-androideabi21-clang
- CXX=armv7a-linux-androideabi21-clang++
- goos: android
goarch: arm64
env:
- CC=aarch64-linux-android21-clang
- CXX=aarch64-linux-android21-clang++
- goos: android
goarch: 386
env:
- CC=i686-linux-android21-clang
- CXX=i686-linux-android21-clang++
- goos: android
goarch: amd64
goamd64: v1
env:
- CC=x86_64-linux-android21-clang
- CXX=x86_64-linux-android21-clang++
targets:
- android_arm_7
- android_arm64
- android_386
- android_amd64
- id: naive-purego-linux-amd64
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_manager
- with_admin_panel
- with_profiler
- badlinkname
- tfogo_checklinkname0
- with_naive_outbound
- with_purego
env:
- CGO_ENABLED=0
- GOTOOLCHAIN=local
hooks:
post:
- cmd: go run -C {{ .Env.CRONET_GO_PATH }} ./cmd/build-naive extract-lib --target {{ .Os }}/{{ .Arch }} -o {{ dir .Path }}
env:
- CGO_ENABLED=0
targets:
- linux_amd64_v1
- id: naive-purego-linux-arm64
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_manager
- with_admin_panel
- with_profiler
- badlinkname
- tfogo_checklinkname0
- with_naive_outbound
- with_purego
env:
- CGO_ENABLED=0
- GOTOOLCHAIN=local
hooks:
post:
- cmd: go run -C {{ .Env.CRONET_GO_PATH }} ./cmd/build-naive extract-lib --target {{ .Os }}/{{ .Arch }} -o {{ dir .Path }}
env:
- CGO_ENABLED=0
targets:
- linux_arm64
- id: naive-purego-windows-amd64
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_manager
- with_admin_panel
- with_profiler
- badlinkname
- tfogo_checklinkname0
- with_naive_outbound
- with_purego
env:
- CGO_ENABLED=0
- GOTOOLCHAIN=local
hooks:
post:
- cmd: go run -C {{ .Env.CRONET_GO_PATH }} ./cmd/build-naive extract-lib --target {{ .Os }}/{{ .Arch }} -o {{ dir .Path }}
env:
- CGO_ENABLED=0
targets:
- windows_amd64_v1
- id: naive-purego-windows-arm64
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_manager
- with_admin_panel
- with_profiler
- badlinkname
- tfogo_checklinkname0
- with_naive_outbound
- with_purego
env:
- CGO_ENABLED=0
- GOTOOLCHAIN=local
hooks:
post:
- cmd: go run -C {{ .Env.CRONET_GO_PATH }} ./cmd/build-naive extract-lib --target {{ .Os }}/{{ .Arch }} -o {{ dir .Path }}
env:
- CGO_ENABLED=0
targets:
- windows_arm64
- id: naive-glibc
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_manager
- with_admin_panel
- with_profiler
- badlinkname
- tfogo_checklinkname0
- with_naive_outbound
env:
- CGO_ENABLED=1
- GOTOOLCHAIN=local
- CGO_LDFLAGS=-fuse-ld=lld -Wl,-z,notext -Wl,-z,execstack
- CC_linux_amd64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=x86_64-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_amd64_staging
- CXX_linux_amd64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=x86_64-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_amd64_staging
- CC_linux_arm64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=aarch64-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_arm64_staging
- CXX_linux_arm64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=aarch64-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_arm64_staging
- CC_linux_386={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=i686-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_i386_staging
- CXX_linux_386={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=i686-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_i386_staging
- CC_linux_arm={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=arm-linux-gnueabihf --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_armhf_staging
- CXX_linux_arm={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=arm-linux-gnueabihf --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_armhf_staging
- CC_linux_mips64le={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=mips64el-linux-gnuabi64 --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_mips64el_staging
- CXX_linux_mips64le={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=mips64el-linux-gnuabi64 --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/bullseye/bullseye_mips64el_staging
- CC_linux_riscv64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=riscv64-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/trixie/trixie_riscv64_staging
- CXX_linux_riscv64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=riscv64-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/trixie/trixie_riscv64_staging
- CC_linux_loong64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=loongarch64-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/sid/sid_loong64_staging
- CXX_linux_loong64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=loongarch64-linux-gnu --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/sid/sid_loong64_staging
- 'CC={{ index .Env (print "CC_" .Os "_" .Arch) }}'
- 'CXX={{ index .Env (print "CXX_" .Os "_" .Arch) }}'
targets:
- linux_amd64_v1
- linux_arm64
- linux_386
- linux_arm_7
- linux_riscv64
- linux_loong64
- id: naive-musl
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_manager
- with_admin_panel
- with_profiler
- badlinkname
- tfogo_checklinkname0
- with_naive_outbound
- with_musl
env:
- CGO_ENABLED=1
- GOTOOLCHAIN=local
- CGO_LDFLAGS=-fuse-ld=lld -Wl,-z,notext -Wl,-z,execstack
- CC_linux_amd64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=x86_64-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/x86_64
- CXX_linux_amd64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=x86_64-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/x86_64
- CC_linux_arm64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=aarch64-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/aarch64
- CXX_linux_arm64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=aarch64-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/aarch64
- CC_linux_386={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=i486-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/i386_pentium4
- CXX_linux_386={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=i486-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/i386_pentium4
- CC_linux_arm={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=arm-openwrt-linux-musleabi --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/arm_cortex-a15_neon-vfpv4
- CXX_linux_arm={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=arm-openwrt-linux-musleabi --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/arm_cortex-a15_neon-vfpv4
- CC_linux_riscv64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=riscv64-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/riscv64
- CXX_linux_riscv64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=riscv64-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/23.05.5/riscv64
- CC_linux_loong64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang --target=loongarch64-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/24.10.5/loongarch64
- CXX_linux_loong64={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/third_party/llvm-build/Release+Asserts/bin/clang++ --target=loongarch64-openwrt-linux-musl --sysroot={{ .Env.CRONET_GO_PATH }}/naiveproxy/src/out/sysroot-build/openwrt/24.10.5/loongarch64
- 'CC={{ index .Env (print "CC_" .Os "_" .Arch) }}'
- 'CXX={{ index .Env (print "CXX_" .Os "_" .Arch) }}'
targets:
- linux_amd64_v1
- linux_arm64
- linux_386
- linux_arm_7
- linux_riscv64
- linux_loong64
- id: compressed
<<: *template
targets:
- linux_386
- linux_amd64_v1
- linux_arm64
- linux_arm_6
- linux_arm_7
- linux_riscv64
- id: compressed-mips
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_profiler
- badlinkname
- tfogo_checklinkname0
targets:
- linux_mips
- linux_mips_softfloat
- linux_mipsle
- linux_mipsle_softfloat
- id: compressed-android
<<: *template
env:
- CGO_ENABLED=1
- GOTOOLCHAIN=local
overrides:
- goos: android
goarch: arm
goarm: 7
env:
- CC=armv7a-linux-androideabi21-clang
- CXX=armv7a-linux-androideabi21-clang++
- goos: android
goarch: arm64
env:
- CC=aarch64-linux-android21-clang
- CXX=aarch64-linux-android21-clang++
- goos: android
goarch: 386
env:
- CC=i686-linux-android21-clang
- CXX=i686-linux-android21-clang++
- goos: android
goarch: amd64
goamd64: v1
env:
- CC=x86_64-linux-android21-clang
- CXX=x86_64-linux-android21-clang++
targets:
- android_arm_7
- android_arm64
- android_386
- android_amd64
- id: openwrt
<<: *template
hooks:
post:
- cmd: bash .github/build_openwrt_packages.sh "{{ .Version }}" "{{ .Target }}" "{{ .Path }}"
targets:
- linux_amd64_v1
- linux_arm64
- linux_386
- linux_arm_7
- linux_arm_6
- linux_riscv64
- linux_loong64
- id: openwrt-mips
<<: *template
tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_utls
- with_acme
- with_clash_api
- with_tailscale
- with_masque
- with_mtproxy
- with_ccm
- with_ocm
- with_openvpn
- with_trusttunnel
- with_sudoku
- with_snell
- with_profiler
- badlinkname
- tfogo_checklinkname0
hooks:
post:
- cmd: bash .github/build_openwrt_packages.sh "{{ .Version }}" "{{ .Target }}" "{{ .Path }}"
targets:
- linux_arm_5
- linux_mips_softfloat
- linux_mips64_softfloat
- linux_mipsle_softfloat
- linux_mipsle_hardfloat
- linux_mips64le
upx:
- enabled: true
ids:
- compressed
- compressed-mips
- compressed-android
compress: best
lzma: true
archives:
- &template
id: archive
builds:
- main
- mips
- android
formats:
- tar.gz
format_overrides:
- goos: windows
formats:
- zip
wrap_in_directory: true
files:
- LICENSE
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}-{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
- id: archive-naive-purego-linux-amd64
<<: *template
builds:
- naive-purego-linux-amd64
files:
- LICENSE
- src: dist/naive-purego-linux-amd64_*/libcronet*
strip_parent: true
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-purego'
- id: archive-naive-purego-linux-arm64
<<: *template
builds:
- naive-purego-linux-arm64
files:
- LICENSE
- src: dist/naive-purego-linux-arm64_*/libcronet*
strip_parent: true
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-purego'
- id: archive-naive-purego-windows-amd64
<<: *template
builds:
- naive-purego-windows-amd64
files:
- LICENSE
- src: dist/naive-purego-windows-amd64_*/libcronet*
strip_parent: true
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-purego'
- id: archive-naive-purego-windows-arm64
<<: *template
builds:
- naive-purego-windows-arm64
files:
- LICENSE
- src: dist/naive-purego-windows-arm64_*/libcronet*
strip_parent: true
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-purego'
- id: archive-naive-glibc
<<: *template
builds:
- naive-glibc
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}-{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}-glibc'
- id: archive-naive-musl
<<: *template
builds:
- naive-musl
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}-{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}-musl'
- id: archive-compressed
<<: *template
builds:
- compressed
- compressed-mips
- compressed-android
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}-{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}-compressed'
source:
enabled: false
name_template: '{{ .ProjectName }}-{{ .Version }}.source'
prefix_template: '{{ .ProjectName }}-{{ .Version }}/'
checksum:
disable: true
name_template: '{{ .ProjectName }}-{{ .Version }}.checksum'
signs:
- artifacts: checksum
release:
github:
owner: shtorm-7
name: sing-box-extended
draft: true
prerelease: auto
mode: replace
ids:
- archive
- archive-naive-glibc
- archive-naive-musl
- archive-naive-purego-linux-amd64
- archive-naive-purego-linux-arm64
- archive-naive-purego-windows-amd64
- archive-naive-purego-windows-arm64
- archive-compressed
- archive-openwrt
skip_upload: true

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
LABEL maintainer="shtorm-7"
COPY . /go/src/github.com/sagernet/sing-box
WORKDIR /go/src/github.com/sagernet/sing-box

View File

@@ -14,37 +14,12 @@ PREFIX ?= $(shell go env GOPATH)
SING_FFI ?= sing-ffi
LIBBOX_FFI_CONFIG ?= ./experimental/libbox/ffi.json
ADMIN_PANEL_DIR = service/admin_panel
ADMIN_PANEL_WEB = $(ADMIN_PANEL_DIR)/web
ADMIN_PANEL_DIST = $(ADMIN_PANEL_DIR)/dist
ADMIN_PANEL_TAGS = $(TAGS),with_admin_panel
DOCKER_IMAGE ?= shtorm7/sing-box-extended
DOCKER_PLATFORMS ?= linux/amd64,linux/arm64
CRONET_GO_PATH ?= $(shell pwd)/cronet-go
.PHONY: test release docs build
build:
export GOTOOLCHAIN=local && \
go build $(MAIN_PARAMS) $(MAIN)
build_admin_panel:
cd $(ADMIN_PANEL_WEB) && \
npm install --no-fund --no-audit && \
npm run build
go run ./cmd/internal/admin_panel_pack \
-dir $(ADMIN_PANEL_DIST)
build_naive:
cd $(CRONET_GO_PATH) && \
for arch in amd64 arm64 386 arm mipsle mips64le riscv64 loong64; do \
go run ./cmd/build-naive --target=linux/$$arch download-toolchain; \
done && \
for arch in amd64 arm64 386 arm mipsle riscv64 loong64; do \
go run ./cmd/build-naive --target=linux/$$arch --libc=musl download-toolchain; \
done
race:
export GOTOOLCHAIN=local && \
go build -race $(MAIN_PARAMS) $(MAIN)
@@ -61,17 +36,23 @@ install:
go build -o $(PREFIX)/bin/$(NAME) $(MAIN_PARAMS) $(MAIN)
fmt:
@golangci-lint fmt
@gofumpt -l -w .
@gofmt -s -w .
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
fmt_docs:
go run ./cmd/internal/format_docs
fmt_install:
go install -v mvdan.cc/gofumpt@latest
go install -v github.com/daixiang0/gci@latest
lint:
GOOS=linux golangci-lint run ./...
GOOS=android golangci-lint run ./...
GOOS=windows golangci-lint run ./...
GOOS=darwin golangci-lint run ./...
# GOOS=freebsd golangci-lint run ./...
GOOS=freebsd golangci-lint run ./...
lint_install:
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
@@ -88,16 +69,17 @@ proto_install:
update_certificates:
go run ./cmd/internal/update_certificates
release: build_admin_panel build_naive
CRONET_GO_PATH=$(CRONET_GO_PATH) go run ./cmd/internal/build goreleaser release --skip=validate --clean -p 3 --skip publish
release:
go run ./cmd/internal/build goreleaser release --clean --skip publish
mkdir dist/release
mv dist/*.tar.gz \
dist/*.zip \
dist/*.ipk \
dist/*.apk \
dist/*.deb \
dist/*.rpm \
dist/*_amd64.pkg.tar.zst \
dist/*_arm64.pkg.tar.zst \
dist/release
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release
./codeberg-release.sh --replace --draft --prerelease -p 5 "v${VERSION}" dist/release
rm -r dist/release
release_repo:
@@ -106,15 +88,6 @@ release_repo:
release_install:
go install -v github.com/tcnksm/ghr@latest
release_docker:
sudo docker buildx build \
--platform $(DOCKER_PLATFORMS) \
-t $(DOCKER_IMAGE):latest \
-t $(DOCKER_IMAGE):$(VERSION) \
--push \
--network=host \
.
update_android_version:
go run ./cmd/internal/update_android_version
@@ -124,14 +97,11 @@ build_android:
upload_android:
mkdir -p dist/release_android
cp ../sing-box-for-android/app/build/outputs/apk/other/release/*.apk dist/release_android
VERSION_CODE=$$(grep VERSION_CODE ../sing-box-for-android/version.properties | cut -d= -f2); \
VERSION_NAME=$$(grep VERSION_NAME ../sing-box-for-android/version.properties | cut -d= -f2); \
printf '{\n "version_code": %s,\n "version_name": "%s"\n}\n' "$$VERSION_CODE" "$$VERSION_NAME" > dist/release_android/SFA-version-metadata.json
cp ../sing-box-for-android/app/build/outputs/apk/otherLegacy/release/*.apk dist/release_android
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release_android
./codeberg-release.sh --replace --draft --prerelease -p 5 "v${VERSION}" dist/release_android
rm -rf dist/release_android
release_android: build_android upload_android
release_android: lib_android update_android_version build_android upload_android
publish_android:
cd ../sing-box-for-android && ./gradlew :app:publishPlayReleaseBundle && ./gradlew --stop
@@ -204,31 +174,14 @@ upload_macos_pkg:
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Intel.pkg"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Universal.pkg"
replace_macos_pkg:
mkdir -p dist/SFM
cp ../sing-box-for-apple/build/SFM-Apple.pkg "dist/SFM/SFM-${VERSION}-Apple.pkg"
cp ../sing-box-for-apple/build/SFM-Intel.pkg "dist/SFM/SFM-${VERSION}-Intel.pkg"
cp ../sing-box-for-apple/build/SFM-Universal.pkg "dist/SFM/SFM-${VERSION}-Universal.pkg"
ghr --replace "v${VERSION}" "dist/SFM/SFM-${VERSION}-Apple.pkg"
ghr --replace "v${VERSION}" "dist/SFM/SFM-${VERSION}-Intel.pkg"
ghr --replace "v${VERSION}" "dist/SFM/SFM-${VERSION}-Universal.pkg"
upload_macos_dsyms:
mkdir -p dist/SFM
cd ../sing-box-for-apple/build/SFM.System-universal.xcarchive && zip -r SFM.dSYMs.zip dSYMs
cp ../sing-box-for-apple/build/SFM.System-universal.xcarchive/SFM.dSYMs.zip "dist/SFM/SFM-${VERSION}.dSYMs.zip"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}.dSYMs.zip"
replace_macos_dsyms:
mkdir -p dist/SFM
cd ../sing-box-for-apple/build/SFM.System-universal.xcarchive && zip -r SFM.dSYMs.zip dSYMs
cp ../sing-box-for-apple/build/SFM.System-universal.xcarchive/SFM.dSYMs.zip "dist/SFM/SFM-${VERSION}.dSYMs.zip"
ghr --replace "v${VERSION}" "dist/SFM/SFM-${VERSION}.dSYMs.zip"
release_macos_standalone: build_macos_pkg notarize_macos_pkg upload_macos_pkg upload_macos_dsyms
replace_macos_standalone: build_macos_pkg notarize_macos_pkg upload_macos_pkg upload_macos_dsyms
build_tvos:
cd ../sing-box-for-apple && \
rm -rf build/SFT.xcarchive && \

View File

@@ -1,76 +1,30 @@
<<<<<<< HEAD
# sing-box-extended
[![license](https://img.shields.io/badge/license-GPLv3-blue.svg)](LICENSE)
[![go](https://img.shields.io/badge/go-1.26-00ADD8.svg)](go.mod)
[![codeberg](https://img.shields.io/badge/mirror-codeberg-2185D0.svg)](https://codeberg.org/shtorm-7/sing-box-extended)
[![telegram](https://img.shields.io/badge/telegram-channel-26A5E4.svg)](https://t.me/sing_box_extended)
Sing-box with extended features.
## 🔥 Features
## Features
### Protocols
- **WARP** — Cloudflare WARP integration through WireGuard
- **MASQUE** — Cloudflare MASQUE proxy over QUIC / HTTP-2
- **MTProxy** — Telegram MTProxy server with FakeTLS and domain fronting
- **Mieru** — Secure, hard to classify, hard to probe network protocol
- **OpenVPN** — OpenVPN client with tls-auth, tls-crypt and tls-crypt-v2 support
- **TrustTunnel** — AdGuard's obfuscated VPN protocol, indistinguishable from HTTPS traffic
- **Sudoku** — Traffic obfuscation protocol based on 4×4 Sudoku puzzles with low-entropy fingerprints
- **SSH** — SSH client and server with certificate authentication and upstream fallback
- **VPN** — Routed tunnel over any TCP sing-box protocol
- **Bond** — Link aggregation for increasing throughput
- **Fallback** — Outbound group with priority-based switching
- **Failover** — Automatic outbound switching with session recovery for high availability
* Amnezia 1.5
* WARP
* Tunneling
* Mieru
* XHTTP
* SDNS (DNSCrypt)
* Extended Wireguard options
* Unified delay
=======
# sing-box
>>>>>>> v1.13.11
### DNS
- **SDNS (DNSCrypt)** — Encrypted DNS queries for enhanced privacy
- **DNS Fallback** — Sequential / parallel queries across upstream resolvers
### Limiters
- **Bandwidth Limiter** — Upload / download / bidirectional rate limiting
- **Connection Limiter** — Concurrent connection control
- **Traffic Limiter** — Per-user traffic quotas
- **Rate Limiter** — Request rate limiting
### Encryption & Obfuscation
- **Amnezia 2.0** — WireGuard traffic obfuscation
- **VLESS encryption** — XRAY encryption for VLESS protocol
### Transports
- **mKCP** — Reliable UDP-based transport
- **XHTTP** — Modern XRAY transport
### Services
- **Admin Panel** — Web-based management interface
- **Manager** — Management service for configuring users, nodes, limiters
- **Manager API (HTTP/gRPC)** — HTTP and gRPC API for the Manager
- **Node Manager API** — Service for connecting nodes to remote manager
### Miscellaneous
- **Providers** — Outbound subscriptions from local files, inline lists, or remote URLs (sing-box JSON, Clash YAML, SIP008, share links)
- **Link Parser** — Outbound configured from a share link (VLESS, VMess, Shadowsocks, Trojan, Hysteria, Hysteria2, TUIC)
- **Extended WireGuard options** — Advanced configuration capabilities
- **Unified Delay** — Unified latency measurement
## 📚 Examples
Configuration examples are available here:
## Examples
https://github.com/shtorm-7/sing-box-extended/tree/extended/examples
## Support the Project
## Support the project
If you want to support the project, you can donate to the following addresses.
#### Tribute
**[RUB Donate](https://web.tribute.tg/d/JxY)**
**[EUR Donate](https://web.tribute.tg/d/JxZ)**
**[USD Donate](https://web.tribute.tg/d/Jy1)**
#### TRX (Tron)
```
TSWU6VUZ4FcUghYDmbbhK15gRVvhvBgW3F

View File

@@ -48,7 +48,6 @@ type CacheFile interface {
RDRCStore
StoreWARPConfig() bool
StoreMASQUEConfig() bool
LoadMode() string
StoreMode(mode string) error
@@ -60,10 +59,6 @@ type CacheFile interface {
SaveRuleSet(tag string, set *SavedBinary) error
LoadWARPConfig(tag string) *SavedBinary
SaveWARPConfig(tag string, set *SavedBinary) error
LoadMASQUEConfig(tag string) *SavedBinary
SaveMASQUEConfig(tag string, set *SavedBinary) error
LoadSubscription(tag string) *SavedBinary
SaveSubscription(tag string, sub *SavedBinary) error
}
type SavedBinary struct {

View File

@@ -30,7 +30,6 @@ type UDPInjectableInbound interface {
type InboundRegistry interface {
option.InboundOptionsRegistry
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) (Inbound, error)
UnsafeCreate(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) (Inbound, error)
}
type InboundManager interface {
@@ -42,15 +41,16 @@ type InboundManager interface {
}
type InboundContext struct {
Inbound string
InboundType string
IPVersion uint8
Network string
Source M.Socksaddr
Destination M.Socksaddr
Gateway *netip.Addr
User string
Outbound string
Inbound string
InboundType string
IPVersion uint8
Network string
Source M.Socksaddr
Destination M.Socksaddr
TunnelSource string
TunnelDestination string
User string
Outbound string
// sniffer

View File

@@ -57,10 +57,6 @@ func (m *Registry) CreateOptions(outboundType string) (any, bool) {
func (m *Registry) Create(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
m.access.Lock()
defer m.access.Unlock()
return m.UnsafeCreate(ctx, router, logger, tag, outboundType, options)
}
func (m *Registry) UnsafeCreate(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
constructor, loaded := m.constructor[outboundType]
if !loaded {
return nil, E.New("outbound type not found: " + outboundType)

View File

@@ -35,7 +35,6 @@ type DirectRouteOutbound interface {
type OutboundRegistry interface {
option.OutboundOptionsRegistry
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
UnsafeCreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
}
type OutboundManager interface {

View File

@@ -233,7 +233,7 @@ func (m *Manager) Remove(tag string) error {
if m.defaultOutbound == outbound {
if len(m.outbounds) > 0 {
m.defaultOutbound = m.outbounds[0]
m.logger.Notice("updated default outbound to ", m.defaultOutbound.Tag())
m.logger.Info("updated default outbound to ", m.defaultOutbound.Tag())
} else {
m.defaultOutbound = nil
}
@@ -303,7 +303,7 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
if tag == m.defaultTag || (m.defaultTag == "" && m.defaultOutbound == nil) {
m.defaultOutbound = outbound
if m.started {
m.logger.Notice("updated default outbound to ", outbound.Tag())
m.logger.Info("updated default outbound to ", outbound.Tag())
}
}
return nil

View File

@@ -57,10 +57,6 @@ func (r *Registry) CreateOptions(outboundType string) (any, bool) {
func (r *Registry) CreateOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Outbound, error) {
r.access.Lock()
defer r.access.Unlock()
return r.UnsafeCreateOutbound(ctx, router, logger, tag, outboundType, options)
}
func (r *Registry) UnsafeCreateOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Outbound, error) {
constructor, loaded := r.constructors[outboundType]
if !loaded {
return nil, E.New("outbound type not found: " + outboundType)

View File

@@ -13,7 +13,6 @@ type PlatformInterface interface {
UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl(fd int) error
BindInterfaceControl(fd int, interfaceName string) error
UsePlatformInterface() bool
OpenInterface(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)

View File

@@ -1,51 +0,0 @@
package adapter
import (
"context"
"time"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/x/list"
)
type Provider interface {
Type() string
Tag() string
Outbounds() []Outbound
Outbound(tag string) (Outbound, bool)
UpdatedAt() time.Time
HealthCheck(ctx context.Context) (map[string]uint16, error)
RegisterCallback(callback ProviderUpdateCallback) *list.Element[ProviderUpdateCallback]
UnregisterCallback(element *list.Element[ProviderUpdateCallback])
}
type ProviderUpdater interface {
Update() error
}
type ProviderSubscriptionInfo interface {
SubscriptionInfo() SubscriptionInfo
}
type ProviderRegistry interface {
option.ProviderOptionsRegistry
CreateProvider(ctx context.Context, router Router, logFactory log.Factory, tag string, providerType string, options any) (Provider, error)
}
type ProviderManager interface {
Lifecycle
Providers() []Provider
Get(tag string) (Provider, bool)
Remove(tag string) error
Create(ctx context.Context, router Router, logFactory log.Factory, tag string, providerType string, options any) error
}
type SubscriptionInfo struct {
Upload int64
Download int64
Total int64
Expire int64
}
type ProviderUpdateCallback = func(tag string) error

View File

@@ -1,309 +0,0 @@
package provider
import (
"context"
"reflect"
"regexp"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/urltest"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/batch"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/x/list"
"github.com/sagernet/sing/service"
)
type Adapter struct {
ctx context.Context
outbound adapter.OutboundManager
router adapter.Router
logFactory log.Factory
logger log.ContextLogger
providerType string
providerTag string
outbounds []adapter.Outbound
outboundsByTag map[string]adapter.Outbound
ticker *time.Ticker
checking atomic.Bool
history adapter.URLTestHistoryStorage
callbackAccess sync.Mutex
callbacks list.List[adapter.ProviderUpdateCallback]
link string
enabled bool
removeEmojis bool
timeout time.Duration
interval time.Duration
}
func NewAdapter(ctx context.Context, router adapter.Router, outbound adapter.OutboundManager, logFactory log.Factory, logger log.ContextLogger, providerTag string, providerType string, options option.ProviderHealthCheckOptions) Adapter {
timeout := time.Duration(options.Timeout)
if timeout == 0 {
timeout = 3 * time.Second
}
interval := time.Duration(options.Interval)
if interval == 0 {
interval = 10 * time.Minute
}
if interval < time.Minute {
interval = time.Minute
}
return Adapter{
ctx: ctx,
outbound: outbound,
router: router,
logFactory: logFactory,
logger: logger,
providerType: providerType,
providerTag: providerTag,
enabled: options.Enabled,
link: options.URL,
timeout: timeout,
interval: interval,
}
}
func (a *Adapter) SetRemoveEmojis(remove bool) {
a.removeEmojis = remove
}
func (a *Adapter) Start() error {
a.history = service.FromContext[adapter.URLTestHistoryStorage](a.ctx)
if a.history == nil {
if clashServer := service.FromContext[adapter.ClashServer](a.ctx); clashServer != nil {
a.history = clashServer.HistoryStorage()
} else {
a.history = urltest.NewHistoryStorage()
}
}
go a.loopCheck()
return nil
}
func (a *Adapter) Type() string {
return a.providerType
}
func (a *Adapter) Tag() string {
return a.providerTag
}
func (a *Adapter) Outbounds() []adapter.Outbound {
return a.outbounds
}
func (a *Adapter) Outbound(tag string) (adapter.Outbound, bool) {
if a.outboundsByTag == nil {
return nil, false
}
detour, ok := a.outboundsByTag[tag]
return detour, ok
}
func (a *Adapter) UpdateOutbounds(oldOpts []option.Outbound, newOpts []option.Outbound) {
if a.removeEmojis {
removeEmojisFromTags(newOpts)
}
uniquifyTags(newOpts)
a.removeUseless(newOpts)
var (
oldOptByTag = make(map[string]option.Outbound)
outbounds = make([]adapter.Outbound, 0, len(newOpts))
outboundsByTag = make(map[string]adapter.Outbound)
)
for _, opt := range oldOpts {
oldOptByTag[opt.Tag] = opt
}
for i, opt := range newOpts {
var tag string
if opt.Tag != "" {
tag = F.ToString(a.providerTag, "/", opt.Tag)
} else {
tag = F.ToString(a.providerTag, "/", i)
}
outbound, exist := a.outbound.Outbound(tag)
if !exist || !reflect.DeepEqual(opt, oldOptByTag[opt.Tag]) {
err := a.outbound.Create(
adapter.WithContext(a.ctx, &adapter.InboundContext{
Outbound: tag,
}),
a.router,
a.logFactory.NewLogger(F.ToString("outbound/", opt.Type, "[", tag, "]")),
tag,
opt.Type,
opt.Options,
)
if err != nil {
a.logger.Warn(err, " in ", tag, ", skip create this outbound")
continue
}
outbound, _ = a.outbound.Outbound(tag)
}
outbounds = append(outbounds, outbound)
outboundsByTag[tag] = outbound
}
if a.enabled && a.history != nil {
go a.HealthCheck(a.ctx)
}
a.outbounds = outbounds
a.outboundsByTag = outboundsByTag
}
func (a *Adapter) HealthCheck(ctx context.Context) (map[string]uint16, error) {
if a.ticker != nil {
a.ticker.Reset(a.interval)
}
return a.healthcheck(ctx)
}
func (a *Adapter) RegisterCallback(callback adapter.ProviderUpdateCallback) *list.Element[adapter.ProviderUpdateCallback] {
a.callbackAccess.Lock()
defer a.callbackAccess.Unlock()
return a.callbacks.PushBack(callback)
}
func (a *Adapter) UnregisterCallback(element *list.Element[adapter.ProviderUpdateCallback]) {
a.callbackAccess.Lock()
defer a.callbackAccess.Unlock()
a.callbacks.Remove(element)
}
func (a *Adapter) UpdateGroups() {
for element := a.callbacks.Front(); element != nil; element = element.Next() {
element.Value(a.providerTag)
}
}
func (a *Adapter) Close() error {
if a.ticker != nil {
a.ticker.Stop()
}
outbounds := a.outbounds
a.outbounds = nil
var err error
for _, ob := range outbounds {
if err2 := a.outbound.Remove(ob.Tag()); err2 != nil {
err = E.Append(err, err2, func(err error) error {
return E.Cause(err, "close outbound [", ob.Tag(), "]")
})
}
}
return err
}
func (a *Adapter) loopCheck() {
if !a.enabled {
return
}
a.ticker = time.NewTicker(a.interval)
a.healthcheck(a.ctx)
for {
select {
case <-a.ctx.Done():
return
case <-a.ticker.C:
a.healthcheck(a.ctx)
}
}
}
func (a *Adapter) healthcheck(ctx context.Context) (map[string]uint16, error) {
result := make(map[string]uint16)
if a.checking.Swap(true) {
return result, nil
}
defer a.checking.Store(false)
b, _ := batch.New(ctx, batch.WithConcurrencyNum[any](10))
var resultAccess sync.Mutex
checked := make(map[string]bool)
for _, detour := range a.outbounds {
tag := detour.Tag()
if checked[tag] {
continue
}
checked[tag] = true
b.Go(tag, func() (any, error) {
ctx, cancel := context.WithTimeout(a.ctx, a.timeout)
defer cancel()
t, err := urltest.URLTest(ctx, a.link, detour)
if err != nil {
a.logger.Debug("outbound ", tag, " unavailable: ", err)
a.history.DeleteURLTestHistory(tag)
} else {
a.logger.Debug("outbound ", tag, " available: ", t, "ms")
a.history.StoreURLTestHistory(tag, &adapter.URLTestHistory{
Time: time.Now(),
Delay: t,
})
resultAccess.Lock()
result[tag] = t
resultAccess.Unlock()
}
return nil, nil
})
}
b.Wait()
return result, nil
}
func (a *Adapter) removeUseless(newOpts []option.Outbound) {
if len(a.outbounds) == 0 {
return
}
exists := make(map[string]bool)
for i, opt := range newOpts {
var tag string
if opt.Tag != "" {
tag = F.ToString(a.providerTag, "/", opt.Tag)
} else {
tag = F.ToString(a.providerTag, "/", i)
}
exists[tag] = true
}
for _, opt := range a.outbounds {
if !exists[opt.Tag()] {
if err := a.outbound.Remove(opt.Tag()); err != nil {
a.logger.Error(err, "close outbound [", opt.Tag(), "]")
}
}
}
}
func uniquifyTags(opts []option.Outbound) {
count := make(map[string]int)
for i, opt := range opts {
count[opt.Tag]++
if count[opt.Tag] > 1 {
opts[i].Tag = F.ToString(opt.Tag, " #", count[opt.Tag])
}
}
}
func removeEmojisFromTags(opts []option.Outbound) {
for i, opt := range opts {
cleaned := flagRegex.ReplaceAllStringFunc(opt.Tag, flagToCountryCode)
cleaned = emojiRegex.ReplaceAllString(cleaned, "")
cleaned = multiSpaceRegex.ReplaceAllString(cleaned, " ")
opts[i].Tag = strings.TrimSpace(cleaned)
}
}
func flagToCountryCode(flag string) string {
runes := []rune(flag)
if len(runes) == 2 {
return string(rune(runes[0]-0x1F1E6+'A')) + string(rune(runes[1]-0x1F1E6+'A')) + " "
}
return ""
}
var flagRegex = regexp.MustCompile(`[\x{1F1E6}-\x{1F1FF}]{2}`)
var emojiRegex = regexp.MustCompile(`[\x{1F1E0}-\x{1F1FF}\x{1F300}-\x{1F9FF}\x{2600}-\x{27BF}\x{FE00}-\x{FE0F}\x{200D}]+`)
var multiSpaceRegex = regexp.MustCompile(`\s{2,}`)

View File

@@ -1,45 +0,0 @@
package provider
import (
"testing"
"github.com/sagernet/sing-box/option"
)
func TestFlagToCountryCodeAllFlags(t *testing.T) {
for first := 'A'; first <= 'Z'; first++ {
for second := 'A'; second <= 'Z'; second++ {
flag := string(rune(0x1F1E6+(first-'A'))) + string(rune(0x1F1E6+(second-'A')))
expected := string(first) + string(second)
result := flagToCountryCode(flag)
// flagToCountryCode appends a space
if result != expected+" " {
t.Errorf("flagToCountryCode(%q) = %q, want %q", expected, result, expected+" ")
}
}
}
}
func TestRemoveEmojisFromTags(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"🇺🇸 United States", "US United States"},
{"🇷🇺 Россия", "RU Россия"},
{"🇩🇪 Germany 🚀", "DE Germany"},
{"🇫🇷🇬🇧 France-UK", "FR GB France-UK"},
{"No emojis here", "No emojis here"},
{"🌍 World", "World"},
{"🇯🇵 Tokyo ⚡ Fast", "JP Tokyo Fast"},
{"Germany 🇩🇪", "Germany DE"},
{"Server 🇺🇸 Node", "Server US Node"},
}
for _, tt := range tests {
opts := []option.Outbound{{Tag: tt.input}}
removeEmojisFromTags(opts)
if opts[0].Tag != tt.expected {
t.Errorf("removeEmojisFromTags(%q) = %q, want %q", tt.input, opts[0].Tag, tt.expected)
}
}
}

View File

@@ -1,157 +0,0 @@
package provider
import (
"context"
"io"
"os"
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/taskmonitor"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
)
var _ adapter.ProviderManager = (*Manager)(nil)
type Manager struct {
logger log.ContextLogger
registry adapter.ProviderRegistry
access sync.Mutex
started bool
stage adapter.StartStage
providers []adapter.Provider
providerByTag map[string]adapter.Provider
wg sync.WaitGroup
}
func NewManager(logger logger.ContextLogger, registry adapter.ProviderRegistry) *Manager {
return &Manager{
logger: logger,
registry: registry,
providerByTag: make(map[string]adapter.Provider),
}
}
func (m *Manager) Initialize() {
}
func (m *Manager) Start(stage adapter.StartStage) error {
m.access.Lock()
if m.started && m.stage >= stage {
panic("already started")
}
m.started = true
m.stage = stage
providers := m.providers
m.access.Unlock()
for _, provider := range providers {
err := adapter.LegacyStart(provider, stage)
if err != nil {
return E.Cause(err, stage, " provider/", provider.Type(), "[", provider.Tag(), "]")
}
}
return nil
}
func (m *Manager) Close() error {
monitor := taskmonitor.New(m.logger, C.StopTimeout)
m.access.Lock()
if !m.started {
m.access.Unlock()
return nil
}
m.started = false
providers := m.providers
m.providers = nil
m.access.Unlock()
var err error
for _, provider := range providers {
if closer, isCloser := provider.(io.Closer); isCloser {
monitor.Start("close provider/", provider.Type(), "[", provider.Tag(), "]")
err = E.Append(err, closer.Close(), func(err error) error {
return E.Cause(err, "close provider/", provider.Type(), "[", provider.Tag(), "]")
})
monitor.Finish()
}
}
return nil
}
func (m *Manager) Providers() []adapter.Provider {
m.access.Lock()
defer m.access.Unlock()
return m.providers
}
func (m *Manager) Get(tag string) (adapter.Provider, bool) {
m.access.Lock()
provider, found := m.providerByTag[tag]
m.access.Unlock()
return provider, found
}
func (m *Manager) Remove(tag string) error {
m.access.Lock()
provider, found := m.providerByTag[tag]
if !found {
m.access.Unlock()
return os.ErrInvalid
}
delete(m.providerByTag, tag)
index := common.Index(m.providers, func(it adapter.Provider) bool {
return it == provider
})
if index == -1 {
panic("invalid provider index")
}
m.providers = append(m.providers[:index], m.providers[index+1:]...)
started := m.started
m.access.Unlock()
if started {
return common.Close(provider)
}
return nil
}
func (m *Manager) Create(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, providerType string, options any) error {
if tag == "" {
return os.ErrInvalid
}
provider, err := m.registry.CreateProvider(ctx, router, logFactory, tag, providerType, options)
if err != nil {
return err
}
m.access.Lock()
defer m.access.Unlock()
if m.started {
for _, stage := range adapter.ListStartStages {
err = adapter.LegacyStart(provider, stage)
if err != nil {
return E.Cause(err, stage, " provider/", provider.Type(), "[", provider.Tag(), "]")
}
}
}
if existsProvider, loaded := m.providerByTag[tag]; loaded {
if m.started {
err = common.Close(existsProvider)
if err != nil {
return E.Cause(err, "close provider", provider.Type(), "[", existsProvider.Tag(), "]")
}
}
existsIndex := common.Index(m.providers, func(it adapter.Provider) bool {
return it == existsProvider
})
if existsIndex == -1 {
panic("invalid provider index")
}
m.providers = append(m.providers[:existsIndex], m.providers[existsIndex+1:]...)
}
m.providers = append(m.providers, provider)
m.providerByTag[tag] = provider
return nil
}

View File

@@ -1,72 +0,0 @@
package provider
import (
"context"
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
)
type ConstructorFunc[T any] func(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, options T) (adapter.Provider, error)
func Register[Options any](registry *Registry, providerType string, constructor ConstructorFunc[Options]) {
registry.register(providerType, func() any {
return new(Options)
}, func(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, rawOptions any) (adapter.Provider, error) {
var options *Options
if rawOptions != nil {
options = rawOptions.(*Options)
}
return constructor(ctx, router, logFactory, tag, common.PtrValueOrDefault(options))
})
}
var _ adapter.ProviderRegistry = (*Registry)(nil)
type (
optionsConstructorFunc func() any
constructorFunc func(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, options any) (adapter.Provider, error)
)
type Registry struct {
access sync.Mutex
optionsType map[string]optionsConstructorFunc
constructors map[string]constructorFunc
}
func NewRegistry() *Registry {
return &Registry{
optionsType: make(map[string]optionsConstructorFunc),
constructors: make(map[string]constructorFunc),
}
}
func (r *Registry) CreateOptions(providerType string) (any, bool) {
r.access.Lock()
defer r.access.Unlock()
optionsConstructor, loaded := r.optionsType[providerType]
if !loaded {
return nil, false
}
return optionsConstructor(), true
}
func (r *Registry) CreateProvider(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, providerType string, options any) (adapter.Provider, error) {
r.access.Lock()
defer r.access.Unlock()
constructor, loaded := r.constructors[providerType]
if !loaded {
return nil, E.New("provider type not found: '" + providerType + "'")
}
return constructor(ctx, router, logFactory, tag, options)
}
func (r *Registry) register(providerType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
r.access.Lock()
defer r.access.Unlock()
r.optionsType[providerType] = optionsConstructor
r.constructors[providerType] = constructor
}

52
box.go
View File

@@ -12,7 +12,6 @@ import (
"github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/adapter/inbound"
"github.com/sagernet/sing-box/adapter/outbound"
"github.com/sagernet/sing-box/adapter/provider"
boxService "github.com/sagernet/sing-box/adapter/service"
"github.com/sagernet/sing-box/common/certificate"
"github.com/sagernet/sing-box/common/dialer"
@@ -45,7 +44,6 @@ type Box struct {
endpoint *endpoint.Manager
inbound *inbound.Manager
outbound *outbound.Manager
provider *provider.Manager
service *boxService.Manager
dnsTransport *dns.TransportManager
dnsRouter *dns.Router
@@ -66,7 +64,6 @@ func Context(
inboundRegistry adapter.InboundRegistry,
outboundRegistry adapter.OutboundRegistry,
endpointRegistry adapter.EndpointRegistry,
providerRegistry adapter.ProviderRegistry,
dnsTransportRegistry adapter.DNSTransportRegistry,
serviceRegistry adapter.ServiceRegistry,
) context.Context {
@@ -85,11 +82,6 @@ func Context(
ctx = service.ContextWith[option.EndpointOptionsRegistry](ctx, endpointRegistry)
ctx = service.ContextWith[adapter.EndpointRegistry](ctx, endpointRegistry)
}
if service.FromContext[option.ProviderOptionsRegistry](ctx) == nil ||
service.FromContext[adapter.ProviderRegistry](ctx) == nil {
ctx = service.ContextWith[option.ProviderOptionsRegistry](ctx, providerRegistry)
ctx = service.ContextWith[adapter.ProviderRegistry](ctx, providerRegistry)
}
if service.FromContext[adapter.DNSTransportRegistry](ctx) == nil {
ctx = service.ContextWith[option.DNSTransportOptionsRegistry](ctx, dnsTransportRegistry)
ctx = service.ContextWith[adapter.DNSTransportRegistry](ctx, dnsTransportRegistry)
@@ -112,7 +104,6 @@ func New(options Options) (*Box, error) {
endpointRegistry := service.FromContext[adapter.EndpointRegistry](ctx)
inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx)
outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx)
providerRegistry := service.FromContext[adapter.ProviderRegistry](ctx)
dnsTransportRegistry := service.FromContext[adapter.DNSTransportRegistry](ctx)
serviceRegistry := service.FromContext[adapter.ServiceRegistry](ctx)
@@ -125,9 +116,6 @@ func New(options Options) (*Box, error) {
if outboundRegistry == nil {
return nil, E.New("missing outbound registry in context")
}
if providerRegistry == nil {
return nil, E.New("missing provider registry in context")
}
if dnsTransportRegistry == nil {
return nil, E.New("missing DNS transport registry in context")
}
@@ -172,7 +160,6 @@ func New(options Options) (*Box, error) {
if err != nil {
return nil, E.Cause(err, "create log factory")
}
service.MustRegister[log.Factory](ctx, logFactory)
var internalServices []adapter.LifecycleService
certificateOptions := common.PtrValueOrDefault(options.Certificate)
@@ -193,13 +180,11 @@ func New(options Options) (*Box, error) {
endpointManager := endpoint.NewManager(logFactory.NewLogger("endpoint"), endpointRegistry)
inboundManager := inbound.NewManager(logFactory.NewLogger("inbound"), inboundRegistry, endpointManager)
outboundManager := outbound.NewManager(logFactory.NewLogger("outbound"), outboundRegistry, endpointManager, routeOptions.Final)
providerManager := provider.NewManager(logFactory.NewLogger("provider"), providerRegistry)
dnsTransportManager := dns.NewTransportManager(logFactory.NewLogger("dns/transport"), dnsTransportRegistry, outboundManager, dnsOptions.Final)
serviceManager := boxService.NewManager(logFactory.NewLogger("service"), serviceRegistry)
service.MustRegister[adapter.EndpointManager](ctx, endpointManager)
service.MustRegister[adapter.InboundManager](ctx, inboundManager)
service.MustRegister[adapter.OutboundManager](ctx, outboundManager)
service.MustRegister[adapter.ProviderManager](ctx, providerManager)
service.MustRegister[adapter.DNSTransportManager](ctx, dnsTransportManager)
service.MustRegister[adapter.ServiceManager](ctx, serviceManager)
dnsRouter := dns.NewRouter(ctx, logFactory, dnsOptions)
@@ -290,10 +275,6 @@ func New(options Options) (*Box, error) {
return nil, E.Cause(err, "initialize inbound[", i, "]")
}
}
options.Outbounds = append(options.Outbounds, option.Outbound{
Tag: "Compatible",
Type: C.TypeDirect,
})
for i, outboundOptions := range options.Outbounds {
var tag string
if outboundOptions.Tag != "" {
@@ -320,25 +301,6 @@ func New(options Options) (*Box, error) {
return nil, E.Cause(err, "initialize outbound[", i, "]")
}
}
for i, providerOptions := range options.Providers {
var tag string
if providerOptions.Tag != "" {
tag = providerOptions.Tag
} else {
tag = F.ToString(i)
}
err = providerManager.Create(
ctx,
router,
logFactory,
tag,
providerOptions.Type,
providerOptions.Options,
)
if err != nil {
return nil, E.Cause(err, "initialize provider[", i, "]")
}
}
for i, serviceOptions := range options.Services {
var tag string
if serviceOptions.Tag != "" {
@@ -429,7 +391,6 @@ func New(options Options) (*Box, error) {
endpoint: endpointManager,
inbound: inboundManager,
outbound: outboundManager,
provider: providerManager,
dnsTransport: dnsTransportManager,
service: serviceManager,
dnsRouter: dnsRouter,
@@ -458,7 +419,7 @@ func (s *Box) PreStart() error {
s.Close()
return err
}
s.logger.Notice("sing-box pre-started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
s.logger.Info("sing-box pre-started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
return nil
}
@@ -477,7 +438,7 @@ func (s *Box) Start() error {
s.Close()
return err
}
s.logger.Notice("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
return nil
}
@@ -493,11 +454,11 @@ func (s *Box) preStart() error {
if err != nil {
return err
}
err = adapter.Start(s.logger, adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.provider, s.service)
err = adapter.Start(s.logger, adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
if err != nil {
return err
}
err = adapter.Start(s.logger, adapter.StartStateStart, s.outbound, s.provider, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
err = adapter.Start(s.logger, adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
if err != nil {
return err
}
@@ -517,7 +478,7 @@ func (s *Box) start() error {
if err != nil {
return err
}
err = adapter.Start(s.logger, adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint, s.provider, s.service)
err = adapter.Start(s.logger, adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint, s.service)
if err != nil {
return err
}
@@ -525,7 +486,7 @@ func (s *Box) start() error {
if err != nil {
return err
}
err = adapter.Start(s.logger, adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.provider, s.service)
err = adapter.Start(s.logger, adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
if err != nil {
return err
}
@@ -551,7 +512,6 @@ func (s *Box) Close() error {
{"service", s.service},
{"endpoint", s.endpoint},
{"inbound", s.inbound},
{"provider", s.provider},
{"outbound", s.outbound},
{"router", s.router},
{"connection", s.connection},

View File

@@ -1,194 +0,0 @@
// Command admin_panel_pack post-processes a directory of built SPA
// assets so it can be served straight from the Go binary via //go:embed.
// It does *not* generate any Go source any more — that responsibility
// moved to the embed directive in service/admin_panel/service.go.
//
// Three transformations happen here, all in-place inside the supplied
// directory:
//
// 1. Legacy WOFF (1.0) fallback fonts are deleted. Every browser made
// after 2014 reads WOFF2 natively, so shipping both formats roughly
// doubles the embedded font payload for no real-world benefit. The
// matching `,url(*.woff) format("woff")` segments are stripped from
// the bundled CSS in step (2) so the @font-face rules don't reference
// files that aren't shipped.
// 2. Bundled CSS is rewritten to drop those WOFF URL fragments.
// 3. Compressible text assets (.html, .css, .js, .svg, .json, .map) are
// pre-gzipped as companion `*.gz` files. The HTTP handler then either
// passes those bytes through verbatim with Content-Encoding: gzip or
// falls back to the raw file for the rare client that does not
// advertise gzip support — no on-line compression, no surprises.
// Already-compressed formats (.woff2, fonts, images) are skipped: gzip
// can't shrink them and the duplicate would only inflate the binary.
package main
import (
"bytes"
"compress/gzip"
"flag"
"fmt"
"io/fs"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)
func main() {
dir := flag.String("dir", "service/admin_panel/dist", "directory of built SPA assets to post-process in place")
flag.Parse()
woffDropped, err := pruneWoff(*dir)
if err != nil {
fail("prune woff: %v", err)
}
cssRewritten, err := rewriteCSS(*dir)
if err != nil {
fail("rewrite css: %v", err)
}
stats, err := gzipText(*dir)
if err != nil {
fail("gzip text: %v", err)
}
fmt.Fprintf(os.Stderr, "post-processed %s: dropped %d woff, rewrote %d css, gzipped %d files (%d→%d bytes)\n",
*dir, woffDropped, cssRewritten, stats.gzipped, stats.totalRaw, stats.totalGz)
}
// pruneWoff deletes every legacy *.woff (WOFF 1.0) font under dir. The
// bundled CSS still references them on entry; rewriteCSS drops those
// references in a separate pass so the two operations stay independently
// testable.
func pruneWoff(dir string) (int, error) {
var n int
err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if d.IsDir() || !strings.EqualFold(filepath.Ext(p), ".woff") {
return nil
}
if err := os.Remove(p); err != nil {
return err
}
n++
return nil
})
return n, err
}
// woffRefAfterRE / woffRefBeforeRE match a single WOFF 1.0 entry inside a
// Vite-bundled CSS `src:` declaration. Vite minifies the rule to
// `src:url(./X.woff2) format("woff2"),url(./X.woff) format("woff");` so the
// "after" regex (the common case) eats the *leading* comma + woff entry,
// leaving only the woff2 source. We also handle the rare reverse ordering
// in a second pass.
var (
woffRefAfterRE = regexp.MustCompile(`,\s*url\([^)]*\.woff\)\s*format\(["']woff["']\)`)
woffRefBeforeRE = regexp.MustCompile(`url\([^)]*\.woff\)\s*format\(["']woff["']\)\s*,\s*`)
)
// rewriteCSS drops every reference to a *.woff URL from every *.css file
// under dir. Pairs naturally with pruneWoff: after both passes, no font
// URL in the bundle points at a file that isn't shipped.
func rewriteCSS(dir string) (int, error) {
var n int
err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if d.IsDir() || !strings.EqualFold(filepath.Ext(p), ".css") {
return nil
}
data, err := os.ReadFile(p)
if err != nil {
return err
}
out := woffRefAfterRE.ReplaceAll(data, nil)
out = woffRefBeforeRE.ReplaceAll(out, nil)
if bytes.Equal(out, data) {
return nil
}
if err := os.WriteFile(p, out, 0o644); err != nil {
return err
}
n++
return nil
})
return n, err
}
// gzipExts is the set of file extensions for which a `.gz` companion is
// generated. Anything not on this list is left alone — woff2/png/jpeg/etc.
// are already compressed, so gzip can only inflate them slightly while
// doubling the embedded payload.
var gzipExts = map[string]bool{
".html": true,
".css": true,
".js": true,
".mjs": true,
".svg": true,
".json": true,
".map": true,
".txt": true,
".xml": true,
".wasm": true,
}
type gzipStats struct {
gzipped int
totalRaw int64
totalGz int64
}
// gzipText produces a `<file>.gz` companion next to every text-like asset
// in dir, using gzip.BestCompression. The companion is dropped if the
// compressed bytes don't save at least 10 % over the raw file — same
// heuristic we used in the previous (Go-source-emitting) generation, just
// applied to disk files now.
func gzipText(dir string) (gzipStats, error) {
var stats gzipStats
err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if d.IsDir() {
return nil
}
ext := strings.ToLower(filepath.Ext(p))
if ext == ".gz" || !gzipExts[ext] {
return nil
}
raw, err := os.ReadFile(p)
if err != nil {
return err
}
var buf bytes.Buffer
w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression)
if err != nil {
return err
}
// Reproducible: no mtime, no OS marker.
w.ModTime = time.Time{}
w.OS = 0xff
if _, err := w.Write(raw); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
if buf.Len() > len(raw)*9/10 {
return nil
}
stats.gzipped++
stats.totalRaw += int64(len(raw))
stats.totalGz += int64(buf.Len())
return os.WriteFile(p+".gz", buf.Bytes(), 0o644)
})
return stats, err
}
func fail(format string, args ...any) {
fmt.Fprintf(os.Stderr, "admin_panel_pack: "+format+"\n", args...)
os.Exit(1)
}

View File

@@ -63,7 +63,7 @@ func init() {
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0")
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0")
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_masque", "with_mtproxy", "with_trusttunnel", "with_openvpn", "with_sudoku", "with_snell", "with_utls", "with_naive_outbound", "with_clash_api", "badlinkname", "tfogo_checklinkname0")
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "badlinkname", "tfogo_checklinkname0")
darwinTags = append(darwinTags, "with_dhcp", "grpcnotrace")
// memcTags = append(memcTags, "with_tailscale")
sharedTags = append(sharedTags, "with_tailscale", "ts_omit_logtail", "ts_omit_ssh", "ts_omit_drive", "ts_omit_taildrop", "ts_omit_webclient", "ts_omit_doctor", "ts_omit_capture", "ts_omit_kube", "ts_omit_aws", "ts_omit_synology", "ts_omit_bird")

View File

@@ -48,8 +48,8 @@ func GetRuntimeEnv(key string) (string, error) {
if readErr != nil {
return "", readErr
}
envStrings := strings.SplitSeq(string(data), "\n")
for envItem := range envStrings {
envStrings := strings.Split(string(data), "\n")
for _, envItem := range envStrings {
envItem = strings.TrimSuffix(envItem, "\r")
envKeyValue := strings.Split(envItem, "=")
if strings.EqualFold(strings.TrimSpace(envKeyValue[0]), key) {

View File

@@ -39,7 +39,7 @@ func main() {
common.Must(os.Chdir(androidPath))
localProps := common.Must1(os.ReadFile("version.properties"))
var propsList [][]string
for propLine := range strings.SplitSeq(string(localProps), "\n") {
for _, propLine := range strings.Split(string(localProps), "\n") {
propsList = append(propsList, strings.Split(propLine, "="))
}
var (

View File

@@ -45,8 +45,10 @@ package certificate
import "crypto/x509"
func newMozillaIncluded() *x509.CertPool {
pool := x509.NewCertPool()
var mozillaIncluded *x509.CertPool
func init() {
mozillaIncluded = x509.NewCertPool()
`)
for {
record, err := reader.Read()
@@ -61,14 +63,14 @@ func newMozillaIncluded() *x509.CertPool {
generated.WriteString("\n // ")
generated.WriteString(record[nameIndex])
generated.WriteString("\n")
generated.WriteString(" pool.AppendCertsFromPEM([]byte(`")
generated.WriteString(" mozillaIncluded.AppendCertsFromPEM([]byte(`")
cert := record[certIndex]
// Remove single quotes
cert = cert[1 : len(cert)-1]
generated.WriteString(cert)
generated.WriteString("`))\n")
}
generated.WriteString("\treturn pool\n}\n")
generated.WriteString("}\n")
return os.WriteFile("common/certificate/mozilla.go", []byte(generated.String()), 0o644)
}
@@ -129,8 +131,10 @@ package certificate
import "crypto/x509"
func newChromeIncluded() *x509.CertPool {
pool := x509.NewCertPool()
var chromeIncluded *x509.CertPool
func init() {
chromeIncluded = x509.NewCertPool()
`)
for {
record, err := reader.Read()
@@ -148,7 +152,7 @@ func newChromeIncluded() *x509.CertPool {
generated.WriteString("\n // ")
generated.WriteString(record[subjectIndex])
generated.WriteString("\n")
generated.WriteString(" pool.AppendCertsFromPEM([]byte(`")
generated.WriteString(" chromeIncluded.AppendCertsFromPEM([]byte(`")
cert := record[certIndex]
// Remove single quotes if present
if len(cert) > 0 && cert[0] == '\'' {
@@ -157,6 +161,6 @@ func newChromeIncluded() *x509.CertPool {
generated.WriteString(cert)
generated.WriteString("`))\n")
}
generated.WriteString("\treturn pool\n}\n")
generated.WriteString("}\n")
return os.WriteFile("common/certificate/chrome.go", []byte(generated.String()), 0o644)
}

View File

@@ -61,17 +61,16 @@ func geoipExport(countryCode string) error {
outputFile *os.File
outputWriter io.Writer
)
switch flagGeoipExportOutput {
case "stdout":
if flagGeoipExportOutput == "stdout" {
outputWriter = os.Stdout
case flagGeoipExportDefaultOutput:
} else if flagGeoipExportOutput == flagGeoipExportDefaultOutput {
outputFile, err = os.Create("geoip-" + countryCode + ".json")
if err != nil {
return err
}
defer outputFile.Close()
outputWriter = outputFile
default:
} else {
outputFile, err = os.Create(flagGeoipExportOutput)
if err != nil {
return err

View File

@@ -43,17 +43,16 @@ func geositeExport(category string) error {
outputFile *os.File
outputWriter io.Writer
)
switch commandGeositeExportOutput {
case "stdout":
if commandGeositeExportOutput == "stdout" {
outputWriter = os.Stdout
case commandGeositeExportDefaultOutput:
} else if commandGeositeExportOutput == commandGeositeExportDefaultOutput {
outputFile, err = os.Create("geosite-" + category + ".json")
if err != nil {
return err
}
defer outputFile.Close()
outputWriter = outputFile
default:
} else {
outputFile, err = os.Create(commandGeositeExportOutput)
if err != nil {
return err

View File

@@ -179,7 +179,6 @@ func run() error {
for {
osSignal := <-osSignals
if osSignal == syscall.SIGHUP {
log.Notice("received SIGHUP, reloading...")
err = check()
if err != nil {
log.Error(E.Cause(err, "reload service"))

View File

@@ -1,78 +0,0 @@
#!/bin/bash
set -euo pipefail
# Usage: ./codeberg-release.sh [--replace] [--draft] [--prerelease] [-p N] TAG PATH
OWNER="shtorm-7"
REPO="sing-box-extended"
API="https://codeberg.org/api/v1"
TOKEN="${CODEBERG_TOKEN:?Set CODEBERG_TOKEN}"
REPLACE=false
DRAFT=false
PRERELEASE=false
PARALLEL=1
while [[ $# -gt 2 ]]; do
case "$1" in
--replace) REPLACE=true; shift ;;
--draft) DRAFT=true; shift ;;
--prerelease) PRERELEASE=true; shift ;;
-p) PARALLEL="$2"; shift 2 ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
TAG="$1"
DIR="$2"
if [[ ! -d "$DIR" ]]; then
echo "Error: $DIR is not a directory"
exit 1
fi
RELEASE_URL="$API/repos/$OWNER/$REPO/releases"
RELEASE_ID=$(curl -s -H "Authorization: token $TOKEN" "$RELEASE_URL/tags/$TAG" | jq -r '.id // empty')
if [[ -n "$RELEASE_ID" && "$REPLACE" == "true" ]]; then
curl -s -H "Authorization: token $TOKEN" "$RELEASE_URL/$RELEASE_ID/assets" | \
jq -r '.[].id' | while read -r aid; do
curl -s -X DELETE -H "Authorization: token $TOKEN" "$RELEASE_URL/$RELEASE_ID/assets/$aid" >/dev/null
done
curl -s -X PATCH -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
"$RELEASE_URL/$RELEASE_ID" \
-d "{\"draft\":$DRAFT,\"prerelease\":$PRERELEASE}" >/dev/null
elif [[ -z "$RELEASE_ID" ]]; then
RELEASE_ID=$(curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
"$RELEASE_URL" \
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"draft\":$DRAFT,\"prerelease\":$PRERELEASE}" | jq -r '.id')
fi
if [[ -z "$RELEASE_ID" || "$RELEASE_ID" == "null" ]]; then
echo "Error: failed to get or create release"
exit 1
fi
echo "Release ID: $RELEASE_ID"
echo "Uploading files from $DIR (parallelism: $PARALLEL)..."
export TOKEN RELEASE_URL RELEASE_ID
upload() {
local file="$1"
local name
name=$(basename "$file")
if curl -s -X POST \
-H "Authorization: token $TOKEN" \
"$RELEASE_URL/$RELEASE_ID/assets?name=$name" \
-F "attachment=@$file" >/dev/null; then
echo "$name"
else
echo "$name"
fi
}
export -f upload
find "$DIR" -maxdepth 1 -type f -print0 | xargs -0 -P "$PARALLEL" -I {} bash -c 'upload "$@"' _ {}
echo "Done."

View File

@@ -29,7 +29,7 @@ type RawHalfConn struct {
func NewRawHalfConn(rawHalfConn reflect.Value, methods *Methods) (*RawHalfConn, error) {
halfConn := &RawHalfConn{
pointer: unsafe.Pointer(rawHalfConn.UnsafeAddr()),
pointer: (unsafe.Pointer)(rawHalfConn.UnsafeAddr()),
methods: methods,
}

View File

@@ -112,7 +112,9 @@ func IsValid(versionName string) bool {
}
func Parse(versionName string) (version Version) {
versionName = strings.TrimPrefix(versionName, "v")
if strings.HasPrefix(versionName, "v") {
versionName = versionName[1:]
}
if strings.Contains(versionName, "-") {
parts := strings.Split(versionName, "-")
versionName = parts[0]

View File

@@ -4,11 +4,13 @@ package certificate
import "crypto/x509"
func newChromeIncluded() *x509.CertPool {
pool := x509.NewCertPool()
var chromeIncluded *x509.CertPool
func init() {
chromeIncluded = x509.NewCertPool()
// CN=Actalis Authentication Root CA; O=Actalis S.p.A./03358520967; L=Milan; C=IT
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE
BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w
MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
@@ -43,7 +45,7 @@ LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
-----END CERTIFICATE-----`))
// CN=TunTrust Root CA; O=Agence Nationale de Certification Electronique; C=TN
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL
BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg
Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv
@@ -78,7 +80,7 @@ d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o=
-----END CERTIFICATE-----`))
// CN=Amazon Root CA 4; O=Amazon; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
@@ -93,7 +95,7 @@ CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
-----END CERTIFICATE-----`))
// CN=Amazon Root CA 1; O=Amazon; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
@@ -115,7 +117,7 @@ rqXRfboQnoZsG4q5WTP468SQvvG5
-----END CERTIFICATE-----`))
// CN=Amazon Root CA 2; O=Amazon; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL
@@ -148,7 +150,7 @@ n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE
-----END CERTIFICATE-----`))
// CN=Amazon Root CA 3; O=Amazon; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
@@ -162,7 +164,7 @@ YyRIHN8wfdVoOw==
-----END CERTIFICATE-----`))
// CN=Certum Trusted Network CA; OU=Certum Certification Authority; O=Unizeto Technologies S.A.; C=PL
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM
MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D
ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU
@@ -186,7 +188,7 @@ VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
-----END CERTIFICATE-----`))
// CN=Certum EC-384 CA; OU=Certum Certification Authority; O=Asseco Data Systems S.A.; C=PL
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw
CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw
JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT
@@ -203,7 +205,7 @@ nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k=
-----END CERTIFICATE-----`))
// CN=Certum Trusted Root CA; OU=Certum Certification Authority; O=Asseco Data Systems S.A.; C=PL
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6
MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu
MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV
@@ -238,7 +240,7 @@ E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb
-----END CERTIFICATE-----`))
// CN=Certum Trusted Network CA 2; OU=Certum Certification Authority; O=Unizeto Technologies S.A.; C=PL
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB
gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG
@@ -274,7 +276,7 @@ DrW5viSP
-----END CERTIFICATE-----`))
// CN=Autoridad de Certificacion Firmaprofesional CIF A62634068; C=ES
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE
BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1
@@ -311,7 +313,7 @@ GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV
-----END CERTIFICATE-----`))
// CN=ANF Secure Server Root CA; OU=ANF CA Raiz; O=ANF Autoridad de Certificacion; C=ES; SerialNumber=G63287510
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV
BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk
YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV
@@ -347,7 +349,7 @@ tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw=
-----END CERTIFICATE-----`))
// CN=Buypass Class 2 Root CA; O=Buypass AS-983163327; C=NO
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow
@@ -380,7 +382,7 @@ Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=
-----END CERTIFICATE-----`))
// CN=Buypass Class 3 Root CA; O=Buypass AS-983163327; C=NO
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow
@@ -413,7 +415,7 @@ u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq
-----END CERTIFICATE-----`))
// CN=Certainly Root R1; O=Certainly; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw
PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy
dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9
@@ -446,7 +448,7 @@ OV+KmalBWQewLK8=
-----END CERTIFICATE-----`))
// CN=Certainly Root E1; O=Certainly; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw
CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu
bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ
@@ -461,7 +463,7 @@ BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
-----END CERTIFICATE-----`))
// CN=Certigna; O=Dhimyotis; C=FR
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV
BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X
DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ
@@ -485,7 +487,7 @@ WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----`))
// CN=Certigna Root CA; OU=0002 48146308100036; O=Dhimyotis; C=FR
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw
WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw
MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x
@@ -523,7 +525,7 @@ jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw
-----END CERTIFICATE-----`))
// OU=certSIGN ROOT CA; O=certSIGN; C=RO
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT
AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD
QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP
@@ -545,7 +547,7 @@ i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN
-----END CERTIFICATE-----`))
// OU=certSIGN ROOT CA G2; O=CERTSIGN SA; C=RO
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g
Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ
@@ -578,7 +580,7 @@ QRBdJ3NghVdJIgc=
-----END CERTIFICATE-----`))
// CN=HiPKI Root CA - G1; O=Chunghwa Telecom Co., Ltd.; C=TW
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa
@@ -611,7 +613,7 @@ YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ==
-----END CERTIFICATE-----`))
// OU=ePKI Root Certification Authority; O=Chunghwa Telecom Co., Ltd.; C=TW
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe
MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
@@ -646,7 +648,7 @@ hNQ+IIX3Sj0rnP0qCglN6oH4EZw=
-----END CERTIFICATE-----`))
// CN=D-TRUST BR Root CA 1 2020; O=D-Trust GmbH; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw
CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS
VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5
@@ -666,7 +668,7 @@ dWNbFJWcHwHP2NVypw87
-----END CERTIFICATE-----`))
// CN=D-TRUST EV Root CA 1 2020; O=D-Trust GmbH; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw
CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS
VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5
@@ -686,7 +688,7 @@ gfM0agPnIjhQW+0ZT0MW
-----END CERTIFICATE-----`))
// CN=D-TRUST Root Class 3 CA 2 EV 2009; O=D-Trust GmbH; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw
@@ -713,7 +715,7 @@ KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1
-----END CERTIFICATE-----`))
// CN=D-TRUST Root Class 3 CA 2 2009; O=D-Trust GmbH; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha
@@ -740,7 +742,7 @@ Johw1+qRzT65ysCQblrGXnRl11z+o+I=
-----END CERTIFICATE-----`))
// CN=T-TeleSec GlobalRoot Class 3; OU=T-Systems Trust Center; O=T-Systems Enterprise Services GmbH; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
@@ -765,7 +767,7 @@ TpPDpFQUWw==
-----END CERTIFICATE-----`))
// CN=T-TeleSec GlobalRoot Class 2; OU=T-Systems Trust Center; O=T-Systems Enterprise Services GmbH; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
@@ -790,7 +792,7 @@ BSeOE6Fuwg==
-----END CERTIFICATE-----`))
// CN=DigiCert TLS RSA4096 Root G5; O=DigiCert, Inc.; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT
HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN
@@ -823,7 +825,7 @@ ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+
-----END CERTIFICATE-----`))
// CN=DigiCert TLS ECC P384 Root G5; O=DigiCert, Inc.; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
@@ -839,7 +841,7 @@ DXZDjC5Ty3zfDBeWUA==
-----END CERTIFICATE-----`))
// CN=DigiCert Assured ID Root CA; OU=www.digicert.com; O=DigiCert Inc; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
@@ -863,7 +865,7 @@ H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
-----END CERTIFICATE-----`))
// CN=DigiCert Assured ID Root G2; OU=www.digicert.com; O=DigiCert Inc; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
@@ -887,7 +889,7 @@ IhNzbM8m9Yop5w==
-----END CERTIFICATE-----`))
// CN=DigiCert Assured ID Root G3; OU=www.digicert.com; O=DigiCert Inc; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
@@ -904,7 +906,7 @@ JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
-----END CERTIFICATE-----`))
// CN=DigiCert Global Root CA; OU=www.digicert.com; O=DigiCert Inc; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
@@ -928,7 +930,7 @@ CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----`))
// CN=DigiCert Global Root G2; OU=www.digicert.com; O=DigiCert Inc; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
@@ -952,7 +954,7 @@ MrY=
-----END CERTIFICATE-----`))
// CN=DigiCert Global Root G3; OU=www.digicert.com; O=DigiCert Inc; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
@@ -969,7 +971,7 @@ sycX
-----END CERTIFICATE-----`))
// CN=DigiCert High Assurance EV Root CA; OU=www.digicert.com; O=DigiCert Inc; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
@@ -994,7 +996,7 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
-----END CERTIFICATE-----`))
// CN=DigiCert Trusted Root G4; OU=www.digicert.com; O=DigiCert Inc; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
@@ -1028,7 +1030,7 @@ gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
-----END CERTIFICATE-----`))
// CN=QuoVadis Root CA 2; O=QuoVadis Limited; C=BM
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
@@ -1063,7 +1065,7 @@ ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
-----END CERTIFICATE-----`))
// CN=QuoVadis Root CA 2 G3; O=QuoVadis Limited; C=BM
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
@@ -1096,7 +1098,7 @@ WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
-----END CERTIFICATE-----`))
// CN=QuoVadis Root CA 3 G3; O=QuoVadis Limited; C=BM
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00
@@ -1129,7 +1131,7 @@ ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0
-----END CERTIFICATE-----`))
// CN=CA Disig Root R2; O=Disig a.s.; L=Bratislava; C=SK
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy
@@ -1162,7 +1164,7 @@ L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL
-----END CERTIFICATE-----`))
// CN=emSign ECC Root CA - G3; OU=emSign PKI; O=eMudhra Technologies Limited; C=IN
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG
EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo
bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
@@ -1179,7 +1181,7 @@ CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD
-----END CERTIFICATE-----`))
// CN=emSign Root CA - G1; OU=emSign PKI; O=eMudhra Technologies Limited; C=IN
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD
VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU
ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH
@@ -1203,7 +1205,7 @@ iN66zB+Afko=
-----END CERTIFICATE-----`))
// CN=AffirmTrust Commercial; O=AffirmTrust; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
@@ -1225,7 +1227,7 @@ nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----`))
// CN=Atos TrustedRoot 2011; O=Atos; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE
AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG
EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM
@@ -1248,7 +1250,7 @@ KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
-----END CERTIFICATE-----`))
// CN=Atos TrustedRoot Root CA ECC TLS 2021; O=Atos; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w
LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w
CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0
@@ -1264,7 +1266,7 @@ CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo
-----END CERTIFICATE-----`))
// CN=Atos TrustedRoot Root CA RSA TLS 2021; O=Atos; C=DE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM
MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx
MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00
@@ -1297,7 +1299,7 @@ oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ==
-----END CERTIFICATE-----`))
// CN=GlobalSign; OU=GlobalSign Root CA - R6; O=GlobalSign
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg
MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh
bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx
@@ -1331,7 +1333,7 @@ JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R
-----END CERTIFICATE-----`))
// CN=GlobalSign Root E46; O=GlobalSign nv-sa; C=BE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx
CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD
ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw
@@ -1346,7 +1348,7 @@ DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ
-----END CERTIFICATE-----`))
// CN=GlobalSign Root R46; O=GlobalSign nv-sa; C=BE
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA
MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD
VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy
@@ -1379,7 +1381,7 @@ vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6
-----END CERTIFICATE-----`))
// CN=GlobalSign; OU=GlobalSign ECC Root CA - R5; O=GlobalSign
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
@@ -1395,7 +1397,7 @@ xwy8p2Fp8fc74SrL+SvzZpA3
-----END CERTIFICATE-----`))
// CN=GlobalSign; OU=GlobalSign Root CA - R3; O=GlobalSign
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
@@ -1418,7 +1420,7 @@ WD9f
-----END CERTIFICATE-----`))
// CN=Starfield Root Certificate Authority - G2; O=Starfield Technologies, Inc.; L=Scottsdale; ST=Arizona; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
@@ -1443,7 +1445,7 @@ mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
-----END CERTIFICATE-----`))
// CN=Go Daddy Root Certificate Authority - G2; O=GoDaddy.com, Inc.; L=Scottsdale; ST=Arizona; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
@@ -1468,7 +1470,7 @@ LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
-----END CERTIFICATE-----`))
// CN=GlobalSign; OU=GlobalSign ECC Root CA - R4; O=GlobalSign
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD
VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh
bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw
@@ -1482,7 +1484,7 @@ bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
-----END CERTIFICATE-----`))
// CN=GTS Root R4; O=Google Trust Services LLC; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD
VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
@@ -1497,7 +1499,7 @@ p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD
-----END CERTIFICATE-----`))
// CN=GTS Root R2; O=Google Trust Services LLC; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
@@ -1530,7 +1532,7 @@ JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV
-----END CERTIFICATE-----`))
// CN=GTS Root R1; O=Google Trust Services LLC; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
@@ -1563,7 +1565,7 @@ bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
-----END CERTIFICATE-----`))
// CN=GTS Root R3; O=Google Trust Services LLC; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD
VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
@@ -1578,7 +1580,7 @@ ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X
-----END CERTIFICATE-----`))
// CN=ACCVRAIZ1; OU=PKIACCV; O=ACCV; C=ES
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE
AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw
CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ
@@ -1624,7 +1626,7 @@ pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7
-----END CERTIFICATE-----`))
// OU=AC RAIZ FNMT-RCM; O=FNMT-RCM; C=ES
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx
CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ
WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ
@@ -1658,7 +1660,7 @@ uu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
-----END CERTIFICATE-----`))
// CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS; OU=Ceres; O=FNMT-RCM; C=ES; OrganizationIdentifier=VATES-Q2826004J
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw
CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw
FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S
@@ -1676,7 +1678,7 @@ v+c=
-----END CERTIFICATE-----`))
// CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1; OU=Kamu Sertifikasyon Merkezi - Kamu SM; O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK; L=Gebze - Kocaeli; C=TR
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx
GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp
bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w
@@ -1704,7 +1706,7 @@ lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=
-----END CERTIFICATE-----`))
// CN=HARICA TLS RSA Root CA 2021; O=Hellenic Academic and Research Institutions CA; C=GR
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs
MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg
@@ -1739,7 +1741,7 @@ xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU
-----END CERTIFICATE-----`))
// CN=HARICA TLS ECC Root CA 2021; O=Hellenic Academic and Research Institutions CA; C=GR
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw
CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh
cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v
@@ -1756,7 +1758,7 @@ nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps
-----END CERTIFICATE-----`))
// CN=IdenTrust Commercial Root CA 1; O=IdenTrust; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK
MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu
VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw
@@ -1789,7 +1791,7 @@ mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A
-----END CERTIFICATE-----`))
// CN=ISRG Root X1; O=Internet Security Research Group; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
@@ -1822,7 +1824,7 @@ emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----`))
// CN=ISRG Root X2; O=Internet Security Research Group; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
@@ -1838,7 +1840,7 @@ tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
-----END CERTIFICATE-----`))
// CN=Izenpe.com; O=IZENPE S.A.; C=ES
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4
MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
@@ -1874,7 +1876,7 @@ QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
-----END CERTIFICATE-----`))
// CN=SZAFIR ROOT CA2; O=Krajowa Izba Rozliczeniowa S.A.; C=PL
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL
BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6
ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw
@@ -1897,7 +1899,7 @@ LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
-----END CERTIFICATE-----`))
// CN=e-Szigno Root CA 2017; O=Microsec Ltd.; L=Budapest; C=HU; OrganizationIdentifier=VATHU-23584497
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV
BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk
LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv
@@ -1914,7 +1916,7 @@ jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ
-----END CERTIFICATE-----`))
// CN=Microsec e-Szigno Root CA 2009; O=Microsec Ltd.; L=Budapest; C=HU; EmailAddress=info@e-szigno.hu
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0
ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G
@@ -1940,7 +1942,7 @@ HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW
-----END CERTIFICATE-----`))
// CN=Microsoft ECC Root Certificate Authority 2017; O=Microsoft Corporation; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw
CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD
VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw
@@ -1957,7 +1959,7 @@ iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M=
-----END CERTIFICATE-----`))
// CN=Microsoft RSA Root Certificate Authority 2017; O=Microsoft Corporation; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl
MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw
NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
@@ -1992,7 +1994,7 @@ RA+GsCyRxj3qrg+E
-----END CERTIFICATE-----`))
// CN=NAVER Global Root Certification Authority; O=NAVER BUSINESS PLATFORM Corp.; C=KR
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM
BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG
T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0
@@ -2027,7 +2029,7 @@ dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul
-----END CERTIFICATE-----`))
// CN=NetLock Arany (Class Gold) Főtanúsítvány; OU=Tanúsítványkiadók (Certification Services); O=NetLock Kft.; L=Budapest; C=HU
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG
EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3
MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl
@@ -2053,7 +2055,7 @@ XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
-----END CERTIFICATE-----`))
// CN=OISTE WISeKey Global Root GC CA; OU=OISTE Foundation Endorsed; O=WISeKey; C=CH
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw
CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91
bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg
@@ -2070,7 +2072,7 @@ Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9
-----END CERTIFICATE-----`))
// CN=OISTE WISeKey Global Root GB CA; OU=OISTE Foundation Endorsed; O=WISeKey; C=CH
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt
MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg
Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i
@@ -2094,7 +2096,7 @@ Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
-----END CERTIFICATE-----`))
// CN=Security Communication ECC RootCA1; O=SECOM Trust Systems CO.,LTD.; C=JP
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT
AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD
VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx
@@ -2110,7 +2112,7 @@ be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=
-----END CERTIFICATE-----`))
// OU=Security Communication RootCA2; O=SECOM Trust Systems CO.,LTD.; C=JP
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl
MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe
U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX
@@ -2133,7 +2135,7 @@ SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE-----`))
// CN=Entrust Root Certification Authority; OU=www.entrust.net/CPS is incorporated by reference, (c) 2006 Entrust, Inc.; O=Entrust, Inc.; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
@@ -2162,7 +2164,7 @@ eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
-----END CERTIFICATE-----`))
// CN=Sectigo Public Server Authentication Root E46; O=Sectigo Limited; C=GB
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw
CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T
ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN
@@ -2178,7 +2180,7 @@ qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q
-----END CERTIFICATE-----`))
// CN=COMODO ECC Certification Authority; O=COMODO CA Limited; L=Salford; ST=Greater Manchester; C=GB
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
@@ -2196,7 +2198,7 @@ GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----`))
// CN=COMODO Certification Authority; O=COMODO CA Limited; L=Salford; ST=Greater Manchester; C=GB
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIID0DCCArigAwIBAgIQIKTEf93f4cdTYwcTiHdgEjANBgkqhkiG9w0BAQUFADCB
gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
@@ -2221,7 +2223,7 @@ R1uUq27UlTMdphVx8fiUylQ5PsE=
-----END CERTIFICATE-----`))
// CN=COMODO RSA Certification Authority; O=COMODO CA Limited; L=Salford; ST=Greater Manchester; C=GB
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
@@ -2257,7 +2259,7 @@ NVOFBkpdn627G190
-----END CERTIFICATE-----`))
// CN=USERTrust RSA Certification Authority; O=The USERTRUST Network; L=Jersey City; ST=New Jersey; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
@@ -2293,7 +2295,7 @@ jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----`))
// CN=USERTrust ECC Certification Authority; O=The USERTRUST Network; L=Jersey City; ST=New Jersey; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
@@ -2311,7 +2313,7 @@ RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
-----END CERTIFICATE-----`))
// CN=Sectigo Public Server Authentication Root R46; O=Sectigo Limited; C=GB
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf
MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD
Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw
@@ -2345,7 +2347,7 @@ QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL
-----END CERTIFICATE-----`))
// CN=Entrust Root Certification Authority - G2; OU=See www.entrust.net/legal-terms, (c) 2009 Entrust, Inc. - for authorized use only; O=Entrust, Inc.; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50
cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs
@@ -2372,7 +2374,7 @@ VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==
-----END CERTIFICATE-----`))
// CN=Entrust Root Certification Authority - EC1; OU=See www.entrust.net/legal-terms, (c) 2012 Entrust, Inc. - for authorized use only; O=Entrust, Inc.; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG
A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3
d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu
@@ -2392,7 +2394,7 @@ hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
-----END CERTIFICATE-----`))
// CN=SSL.com Root Certification Authority RSA; O=SSL Corporation; L=Houston; ST=Texas; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE
BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK
DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp
@@ -2428,7 +2430,7 @@ Ic2wBlX7Jz9TkHCpBB5XJ7k=
-----END CERTIFICATE-----`))
// CN=SSL.com TLS ECC Root CA 2022; O=SSL Corporation; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw
CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT
U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2
@@ -2444,7 +2446,7 @@ b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g==
-----END CERTIFICATE-----`))
// CN=SSL.com TLS RSA Root CA 2022; O=SSL Corporation; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO
MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD
DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX
@@ -2478,7 +2480,7 @@ Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU
-----END CERTIFICATE-----`))
// CN=SSL.com Root Certification Authority ECC; O=SSL Corporation; L=Houston; ST=Texas; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC
VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T
U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0
@@ -2496,7 +2498,7 @@ gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl
-----END CERTIFICATE-----`))
// CN=SSL.com EV Root Certification Authority RSA R2; O=SSL Corporation; L=Houston; ST=Texas; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV
BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE
CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy
@@ -2532,7 +2534,7 @@ mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w==
-----END CERTIFICATE-----`))
// CN=SSL.com EV Root Certification Authority ECC; O=SSL Corporation; L=Houston; ST=Texas; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC
VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T
U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp
@@ -2550,7 +2552,7 @@ h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
-----END CERTIFICATE-----`))
// CN=SwissSign Gold CA - G2; O=SwissSign AG; C=CH
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln
biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF
@@ -2585,7 +2587,7 @@ Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
-----END CERTIFICATE-----`))
// CN=TWCA CYBER Root CA; OU=Root CA; O=TAIWAN-CA; C=TW
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQ
MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290
IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5
@@ -2619,7 +2621,7 @@ t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X
-----END CERTIFICATE-----`))
// CN=TWCA Global Root CA; OU=Root CA; O=TAIWAN-CA; C=TW
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx
EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT
VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5
@@ -2652,7 +2654,7 @@ KwbQBM0=
-----END CERTIFICATE-----`))
// CN=TeliaSonera Root CA v1; O=TeliaSonera
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw
NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv
b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD
@@ -2684,7 +2686,7 @@ SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----`))
// CN=Telia Root CA v2; O=Telia Finland Oyj; C=FI
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx
CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE
AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1
@@ -2718,7 +2720,7 @@ rBPuUBQemMc=
-----END CERTIFICATE-----`))
// CN=Trustwave Global ECC P384 Certification Authority; O=Trustwave Holdings, Inc.; L=Chicago; ST=Illinois; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf
BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3
@@ -2737,7 +2739,7 @@ Sw==
-----END CERTIFICATE-----`))
// CN=Trustwave Global ECC P256 Certification Authority; O=Trustwave Holdings, Inc.; L=Chicago; ST=Illinois; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf
BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3
@@ -2754,7 +2756,7 @@ DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7
-----END CERTIFICATE-----`))
// CN=SecureTrust CA; O=SecureTrust Corporation; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
@@ -2778,7 +2780,7 @@ CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
-----END CERTIFICATE-----`))
// CN=Trustwave Global Certification Authority; O=Trustwave Holdings, Inc.; L=Chicago; ST=Illinois; C=US
pool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
chromeIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw
CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x
ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1
@@ -2812,5 +2814,4 @@ h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9
EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK
yeC2nOnOcXHebD8WpHk=
-----END CERTIFICATE-----`))
return pool
}

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,6 @@ var _ adapter.CertificateStore = (*Store)(nil)
type Store struct {
access sync.RWMutex
storeType string
systemPool *x509.CertPool
currentPool *x509.CertPool
certificate string
@@ -32,13 +31,9 @@ type Store struct {
}
func NewStore(ctx context.Context, logger logger.Logger, options option.CertificateOptions) (*Store, error) {
storeType := options.Store
if storeType == "" {
storeType = C.CertificateStoreSystem
}
var systemPool *x509.CertPool
switch storeType {
case C.CertificateStoreSystem:
switch options.Store {
case C.CertificateStoreSystem, "":
systemPool = x509.NewCertPool()
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
var systemValid bool
@@ -56,13 +51,16 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
}
systemPool = certPool
}
case C.CertificateStoreMozilla, C.CertificateStoreChrome:
case C.CertificateStoreMozilla:
systemPool = mozillaIncluded
case C.CertificateStoreChrome:
systemPool = chromeIncluded
case C.CertificateStoreNone:
systemPool = nil
default:
return nil, E.New("unknown certificate store: ", options.Store)
}
store := &Store{
storeType: storeType,
systemPool: systemPool,
certificate: strings.Join(options.Certificate, "\n"),
certificatePaths: options.CertificatePath,
@@ -126,9 +124,13 @@ func (s *Store) Pool() *x509.CertPool {
}
func (s *Store) update() error {
currentPool, err := s.newBasePool()
if err != nil {
return err
s.access.Lock()
defer s.access.Unlock()
var currentPool *x509.CertPool
if s.systemPool == nil {
currentPool = x509.NewCertPool()
} else {
currentPool = s.systemPool.Clone()
}
if s.certificate != "" {
if !currentPool.AppendCertsFromPEM([]byte(s.certificate)) {
@@ -163,30 +165,10 @@ func (s *Store) update() error {
if firstErr != nil {
return firstErr
}
s.access.Lock()
defer s.access.Unlock()
s.currentPool = currentPool
return nil
}
func (s *Store) newBasePool() (*x509.CertPool, error) {
switch s.storeType {
case C.CertificateStoreSystem:
if s.systemPool == nil {
return x509.NewCertPool(), nil
}
return s.systemPool.Clone(), nil
case C.CertificateStoreMozilla:
return newMozillaIncluded(), nil
case C.CertificateStoreChrome:
return newChromeIncluded(), nil
case C.CertificateStoreNone:
return x509.NewCertPool(), nil
default:
return nil, E.New("unknown certificate store: ", s.storeType)
}
}
func readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) {
files, err := os.ReadDir(dir)
if err != nil {

View File

@@ -1,12 +1,15 @@
package cloudflare
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/tidwall/gjson"
)
type CloudflareApi struct {
@@ -22,93 +25,50 @@ func NewCloudflareApi(opts ...CloudflareApiOption) *CloudflareApi {
}
func (api *CloudflareApi) CreateProfile(ctx context.Context, publicKey string) (*CloudflareProfile, error) {
serial, err := GenerateRandomAndroidSerial()
if err != nil {
return nil, fmt.Errorf("failed to generate serial: %v", err)
}
data := Registration{
Key: publicKey,
InstallID: "",
FcmToken: "",
Tos: TimeAsCfString(time.Now()),
Model: "PC",
Serial: serial,
OsVersion: "",
KeyType: KeyTypeWg,
TunType: TunTypeWg,
Locale: "en-US",
}
jsonData, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("failed to marshal json: %v", err)
}
request, err := http.NewRequest("POST", ApiUrl+"/"+ApiVersion+"/reg", bytes.NewBuffer(jsonData))
request, err := http.NewRequest("POST", "https://api.cloudflareclient.com/v0i1909051800/reg", strings.NewReader(
fmt.Sprintf(
"{\"install_id\":\"\",\"tos\":\"%s\",\"key\":\"%s\",\"fcm_token\":\"\",\"type\":\"ios\",\"locale\":\"en_US\"}",
time.Now().Format("2006-01-02T15:04:05.000Z"),
publicKey,
),
))
if err != nil {
return nil, err
}
for k, v := range Headers {
request.Header.Set(k, v)
}
response, err := api.client.Do(request.WithContext(ctx))
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to register: %v", response.StatusCode)
if response.StatusCode != 200 {
return nil, fmt.Errorf("status code is not 200")
}
profile := new(CloudflareProfile)
return profile, json.NewDecoder(response.Body).Decode(profile)
}
func (api *CloudflareApi) EnrollKey(ctx context.Context, authToken string, id string, keyType, tunType, publicKey string) (*CloudflareProfile, error) {
deviceUpdate := DeviceUpdate{
Name: "PC",
Key: publicKey,
KeyType: keyType,
TunType: tunType,
}
jsonData, err := json.Marshal(deviceUpdate)
if err != nil {
return nil, fmt.Errorf("failed to marshal json: %v", err)
}
request, err := http.NewRequest("PATCH", ApiUrl+"/"+ApiVersion+"/reg/"+id, bytes.NewBuffer(jsonData))
content, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
for k, v := range Headers {
request.Header.Set(k, v)
}
request.Header.Set("Authorization", "Bearer "+authToken)
response, err := api.client.Do(request.WithContext(ctx))
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to enroll key: %v", response.StatusCode)
}
profile := new(CloudflareProfile)
return profile, json.NewDecoder(response.Body).Decode(profile)
return profile, json.NewDecoder(strings.NewReader(gjson.Get(string(content), "result").Raw)).Decode(profile)
}
func (api *CloudflareApi) GetProfile(ctx context.Context, authToken string, id string) (*CloudflareProfile, error) {
request, err := http.NewRequest("GET", ApiUrl+"/"+ApiVersion+"/reg/"+id, nil)
request, err := http.NewRequest("GET", "https://api.cloudflareclient.com/v0i1909051800/reg/"+id, nil)
if err != nil {
return nil, err
}
for k, v := range Headers {
request.Header.Set(k, v)
}
request.Header.Set("Authorization", "Bearer "+authToken)
response, err := api.client.Do(request.WithContext(ctx))
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get profile: %v", response.StatusCode)
if response.StatusCode != 200 {
return nil, fmt.Errorf("status code is not 200")
}
content, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
profile := new(CloudflareProfile)
return profile, json.NewDecoder(response.Body).Decode(profile)
return profile, json.NewDecoder(strings.NewReader(gjson.Get(string(content), "result").Raw)).Decode(profile)
}

View File

@@ -1,25 +0,0 @@
package cloudflare
const (
ApiUrl = "https://api.cloudflareclient.com"
ApiVersion = "v0a4471"
ConnectSNI = "consumer-masque.cloudflareclient.com"
// unused for now
ZeroTierSNI = "zt-masque.cloudflareclient.com"
ConnectURI = "https://cloudflareaccess.com"
DefaultModel = "PC"
KeyTypeWg = "curve25519"
TunTypeWg = "wireguard"
KeyTypeMasque = "secp256r1"
TunTypeMasque = "masque"
DefaultLocale = "en_US"
DefaultEndpointH2V4 = "162.159.198.2"
DefaultEndpointH2V6 = ""
)
var Headers = map[string]string{
"User-Agent": "WARP for Android",
"CF-Client-Version": "a-6.35-4471",
"Content-Type": "application/json; charset=UTF-8",
"Connection": "Keep-Alive",
}

View File

@@ -1,132 +0,0 @@
package cloudflare
import (
"strings"
)
type Registration struct {
Key string `json:"key"`
InstallID string `json:"install_id"`
FcmToken string `json:"fcm_token"`
Tos string `json:"tos"`
Model string `json:"model"`
Serial string `json:"serial_number"`
OsVersion string `json:"os_version"`
KeyType string `json:"key_type"`
TunType string `json:"tunnel_type"`
Locale string `json:"locale"`
}
type CloudflareProfile struct {
ID string `json:"id"`
Type string `json:"type"`
Model string `json:"model"`
Name string `json:"name"`
Key string `json:"key"`
KeyType string `json:"key_type"`
TunType string `json:"tunnel_type"`
Account Account `json:"account"`
Config Config `json:"config"`
// WarpEnabled not set for ZeroTier
WarpEnabled bool `json:"warp_enabled,omitempty"`
// Waitlist not set for ZeroTier
Waitlist bool `json:"waitlist_enabled,omitempty"`
Created string `json:"created"`
Updated string `json:"updated"`
// Tos not set for ZeroTier
Tos string `json:"tos,omitempty"`
// Place not set for ZeroTier
Place int `json:"place,omitempty"`
Locale string `json:"locale"`
// Enabled not set for ZeroTier
Enabled bool `json:"enabled,omitempty"`
InstallID string `json:"install_id"`
// Token only set for /reg call
Token string `json:"token,omitempty"`
FcmToken string `json:"fcm_token"`
// SerialNumber not set for ZeroTier
SerialNumber string `json:"serial_number,omitempty"`
Policy Policy `json:"policy"`
}
type Account struct {
ID string `json:"id"`
AccountType string `json:"account_type"`
// Created not set for ZeroTier
Created string `json:"created,omitempty"`
// Updated not set for ZeroTier
Updated string `json:"updated,omitempty"`
// Managed only set for ZeroTier
Managed string `json:"managed,omitempty"`
// Organization only set for ZeroTier
Organization string `json:"organization,omitempty"`
// PremiumData not set for ZeroTier
PremiumData int `json:"premium_data,omitempty"`
// Quota not set for ZeroTier
Quota int `json:"quota,omitempty"`
// WarpPlus not set for ZeroTier
WarpPlus bool `json:"warp_plus,omitempty"`
// ReferralCode not set for ZeroTier
ReferralCount int `json:"referral_count,omitempty"`
// ReferralRenewalCount not set for ZeroTier
ReferralRenewalCount int `json:"referral_renewal_countdown,omitempty"`
// Role not set for ZeroTier
Role string `json:"role,omitempty"`
// License not set for ZeroTier
License string `json:"license,omitempty"`
}
type Config struct {
ClientID string `json:"client_id"`
Peers []Peer `json:"peers"`
Interface struct {
Addresses struct {
V4 string `json:"v4"`
V6 string `json:"v6"`
} `json:"addresses"`
} `json:"interface"`
Services struct {
HTTPProxy string `json:"http_proxy"`
} `json:"services"`
}
type Peer struct {
PublicKey string `json:"public_key"`
Endpoint struct {
V4 string `json:"v4"`
V6 string `json:"v6"`
Host string `json:"host"`
Ports []int `json:"ports"`
} `json:"endpoint"`
}
type Policy struct {
TunnelProtocol string `json:"tunnel_protocol"`
}
type DeviceUpdate struct {
Key string `json:"key"`
KeyType string `json:"key_type"`
TunType string `json:"tunnel_type"`
Name string `json:"name,omitempty"`
}
type APIError struct {
Result interface{} `json:"result"`
Success bool `json:"success"`
Errors []ErrorInfo `json:"errors"`
Messages []string `json:"messages"`
}
type ErrorInfo struct {
Code int `json:"code"`
Message string `json:"message"`
}
func (e *APIError) Error() string {
errors := make([]string, len(e.Errors))
for i, err := range e.Errors {
errors[i] = err.Message
}
return strings.Join(errors, ",")
}

View File

@@ -4,14 +4,12 @@ import (
"context"
"net"
"net/http"
"time"
)
type CloudflareApiOption func(api *CloudflareApi)
func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) CloudflareApiOption {
return func(api *CloudflareApi) {
api.client.Timeout = 30 * time.Second
api.client.Transport = &http.Transport{
DialContext: dialContext,
}

View File

@@ -0,0 +1,64 @@
package cloudflare
import "time"
type CloudflareProfile struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Key string `json:"key"`
Account struct {
ID string `json:"id"`
AccountType string `json:"account_type"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
PremiumData int `json:"premium_data"`
Quota int `json:"quota"`
Usage int `json:"usage"`
WARPPlus bool `json:"warp_plus"`
ReferralCount int `json:"referral_count"`
ReferralRenewalCountdown int `json:"referral_renewal_countdown"`
Role string `json:"role"`
License string `json:"license"`
TTL time.Time `json:"ttl"`
} `json:"account"`
Config struct {
ClientID string `json:"client_id"`
Interface struct {
Addresses struct {
V4 string `json:"v4"`
V6 string `json:"v6"`
} `json:"addresses"`
} `json:"interface"`
Peers []struct {
PublicKey string `json:"public_key"`
Endpoint struct {
V4 string `json:"v4"`
V6 string `json:"v6"`
Host string `json:"host"`
Ports []int `json:"ports"`
} `json:"endpoint"`
} `json:"peers"`
Services struct {
HTTPProxy string `json:"http_proxy"`
} `json:"services"`
Metrics struct {
Ping int `json:"ping"`
Report int `json:"report"`
} `json:"metrics"`
} `json:"config"`
Token string `json:"token"`
WARPEnabled bool `json:"warp_enabled"`
WaitlistEnabled bool `json:"waitlist_enabled"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
Tos time.Time `json:"tos"`
Place int `json:"place"`
Locale string `json:"locale"`
Enabled bool `json:"enabled"`
InstallID string `json:"install_id"`
FcmToken string `json:"fcm_token"`
Policy struct {
TunnelProtocol string `json:"tunnel_protocol"`
} `json:"policy"`
}

View File

@@ -1,19 +0,0 @@
package cloudflare
import (
"crypto/rand"
"encoding/hex"
"time"
)
func GenerateRandomAndroidSerial() (string, error) {
serial := make([]byte, 8)
if _, err := rand.Read(serial); err != nil {
return "", err
}
return hex.EncodeToString(serial), nil
}
func TimeAsCfString(t time.Time) string {
return t.Format("2006-01-02T15:04:05.000-07:00")
}

View File

@@ -1,77 +0,0 @@
package congestion
import (
"time"
"github.com/sagernet/quic-go"
"github.com/sagernet/quic-go/congestion"
"github.com/sagernet/sing-quic/congestion_bbr1"
"github.com/sagernet/sing-quic/congestion_bbr2"
congestion_meta1 "github.com/sagernet/sing-quic/congestion_meta1"
congestion_meta2 "github.com/sagernet/sing-quic/congestion_meta2"
E "github.com/sagernet/sing/common/exceptions"
)
func NewCongestionControl(name string, cwnd int, timeFunc func() time.Time) (func(conn *quic.Conn) congestion.CongestionControl, error) {
if timeFunc == nil {
timeFunc = time.Now
}
if cwnd == 0 {
cwnd = 32
}
switch name {
case "", "bbr":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_meta2.NewBbrSender(
congestion_meta2.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
congestion.ByteCount(cwnd)*congestion.ByteCount(conn.Config().InitialPacketSize),
)
}, nil
case "bbr_standard":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_bbr1.NewBbrSender(
congestion_bbr1.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
congestion_bbr1.InitialCongestionWindowPackets,
congestion_bbr1.MaxCongestionWindowPackets,
)
}, nil
case "bbr2":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_bbr2.NewBBR2Sender(
congestion_bbr2.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
0,
false,
)
}, nil
case "bbr2_variant":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_bbr2.NewBBR2Sender(
congestion_bbr2.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
32*congestion.ByteCount(conn.Config().InitialPacketSize),
true,
)
}, nil
case "cubic":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_meta1.NewCubicSender(
congestion_meta1.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
false,
)
}, nil
case "reno":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_meta1.NewCubicSender(
congestion_meta1.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
true,
)
}, nil
default:
return nil, E.New("unknown congestion control: ", name)
}
}

View File

@@ -63,7 +63,9 @@ parseLine:
}
continue
}
ruleLine = strings.TrimSuffix(ruleLine, "|")
if strings.HasSuffix(ruleLine, "|") {
ruleLine = ruleLine[:len(ruleLine)-1]
}
var (
isExclude bool
isSuffix bool
@@ -74,7 +76,7 @@ parseLine:
)
if !strings.HasPrefix(ruleLine, "/") && strings.Contains(ruleLine, "$") {
params := common.SubstringAfter(ruleLine, "$")
for param := range strings.SplitSeq(params, ",") {
for _, param := range strings.Split(params, ",") {
paramParts := strings.Split(param, "=")
var ignored bool
if len(paramParts) > 0 && len(paramParts) <= 2 {
@@ -104,7 +106,9 @@ parseLine:
ruleLine = ruleLine[2:]
isExclude = true
}
ruleLine = strings.TrimSuffix(ruleLine, "|")
if strings.HasSuffix(ruleLine, "|") {
ruleLine = ruleLine[:len(ruleLine)-1]
}
if strings.HasPrefix(ruleLine, "||") {
ruleLine = ruleLine[2:]
isSuffix = true
@@ -410,18 +414,18 @@ func ignoreIPCIDRRegexp(ruleLine string) bool {
}
func parseAdGuardHostLine(ruleLine string) (string, error) {
before, after, ok := strings.Cut(ruleLine, " ")
if !ok {
idx := strings.Index(ruleLine, " ")
if idx == -1 {
return "", os.ErrInvalid
}
address, err := netip.ParseAddr(before)
address, err := netip.ParseAddr(ruleLine[:idx])
if err != nil {
return "", err
}
if !address.IsUnspecified() {
return "", nil
}
domain := after
domain := ruleLine[idx+1:]
if !M.IsDomainName(domain) {
return "", E.New("invalid domain name: ", domain)
}

View File

@@ -70,20 +70,9 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
if !(C.IsLinux || C.IsDarwin || C.IsWindows) {
return nil, E.New("`bind_interface` is only supported on Linux, macOS and Windows")
}
if platformInterface != nil && platformInterface.UsePlatformAutoDetectInterfaceControl() {
interfaceName := options.BindInterface
bindFunc := func(network, address string, conn syscall.RawConn) error {
return control.Raw(conn, func(fd uintptr) error {
return platformInterface.BindInterfaceControl(int(fd), interfaceName)
})
}
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else {
bindFunc := control.BindToInterface(interfaceFinder, options.BindInterface, -1)
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
}
bindFunc := control.BindToInterface(interfaceFinder, options.BindInterface, -1)
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
}
if options.RoutingMark > 0 {
if !C.IsLinux {

View File

@@ -136,16 +136,18 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
go startRacer(fallbackCtx, false, iif)
}
var errors []error
for res := range results {
if res.error == nil {
return res.Conn, res.primary, nil
}
errors = append(errors, res.error)
if len(errors) == len(primaryInterfaces)+len(fallbackInterfaces) {
return nil, false, E.Errors(errors...)
for {
select {
case res := <-results:
if res.error == nil {
return res.Conn, res.primary, nil
}
errors = append(errors, res.error)
if len(errors) == len(primaryInterfaces)+len(fallbackInterfaces) {
return nil, false, E.Errors(errors...)
}
}
}
return nil, false, E.Errors(errors...)
}
func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listener net.ListenConfig, network string, addr string, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
@@ -182,10 +184,10 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType) (primaryInterfaces []adapter.NetworkInterface, fallbackInterfaces []adapter.NetworkInterface) {
interfaces := networkManager.NetworkInterfaces()
myInterfaces := networkManager.InterfaceMonitor().MyInterfaces()
if len(myInterfaces) > 0 {
myInterface := networkManager.InterfaceMonitor().MyInterface()
if myInterface != "" {
interfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
return !common.Contains(myInterfaces, it.Name)
return it.Name != myInterface
})
}
switch strategy {

View File

@@ -19,6 +19,11 @@ func oldWriteString(writer varbin.Writer, value string) error {
return varbin.Write(writer, binary.BigEndian, value)
}
func oldWriteItem(writer varbin.Writer, item Item) error {
//nolint:staticcheck
return varbin.Write(writer, binary.BigEndian, item)
}
func oldReadString(reader varbin.Reader) (string, error) {
//nolint:staticcheck
return varbin.ReadValue[string](reader, binary.BigEndian)
@@ -219,7 +224,7 @@ func TestGeositeWriteReadCompat(t *testing.T) {
func generateLargeItems(count int) map[string][]Item {
items := make([]Item, count)
for i := range count {
for i := 0; i < count; i++ {
items[i] = Item{
Type: ItemType(i % 4),
Value: strings.Repeat("x", i%200) + ".com",

View File

@@ -48,6 +48,12 @@ func NewReader(readSeeker io.ReadSeeker) (*Reader, []string, error) {
return reader, codes, nil
}
type geositeMetadata struct {
Code string
Index uint64
Length uint64
}
func (r *Reader) readMetadata() error {
counter := &readCounter{Reader: r.reader}
reader := bufio.NewReader(counter)
@@ -95,9 +101,6 @@ func (r *Reader) readMetadata() error {
}
func (r *Reader) Read(code string) ([]Item, error) {
r.access.Lock()
defer r.access.Unlock()
index, exists := r.domainIndex[code]
if !exists {
return nil, E.New("code ", code, " not exists!")

View File

@@ -11,13 +11,3 @@ func ContextWithIsExternalConnection(ctx context.Context) context.Context {
func IsExternalConnectionFromContext(ctx context.Context) bool {
return ctx.Value(contextKeyIsExternalConnection{}) != nil
}
type contextKeyIsProviderConnection struct{}
func ContextWithIsProviderConnection(ctx context.Context) context.Context {
return context.WithValue(ctx, contextKeyIsProviderConnection{}, true)
}
func IsProviderConnectionFromContext(ctx context.Context) bool {
return ctx.Value(contextKeyIsProviderConnection{}) != nil
}

View File

@@ -17,31 +17,30 @@ type Group struct {
type groupConnItem struct {
conn io.Closer
isExternal bool
isProvider bool
}
func NewGroup() *Group {
return &Group{}
}
func (g *Group) NewConn(conn net.Conn, isExternal bool, isProvider bool) net.Conn {
func (g *Group) NewConn(conn net.Conn, isExternal bool) net.Conn {
g.access.Lock()
defer g.access.Unlock()
item := g.connections.PushBack(&groupConnItem{conn, isExternal, isProvider})
item := g.connections.PushBack(&groupConnItem{conn, isExternal})
return &Conn{Conn: conn, group: g, element: item}
}
func (g *Group) NewPacketConn(conn net.PacketConn, isExternal bool, isProvider bool) net.PacketConn {
func (g *Group) NewPacketConn(conn net.PacketConn, isExternal bool) net.PacketConn {
g.access.Lock()
defer g.access.Unlock()
item := g.connections.PushBack(&groupConnItem{conn, isExternal, isProvider})
item := g.connections.PushBack(&groupConnItem{conn, isExternal})
return &PacketConn{PacketConn: conn, group: g, element: item}
}
func (g *Group) NewSingPacketConn(conn N.PacketConn, isExternal bool, isProvider bool) N.PacketConn {
func (g *Group) NewSingPacketConn(conn N.PacketConn, isExternal bool) N.PacketConn {
g.access.Lock()
defer g.access.Unlock()
item := g.connections.PushBack(&groupConnItem{conn, isExternal, isProvider})
item := g.connections.PushBack(&groupConnItem{conn, isExternal})
return &SingPacketConn{PacketConn: conn, group: g, element: item}
}

View File

@@ -131,7 +131,7 @@ func (j *ClientHello) parseHandshake(hs []byte) error {
return &ParseError{LengthErr, 7}
}
for i := range numCiphers {
for i := 0; i < numCiphers; i++ {
cipherSuite := uint16(cs[2+i<<1])<<8 | uint16(cs[3+i<<1])
cipherSuites = append(cipherSuites, cipherSuite)
}
@@ -234,7 +234,7 @@ func (j *ClientHello) parseExtensions(exs []byte) error {
return &ParseError{LengthErr, 16}
}
for i := range numCurves {
for i := 0; i < numCurves; i++ {
ecType := uint16(sex[i*2])<<8 | uint16(sex[1+i*2])
ellipticCurves = append(ellipticCurves, ecType)
}
@@ -256,7 +256,7 @@ func (j *ClientHello) parseExtensions(exs []byte) error {
return &ParseError{LengthErr, 18}
}
for i := range numPF {
for i := 0; i < numPF; i++ {
ellipticCurvePF[i] = uint8(sex[i])
}
case versionExtensionType:

View File

@@ -1,54 +0,0 @@
package kmutex
import "sync"
type Kmutex[T comparable] struct {
l sync.Locker
s map[T]*klock
}
type klock struct {
cond *sync.Cond
ref uint64
}
func New[T comparable]() *Kmutex[T] {
l := sync.Mutex{}
return &Kmutex[T]{
l: &l,
s: make(map[T]*klock),
}
}
func (km *Kmutex[T]) Unlock(key T) {
km.l.Lock()
defer km.l.Unlock()
kl, ok := km.s[key]
if !ok || kl.ref == 0 {
panic("unlock of unlocked kmutex")
}
kl.ref--
if kl.ref == 0 {
delete(km.s, key)
return
}
kl.cond.Signal()
}
func (km *Kmutex[T]) Lock(key T) {
km.l.Lock()
defer km.l.Unlock()
for {
kl, ok := km.s[key]
if !ok {
km.s[key] = &klock{
cond: sync.NewCond(km.l),
ref: 1,
}
return
}
kl.ref++
kl.cond.Wait()
return
}
}

View File

@@ -1,96 +0,0 @@
package kmutex
import (
"sync"
"testing"
"time"
)
// Number of unique resources to access
const number = 100
func makeIds(count int) []int {
ids := make([]int, count)
for i := 0; i < count; i++ {
ids[i] = i
}
return ids
}
func TestKmutex(t *testing.T) {
km := New[int]()
ids := makeIds(number)
resources := make([]int, number)
wg := sync.WaitGroup{}
lc := make(chan int)
uc := make(chan int)
// Start 10n goroutines accessing n resources 10 times each
for i := 0; i < 10*number; i++ {
wg.Add(1)
go func(k int) {
for j := 0; j < 10; j++ {
lc <- k
km.Lock(ids[k])
// read and write resource to check for race
resources[k] = resources[k] + 1
km.Unlock(ids[k])
uc <- k
}
wg.Done()
}(i % len(ids))
}
to := time.After(time.Second)
counts := make(map[int]int)
var lCount, ulCount int
loop:
for {
select {
case k := <-lc:
counts[k] = counts[k] + 1
lCount++
case k := <-uc:
counts[k] = counts[k] - 1
ulCount++
case <-to:
t.Fatal("timed out waiting for results")
break loop
}
expectCount := 100 * number
if lCount == expectCount && ulCount == expectCount {
// Have all results
break
}
}
for k, c := range counts {
if c != 0 {
t.Errorf("Key %d count != 0: %d\n", k, c)
}
}
wg.Wait()
}
func BenchmarkKmutex1000(b *testing.B) {
km := New[int]()
ids := makeIds(number)
resources := make([]int, number)
wg := sync.WaitGroup{}
// Start 1000 goroutines accessing 100 resources N times each
b.ResetTimer()
for i := 0; i < 1000; i++ {
wg.Add(1)
go func(k int) {
for j := 0; j < b.N; j++ {
km.Lock(ids[k])
// read and write resource to check for race
resources[k] = resources[k] + 1
km.Unlock(ids[k])
}
wg.Done()
}(i % len(ids))
}
wg.Wait()
}

View File

@@ -6,7 +6,48 @@
package ktls
import "golang.org/x/crypto/cryptobyte"
import (
"fmt"
"golang.org/x/crypto/cryptobyte"
)
// The marshalingFunction type is an adapter to allow the use of ordinary
// functions as cryptobyte.MarshalingValue.
type marshalingFunction func(b *cryptobyte.Builder) error
func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error {
return f(b)
}
// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If
// the length of the sequence is not the value specified, it produces an error.
func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) {
b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error {
if len(v) != n {
return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v))
}
b.AddBytes(v)
return nil
}))
}
// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder.
func addUint64(b *cryptobyte.Builder, v uint64) {
b.AddUint32(uint32(v >> 32))
b.AddUint32(uint32(v))
}
// readUint64 decodes a big-endian, 64-bit value into out and advances over it.
// It reports whether the read was successful.
func readUint64(s *cryptobyte.String, out *uint64) bool {
var hi, lo uint32
if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) {
return false
}
*out = uint64(hi)<<32 | uint64(lo)
return true
}
// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a
// []byte instead of a cryptobyte.String.
@@ -20,6 +61,12 @@ func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out))
}
// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a
// []byte instead of a cryptobyte.String.
func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out))
}
type keyUpdateMsg struct {
updateRequested bool
}
@@ -78,6 +125,11 @@ const (
typeMessageHash uint8 = 254 // synthetic message
)
// TLS compression types.
const (
compressionNone uint8 = 0
)
// TLS extension numbers
const (
extensionServerName uint16 = 0

View File

@@ -77,5 +77,78 @@ func (c *Conn) writeRecordLocked(typ uint16, data []byte) (n int, err error) {
if !c.kernelTx {
return c.rawConn.WriteRecordLocked(typ, data)
}
/*for len(data) > 0 {
m := len(data)
if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
m = maxPayload
}
_, err = c.writeKernelRecord(typ, data[:m])
if err != nil {
return
}
n += m
data = data[m:]
}*/
return c.writeKernelRecord(typ, data)
}
const (
// tcpMSSEstimate is a conservative estimate of the TCP maximum segment
// size (MSS). A constant is used, rather than querying the kernel for
// the actual MSS, to avoid complexity. The value here is the IPv6
// minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40
// bytes) and a TCP header with timestamps (32 bytes).
tcpMSSEstimate = 1208
// recordSizeBoostThreshold is the number of bytes of application data
// sent after which the TLS record size will be increased to the
// maximum.
recordSizeBoostThreshold = 128 * 1024
)
func (c *Conn) maxPayloadSizeForWrite(typ uint16) int {
if /*c.config.DynamicRecordSizingDisabled ||*/ typ != recordTypeApplicationData {
return maxPlaintext
}
if *c.rawConn.PacketsSent >= recordSizeBoostThreshold {
return maxPlaintext
}
// Subtract TLS overheads to get the maximum payload size.
payloadBytes := tcpMSSEstimate - recordHeaderLen - c.rawConn.Out.ExplicitNonceLen()
if rawCipher := *c.rawConn.Out.Cipher; rawCipher != nil {
switch ciph := rawCipher.(type) {
case cipher.Stream:
payloadBytes -= (*c.rawConn.Out.Mac).Size()
case cipher.AEAD:
payloadBytes -= ciph.Overhead()
/*case cbcMode:
blockSize := ciph.BlockSize()
// The payload must fit in a multiple of blockSize, with
// room for at least one padding byte.
payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1
// The RawMac is appended before padding so affects the
// payload size directly.
payloadBytes -= c.out.mac.Size()*/
default:
panic("unknown cipher type")
}
}
if *c.rawConn.Vers == tls.VersionTLS13 {
payloadBytes-- // encrypted ContentType
}
// Allow packet growth in arithmetic progression up to max.
pkt := *c.rawConn.PacketsSent
*c.rawConn.PacketsSent++
if pkt > 1000 {
return maxPlaintext // avoid overflow in multiply below
}
n := payloadBytes * int(pkt+1)
if n > maxPlaintext {
n = maxPlaintext
}
return n
}

View File

@@ -1,164 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package list
// Element is an element of a linked list.
type Element[T any] struct {
next, prev *Element[T]
list *List[T]
Value T
}
func (e *Element[T]) Next() *Element[T] {
if p := e.next; e.list != nil && p != &e.list.root {
return p
}
return nil
}
func (e *Element[T]) Prev() *Element[T] {
if p := e.prev; e.list != nil && p != &e.list.root {
return p
}
return nil
}
func (e *Element[T]) Remove() bool {
if e.list == nil {
return false
}
e.list.remove(e)
return true
}
type List[T any] struct {
root Element[T]
len int
}
func (l *List[T]) Init() *List[T] {
l.root.next = &l.root
l.root.prev = &l.root
l.len = 0
return l
}
func New[T any]() *List[T] { return new(List[T]).Init() }
func (l *List[T]) Len() int { return l.len }
func (l *List[T]) Front() *Element[T] {
if l.len == 0 {
return nil
}
return l.root.next
}
func (l *List[T]) Back() *Element[T] {
if l.len == 0 {
return nil
}
return l.root.prev
}
func (l *List[T]) lazyInit() {
if l.root.next == nil {
l.Init()
}
}
func (l *List[T]) insert(e, at *Element[T]) *Element[T] {
e.prev = at
e.next = at.next
e.prev.next = e
e.next.prev = e
e.list = l
l.len++
return e
}
func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] {
return l.insert(&Element[T]{Value: v}, at)
}
func (l *List[T]) remove(e *Element[T]) {
e.prev.next = e.next
e.next.prev = e.prev
e.next = nil
e.prev = nil
e.list = nil
l.len--
}
func (l *List[T]) Remove(e *Element[T]) T {
if e.list == l {
l.remove(e)
}
return e.Value
}
func (l *List[T]) PushFront(v T) *Element[T] {
l.lazyInit()
return l.insertValue(v, &l.root)
}
func (l *List[T]) PushBack(v T) *Element[T] {
l.lazyInit()
return l.insertValue(v, l.root.prev)
}
func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
if mark.list != l {
return nil
}
return l.insertValue(v, mark.prev)
}
func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
if mark.list != l {
return nil
}
return l.insertValue(v, mark)
}
func (l *List[T]) MoveToFront(e *Element[T]) {
if e.list != l || l.root.next == e {
return
}
l.move(e, &l.root)
}
func (l *List[T]) MoveToBack(e *Element[T]) {
if e.list != l || l.root.prev == e {
return
}
l.move(e, l.root.prev)
}
func (l *List[T]) MoveBefore(e, mark *Element[T]) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark.prev)
}
func (l *List[T]) MoveAfter(e, mark *Element[T]) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark)
}
func (l *List[T]) move(e, at *Element[T]) {
if e == at {
return
}
e.prev.next = e.next
e.next.prev = e.prev
e.prev = at
e.next = at.next
e.prev.next = e
e.next.prev = e
}

View File

@@ -77,7 +77,7 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
if err != nil {
return nil, err
}
l.logger.Notice("tcp server started at ", tcpListener.Addr())
l.logger.Info("tcp server started at ", tcpListener.Addr())
l.tcpListener = tcpListener
return tcpListener, err
}

View File

@@ -54,7 +54,7 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
}
l.udpConn = udpConn.(*net.UDPConn)
l.udpAddr = bindAddr
l.logger.Notice("udp server started at ", udpConn.LocalAddr())
l.logger.Info("udp server started at ", udpConn.LocalAddr())
return udpConn, err
}

View File

@@ -1,98 +0,0 @@
package source
import (
"io"
"io/fs"
"io/ioutil"
"strconv"
"strings"
"github.com/golang-migrate/migrate/v4/source"
E "github.com/sagernet/sing/common/exceptions"
)
type RawDriver struct {
migrations *source.Migrations
rawMigrations map[string]string
}
func NewRawDriver(rawMigrations map[string]string) *RawDriver {
return &RawDriver{rawMigrations: rawMigrations}
}
func (d *RawDriver) Init() error {
ms := source.NewMigrations()
for key := range d.rawMigrations {
m, err := source.DefaultParse(key)
if err != nil {
continue
}
if !ms.Append(m) {
return source.ErrDuplicateMigration{
Migration: *m,
}
}
}
d.migrations = ms
return nil
}
func (d *RawDriver) Open(url string) (source.Driver, error) {
return nil, E.New("open() cannot be called")
}
func (d *RawDriver) Close() error {
return nil
}
func (d *RawDriver) First() (version uint, err error) {
if version, ok := d.migrations.First(); ok {
return version, nil
}
return 0, &fs.PathError{
Op: "first",
Err: fs.ErrNotExist,
}
}
func (d *RawDriver) Prev(version uint) (prevVersion uint, err error) {
if version, ok := d.migrations.Prev(version); ok {
return version, nil
}
return 0, &fs.PathError{
Op: "prev for version " + strconv.FormatUint(uint64(version), 10),
Err: fs.ErrNotExist,
}
}
func (d *RawDriver) Next(version uint) (nextVersion uint, err error) {
if version, ok := d.migrations.Next(version); ok {
return version, nil
}
return 0, &fs.PathError{
Op: "next for version " + strconv.FormatUint(uint64(version), 10),
Err: fs.ErrNotExist,
}
}
func (d *RawDriver) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
if m, ok := d.migrations.Up(version); ok {
body := ioutil.NopCloser(strings.NewReader(d.rawMigrations[m.Raw]))
return body, m.Identifier, nil
}
return nil, "", &fs.PathError{
Op: "read up for version " + strconv.FormatUint(uint64(version), 10),
Err: fs.ErrNotExist,
}
}
func (d *RawDriver) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
if m, ok := d.migrations.Down(version); ok {
body := ioutil.NopCloser(strings.NewReader(d.rawMigrations[m.Raw]))
return body, m.Identifier, nil
}
return nil, "", &fs.PathError{
Op: "read down for version " + strconv.FormatUint(uint64(version), 10),
Err: fs.ErrNotExist,
}
}

View File

@@ -38,9 +38,6 @@ func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.Conte
}
}
service, err := mux.NewService(mux.ServiceOptions{
NewConnectionContext: func(ctx context.Context, conn net.Conn) context.Context {
return log.ContextWithNewMuxID(ctx)
},
NewStreamContext: func(ctx context.Context, conn net.Conn) context.Context {
return log.ContextWithNewID(ctx)
},

View File

@@ -1,38 +0,0 @@
package onclose
import (
"net"
"sync"
)
type CloseHandlerFunc = func()
type Conn struct {
net.Conn
onClose func()
once sync.Once
}
func NewConn(conn net.Conn, onClose func()) *Conn {
return &Conn{Conn: conn, onClose: onClose}
}
func (c *Conn) Close() error {
c.once.Do(c.onClose)
return c.Conn.Close()
}
type PacketConn struct {
net.PacketConn
onClose func()
once sync.Once
}
func NewPacketConn(conn net.PacketConn, onClose func()) *PacketConn {
return &PacketConn{PacketConn: conn, onClose: onClose}
}
func (c *PacketConn) Close() error {
c.once.Do(c.onClose)
return c.PacketConn.Close()
}

View File

@@ -81,7 +81,7 @@ func (f *darwinConnectionFinder) find(network string, source netip.AddrPort, des
source = normalizeDarwinAddrPort(source)
destination = normalizeDarwinAddrPort(destination)
var lastOwner *adapter.ConnectionOwner
for attempt := range 2 {
for attempt := 0; attempt < 2; attempt++ {
snapshot, fromCache, err := f.loadSnapshot(networkName, attempt > 0)
if err != nil {
return nil, err
@@ -261,8 +261,7 @@ func getExecPathFromPID(pid uint32) (string, error) {
procpidpathinfo,
0,
uintptr(unsafe.Pointer(&buf[0])),
procpidpathinfosize,
)
procpidpathinfosize)
if errno != 0 {
return "", errno
}

View File

@@ -1,6 +1,5 @@
//go:build linux
//nolint:unused
package process
import (
@@ -118,7 +117,7 @@ func (c *socketDiagConn) query(source netip.AddrPort, destination netip.AddrPort
c.access.Lock()
defer c.access.Unlock()
request := packSocketDiagRequest(c.family, c.protocol, source, destination, false)
for range 2 {
for attempt := 0; attempt < 2; attempt++ {
err = c.ensureOpenLocked()
if err != nil {
return 0, 0, E.Cause(err, "dial netlink")

View File

@@ -109,7 +109,7 @@ func getInterfaceDisplayName(name string) (string, error) {
if err != nil {
return "", err
}
for deviceSpan := range strings.SplitSeq(string(content), "Ethernet Address") {
for _, deviceSpan := range strings.Split(string(content), "Ethernet Address") {
if strings.Contains(deviceSpan, "Device: "+name) {
substr := "Hardware Port: "
deviceSpan = deviceSpan[strings.Index(deviceSpan, substr)+len(substr):]

View File

@@ -40,14 +40,14 @@ func (m *connmanMonitor) ReadWIFIState() adapter.WIFIState {
defer cancel()
cmObj := m.conn.Object("net.connman", "/")
var services []any
var services []interface{}
err := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0).Store(&services)
if err != nil {
return adapter.WIFIState{}
}
for _, service := range services {
servicePair, ok := service.([]any)
servicePair, ok := service.([]interface{})
if !ok || len(servicePair) != 2 {
continue
}

View File

@@ -1,4 +1,3 @@
//nolint:unused
package settings
import (
@@ -74,13 +73,13 @@ func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
scanner := bufio.NewScanner(strings.NewReader(status))
for scanner.Scan() {
line := scanner.Text()
if after, ok := strings.CutPrefix(line, "wpa_state="); ok {
state := after
if strings.HasPrefix(line, "wpa_state=") {
state := strings.TrimPrefix(line, "wpa_state=")
connected = state == "COMPLETED"
} else if after, ok := strings.CutPrefix(line, "ssid="); ok {
ssid = after
} else if after, ok := strings.CutPrefix(line, "bssid="); ok {
bssid = after
} else if strings.HasPrefix(line, "ssid=") {
ssid = strings.TrimPrefix(line, "ssid=")
} else if strings.HasPrefix(line, "bssid=") {
bssid = strings.TrimPrefix(line, "bssid=")
}
}

View File

@@ -1,6 +1,5 @@
//go:build !linux && !windows
//nolint:unused
package settings
import (

View File

@@ -54,8 +54,9 @@ type xorNonceAEAD struct {
aead cipher.AEAD
}
func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
for i, b := range nonce {

View File

@@ -1,8 +1,6 @@
package sniff
import (
"slices"
"github.com/sagernet/sing-box/common/ja3"
)
@@ -17,8 +15,15 @@ const (
// Note: uQUIC with Chromium mimicry cannot be reliably distinguished from real Chromium
// since it uses the same TLS fingerprint, so it will be identified as Chromium.
func isQUICGo(fingerprint *ja3.ClientHello) bool {
if slices.Contains(fingerprint.EllipticCurves, x25519Kyber768Draft00) {
return true
for _, curve := range fingerprint.EllipticCurves {
if curve == x25519Kyber768Draft00 {
return true
}
}
return slices.Contains(fingerprint.Extensions, extensionRenegotiationInfo)
for _, ext := range fingerprint.Extensions {
if ext == extensionRenegotiationInfo {
return true
}
}
return false
}

View File

@@ -30,7 +30,7 @@ func TestSniffQUICQuicGoFingerprint(t *testing.T) {
go func() {
var packets [][]byte
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
for range 10 {
for i := 0; i < 10; i++ {
buf := make([]byte, 2048)
n, _, err := udpConn.ReadFromUDP(buf)
if err != nil {
@@ -104,7 +104,7 @@ func TestSniffQUICInitialFromQuicGo(t *testing.T) {
go func() {
var packets [][]byte
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
for range 5 { // Capture up to 5 packets
for i := 0; i < 5; i++ { // Capture up to 5 packets
buf := make([]byte, 2048)
n, _, err := udpConn.ReadFromUDP(buf)
if err != nil {

View File

@@ -78,7 +78,7 @@ func Read(reader io.Reader, recover bool) (ruleSetCompat option.PlainRuleSetComp
}
ruleSetCompat.Version = version
ruleSetCompat.Options.Rules = make([]option.HeadlessRule, length)
for i := range length {
for i := uint64(0); i < length; i++ {
ruleSetCompat.Options.Rules[i], err = readRule(bReader, recover)
if err != nil {
err = E.Cause(err, "read rule[", i, "]")
@@ -644,7 +644,7 @@ func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.Lo
return
}
logicalRule.Rules = make([]option.HeadlessRule, length)
for i := range length {
for i := uint64(0); i < length; i++ {
logicalRule.Rules[i], err = readRule(reader, recovery)
if err != nil {
err = E.Cause(err, "read logical rule [", i, "]")

View File

@@ -450,7 +450,7 @@ func buildIPSet(cidrs ...string) *netipx.IPSet {
func buildLargeIPSet(count int) *netipx.IPSet {
var builder netipx.IPSetBuilder
for i := range count {
for i := 0; i < count; i++ {
prefix := netip.PrefixFrom(netip.AddrFrom4([4]byte{10, byte(i / 256), byte(i % 256), 0}), 24)
builder.AddPrefix(prefix)
}

View File

@@ -1,74 +0,0 @@
package tls
import (
"context"
"crypto/ecdsa"
"crypto/tls"
"crypto/x509"
"time"
"github.com/sagernet/quic-go/http3"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
)
func NewMASQUEClient(ctx context.Context, logger logger.ContextLogger, serverName string, cert [][]byte, privateKey *ecdsa.PrivateKey, peerPublicKey *ecdsa.PublicKey, options option.MASQUEOutboundTLSOptions) (Config, error) {
var tlsConfig tls.Config
tlsConfig.ServerName = serverName
tlsConfig.InsecureSkipVerify = true
tlsConfig.NextProtos = []string{http3.NextProtoH3}
tlsConfig.Certificates = []tls.Certificate{
{
Certificate: cert,
PrivateKey: privateKey,
},
}
if options.CipherSuites != nil {
find:
for _, cipherSuite := range options.CipherSuites {
for _, tlsCipherSuite := range tls.CipherSuites() {
if cipherSuite == tlsCipherSuite.Name {
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
continue find
}
}
return nil, E.New("unknown cipher_suite: ", cipherSuite)
}
}
for _, curve := range options.CurvePreferences {
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curve))
}
if !options.Insecure {
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(rawCerts) == 0 {
return nil
}
cert, err := x509.ParseCertificate(rawCerts[0])
if err != nil {
return err
}
if _, ok := cert.PublicKey.(*ecdsa.PublicKey); !ok {
return x509.ErrUnsupportedAlgorithm
}
if !cert.PublicKey.(*ecdsa.PublicKey).Equal(peerPublicKey) {
return x509.CertificateInvalidError{Cert: cert, Reason: 10, Detail: "remote endpoint has a different public key than what we trust in config.json"}
}
return nil
}
}
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
if options.KernelRx || options.KernelTx {
if !C.IsLinux {
return nil, E.New("kTLS is only supported on Linux")
}
config = &KTLSClientConfig{
Config: config,
logger: logger,
kernelTx: options.KernelTx,
kernelRx: options.KernelRx,
}
}
return config, nil
}

View File

@@ -1,135 +0,0 @@
package tls
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"os"
"strings"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
)
func NewOpenVPNClient(ctx context.Context, logger logger.ContextLogger, options option.OpenVPNTLSOptions) (Config, error) {
ca := options.CA
if ca == "" && options.CAPath != "" {
data, err := os.ReadFile(options.CAPath)
if err != nil {
return nil, E.Cause(err, "read ca_path")
}
ca = string(data)
}
certificate := options.Certificate
if certificate == "" && options.CertificatePath != "" {
data, err := os.ReadFile(options.CertificatePath)
if err != nil {
return nil, E.Cause(err, "read certificate_path")
}
certificate = string(data)
}
key := options.Key
if key == "" && options.KeyPath != "" {
data, err := os.ReadFile(options.KeyPath)
if err != nil {
return nil, E.Cause(err, "read key_path")
}
key = string(data)
}
if strings.TrimSpace(ca) == "" {
return nil, E.New("openvpn: missing ca certificate")
}
if block, _ := pem.Decode([]byte(ca)); block == nil {
return nil, E.New("openvpn: ca is not valid PEM")
}
hasCert := strings.TrimSpace(certificate) != "" || strings.TrimSpace(key) != ""
if hasCert {
if strings.TrimSpace(certificate) == "" || strings.TrimSpace(key) == "" {
return nil, E.New("openvpn: certificate and key must both be set")
}
if block, _ := pem.Decode([]byte(certificate)); block == nil {
return nil, E.New("openvpn: certificate is not valid PEM")
}
if block, _ := pem.Decode([]byte(key)); block == nil {
return nil, E.New("openvpn: key is not valid PEM")
}
}
roots := x509.NewCertPool()
if !roots.AppendCertsFromPEM([]byte(ca)) {
return nil, E.New("openvpn: failed to parse ca certificate")
}
var tlsConfig tls.Config
tlsConfig.RootCAs = roots
tlsConfig.InsecureSkipVerify = true
if options.CipherSuites != nil {
find:
for _, cipherSuite := range options.CipherSuites {
for _, tlsCipherSuite := range tls.CipherSuites() {
if cipherSuite == tlsCipherSuite.Name {
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
continue find
}
}
return nil, E.New("unknown cipher_suite: ", cipherSuite)
}
}
tlsConfig.VerifyConnection = func(cs tls.ConnectionState) error {
if len(cs.PeerCertificates) == 0 {
return E.New("openvpn: server did not provide certificate")
}
cert := cs.PeerCertificates[0]
intermediates := x509.NewCertPool()
for _, intermediate := range cs.PeerCertificates[1:] {
intermediates.AddCert(intermediate)
}
_, err := cert.Verify(x509.VerifyOptions{
Roots: roots,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
})
if err != nil {
return err
}
if options.VerifyX509Name != "" {
cn := cert.Subject.CommonName
switch options.VerifyX509NameMode {
case "name-prefix":
if !strings.HasPrefix(cn, options.VerifyX509Name) {
return E.New("openvpn: server CN ", cn, " does not match prefix ", options.VerifyX509Name)
}
case "name-suffix":
if !strings.HasSuffix(cn, options.VerifyX509Name) {
return E.New("openvpn: server CN ", cn, " does not match suffix ", options.VerifyX509Name)
}
default:
if cn != options.VerifyX509Name {
return E.New("openvpn: server CN ", cn, " does not match ", options.VerifyX509Name)
}
}
}
return nil
}
if hasCert {
cert, err := tls.X509KeyPair([]byte(certificate), []byte(key))
if err != nil {
return nil, E.Cause(err, "openvpn: parse client certificate/key")
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
var config Config = &STDClientConfig{ctx, &tlsConfig, false, 0, false}
if options.KernelRx || options.KernelTx {
if !C.IsLinux {
return nil, E.New("kTLS is only supported on Linux")
}
config = &KTLSClientConfig{
Config: config,
logger: logger,
kernelTx: options.KernelTx,
kernelRx: options.KernelRx,
}
}
return config, nil
}

View File

@@ -267,8 +267,8 @@ type realityVerifier struct {
}
func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
p, _ := reflect.TypeFor[utls.Conn]().FieldByName("peerCertificates")
certs := *(*([]*x509.Certificate))(unsafe.Add(unsafe.Pointer(c.Conn), p.Offset))
p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset))
if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {
h := hmac.New(sha512.New, c.authKey)
h.Write(pub)

View File

@@ -141,14 +141,13 @@ func (c *STDServerConfig) startWatcher() error {
func (c *STDServerConfig) certificateUpdated(path string) error {
if path == c.certificatePath || path == c.keyPath {
switch path {
case c.certificatePath:
if path == c.certificatePath {
certificate, err := os.ReadFile(c.certificatePath)
if err != nil {
return E.Cause(err, "reload certificate from ", c.certificatePath)
}
c.certificate = certificate
case c.keyPath:
} else if path == c.keyPath {
key, err := os.ReadFile(c.keyPath)
if err != nil {
return E.Cause(err, "reload key from ", c.keyPath)
@@ -164,7 +163,7 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
config.Certificates = []tls.Certificate{keyPair}
c.config = config
c.access.Unlock()
c.logger.Notice("reloaded TLS certificate")
c.logger.Info("reloaded TLS certificate")
} else if common.Contains(c.clientCertificatePath, path) {
clientCertificateCA := x509.NewCertPool()
var reloaded bool
@@ -188,7 +187,7 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
config.ClientCAs = clientCertificateCA
c.config = config
c.access.Unlock()
c.logger.Notice("reloaded client certificates")
c.logger.Info("reloaded client certificates")
} else if path == c.echKeyPath {
echKey, err := os.ReadFile(c.echKeyPath)
if err != nil {
@@ -198,7 +197,7 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
if err != nil {
return err
}
c.logger.Notice("reloaded ECH keys")
c.logger.Info("reloaded ECH keys")
}
return nil
}
@@ -339,10 +338,9 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
}
tlsConfig.ClientCAs = clientCertificateCA
} else if len(options.ClientCertificatePublicKeySHA256) > 0 {
switch tlsConfig.ClientAuth {
case tls.RequireAndVerifyClientCert:
if tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {
tlsConfig.ClientAuth = tls.RequireAnyClientCert
case tls.VerifyClientCertIfGiven:
} else if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven {
tlsConfig.ClientAuth = tls.RequestClientCert
}
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {

View File

@@ -1,78 +0,0 @@
package common
import (
"encoding/base64"
"encoding/json"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"github.com/sagernet/sing/common/json/badoption"
)
func StringToType[T any](str string) T {
var value T
v := reflect.ValueOf(&value).Elem()
switch any(value).(type) {
case badoption.Duration:
d, err := time.ParseDuration(str)
if err != nil {
v.SetInt(StringToType[int64](str))
} else {
v.Set(reflect.ValueOf(d))
}
return value
case badoption.HTTPHeader:
headers := badoption.HTTPHeader{}
reg := regexp.MustCompile(`^[ \t]*?(\S+?):[ \t]*?(\S+?)[ \t]*?$`)
for _, header := range strings.Split(str, "\n") {
result := reg.FindStringSubmatch(header)
if result != nil {
key := result[1]
headers[key] = strings.Split(result[2], ",")
}
}
v.Set(reflect.ValueOf(headers))
return value
}
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i, _ := strconv.ParseInt(str, 10, 64)
v.SetInt(i)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
i, _ := strconv.ParseUint(str, 10, 64)
v.SetUint(i)
case reflect.Float32, reflect.Float64:
f, _ := strconv.ParseFloat(str, 64)
v.SetFloat(f)
case reflect.Bool:
b, _ := strconv.ParseBool(str)
v.SetBool(b)
default:
panic("unsupported type")
}
return value
}
func DecodeBase64URLSafe(content string) (string, error) {
s := strings.ReplaceAll(content, " ", "-")
s = strings.ReplaceAll(s, "/", "_")
s = strings.ReplaceAll(s, "+", "-")
s = strings.ReplaceAll(s, "=", "")
result, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
return content, nil
}
return string(result), nil
}
func ParseXHTTPRange(value string) (badoption.Range[int], error) {
result := badoption.Range[int]{}
encoded, err := json.Marshal(value)
if err != nil {
return result, err
}
return result, result.UnmarshalJSON(encoded)
}

View File

@@ -1,25 +0,0 @@
package vision
import (
"context"
"net"
)
type Hook func(net.Conn)
type hookKey struct{}
func WithHook(ctx context.Context, hook Hook) context.Context {
if hook == nil {
return ctx
}
return context.WithValue(ctx, hookKey{}, hook)
}
func HookFromContext(ctx context.Context) (Hook, bool) {
if ctx == nil {
return nil, false
}
hook, ok := ctx.Value(hookKey{}).(Hook)
return hook, ok
}

View File

@@ -38,8 +38,8 @@ func MergeMulti(dest MultiBuffer, src MultiBuffer) (MultiBuffer, MultiBuffer) {
// MergeBytes merges the given bytes into MultiBuffer and return the new address of the merged MultiBuffer.
func MergeBytes(dest MultiBuffer, src []byte) MultiBuffer {
n := len(dest)
if n > 0 && !dest[n-1].IsFull() {
nBytes, _ := dest[n-1].Write(src)
if n > 0 && !(dest)[n-1].IsFull() {
nBytes, _ := (dest)[n-1].Write(src)
src = src[nBytes:]
}

View File

@@ -1,18 +0,0 @@
package cpuid
import (
"runtime"
"golang.org/x/sys/cpu"
)
var (
// Keep in sync with crypto/tls/cipher_suites.go.
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasGHASH
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
// HasAESGCM indicates whether the CPU has AES-GCM hardware acceleration.
HasAESGCM = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
)

View File

@@ -0,0 +1,76 @@
package badoption
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"github.com/sagernet/sing-box/common/xray/crypto"
E "github.com/sagernet/sing/common/exceptions"
)
type Range struct {
From int32 `json:"from"`
To int32 `json:"to"`
}
func (c *Range) Build() *Range {
return (*Range)(c)
}
func (c *Range) MarshalJSON() ([]byte, error) {
if c.From == c.To {
return json.Marshal(c.From)
}
return json.Marshal(fmt.Sprintf("%d-%d", c.From, c.To))
}
func (c *Range) UnmarshalJSON(content []byte) error {
var rangeValue struct {
From int32 `json:"from"`
To int32 `json:"to"`
}
var stringValue string
err := json.Unmarshal(content, &stringValue)
if err == nil {
parts := strings.Split(stringValue, "-")
if len(parts) != 2 {
from, err := strconv.ParseInt(parts[0], 10, 32)
if err != nil {
return err
}
rangeValue.From, rangeValue.To = int32(from), int32(from)
} else {
from, err := strconv.ParseInt(parts[0], 10, 32)
if err != nil {
return err
}
to, err := strconv.ParseInt(parts[1], 10, 32)
if err != nil {
return err
}
rangeValue.From, rangeValue.To = int32(from), int32(to)
}
} else {
var int32Value int32
err := json.Unmarshal(content, &int32Value)
if err == nil {
rangeValue.From, rangeValue.To = int32Value, int32Value
} else {
err := json.Unmarshal(content, &rangeValue)
if err != nil {
return err
}
}
}
if rangeValue.From > rangeValue.To {
return E.New("invalid range")
}
*c = Range{rangeValue.From, rangeValue.To}
return nil
}
func (c Range) Rand() int32 {
return int32(crypto.RandBetween(int64(c.From), int64(c.To)))
}

View File

@@ -42,7 +42,7 @@ func New(opts ...Option) (*Reader, *Writer) {
}
for _, opt := range opts {
opt(&p.option)
opt(&(p.option))
}
return &Reader{

View File

@@ -1,290 +1,28 @@
package utils
import (
"hash/fnv"
"math"
"math/rand"
"net/http"
"strconv"
"strings"
"time"
"github.com/klauspost/cpuid/v2"
)
func GetRandomizer() *rand.Rand {
// Seed the PRNG with the hash of CPU info, increasing the overall probable space.
fnvHash := fnv.New64()
fnvHash.Write([]byte(strconv.Itoa(cpuid.CPU.Family) + strconv.Itoa(cpuid.CPU.Model) + strconv.Itoa(cpuid.CPU.PhysicalCores) + strconv.Itoa(cpuid.CPU.LogicalCores) + strconv.Itoa(cpuid.CPU.CacheLine) + strconv.Itoa(cpuid.CPU.ThreadsPerCore)))
return rand.New(rand.NewSource(int64(fnvHash.Sum64())))
}
var globalRng *rand.Rand = GetRandomizer()
// The Chrome version generator will suffer from deviation of a normal distribution.
func ChromeVersion() int {
// Start from Chrome 144, released on 2026.1.13.
var startVersion int = 144
var timeStart int64 = time.Date(2026, 1, 13, 0, 0, 0, 0, time.UTC).Unix() / 86400
var timeCurrent int64 = time.Now().Unix() / 86400
var timeDiff int = int((timeCurrent - timeStart - 35)) - int(math.Floor(math.Pow(globalRng.Float64(), 2)*105))
return startVersion + (timeDiff / 35) // It's 31.15 currently.
}
var safariMinorMap [25]int = [25]int{
0, 0, 0, 1, 1,
1, 2, 2, 2, 2, 3, 3, 3, 4, 4,
4, 5, 5, 5, 5, 5, 6, 6, 6, 6,
}
// The following version generators use deterministic generators, but with the distribution scaled by a curve.
func CurlVersion() string {
// curl 8.0.0 was released on 20/03/2023.
var timeCurrent int64 = time.Now().Unix() / 86400
var timeStart int64 = time.Date(2023, 3, 20, 0, 0, 0, 0, time.UTC).Unix() / 86400
var timeDiff int = int((timeCurrent - timeStart - 60)) - int(math.Floor(math.Pow(globalRng.Float64(), 2)*165))
var minorValue int = int(timeDiff / 57) // The release cadence is actually 56.67 days.
return "8." + strconv.Itoa(minorValue) + ".0"
}
func FirefoxVersion() int {
// Firefox 128 ESR was released on 09/07/2023.
var timeCurrent int64 = time.Now().Unix() / 86400
var timeStart int64 = time.Date(2024, 7, 29, 0, 0, 0, 0, time.UTC).Unix() / 86400
timeDiff := timeCurrent - timeStart - 25 - int64(math.Floor(math.Pow(globalRng.Float64(), 2)*50))
return int(timeDiff/30) + 128
}
func SafariVersion() string {
var anchoredTime time.Time = time.Now()
var releaseYear int = anchoredTime.Year()
var splitPoint time.Time = time.Date(releaseYear, 9, 23, 0, 0, 0, 0, time.UTC)
delayedDays := int(math.Floor(math.Pow(globalRng.Float64(), 3) * 75))
splitPoint = splitPoint.AddDate(0, 0, delayedDays)
if anchoredTime.Compare(splitPoint) < 0 {
releaseYear--
splitPoint = time.Date(releaseYear, 9, 23, 0, 0, 0, 0, time.UTC)
splitPoint = splitPoint.AddDate(0, 0, delayedDays)
// Use only CPU info as seed for PRNG
seed := int64(cpuid.CPU.Family + cpuid.CPU.Model + cpuid.CPU.PhysicalCores + cpuid.CPU.LogicalCores + cpuid.CPU.CacheLine)
rng := rand.New(rand.NewSource(seed))
// Start from Chrome 144 released on 2026.1.13
releaseDate := time.Date(2026, 1, 13, 0, 0, 0, 0, time.UTC)
version := 144
now := time.Now()
// Each version has random 25-45 day interval
for releaseDate.Before(now) {
releaseDate = releaseDate.AddDate(0, 0, rng.Intn(21)+25)
version++
}
minorVersion := safariMinorMap[(anchoredTime.Unix()-splitPoint.Unix())/1296000]
return strconv.Itoa(releaseYear-1999) + "." + strconv.Itoa(minorVersion)
return version - 1
}
// The full Chromium brand GREASE implementation
var (
clientHintGreaseNA = []string{" ", "(", ":", "-", ".", "/", ")", ";", "=", "?", "_"}
clientHintVersionNA = []string{"8", "99", "24"}
clientHintShuffle3 = [][3]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}
clientHintShuffle4 = [][4]int{
{0, 1, 2, 3},
{0, 1, 3, 2},
{0, 2, 1, 3},
{0, 2, 3, 1},
{0, 3, 1, 2},
{0, 3, 2, 1},
{1, 0, 2, 3},
{1, 0, 3, 2},
{1, 2, 0, 3},
{1, 2, 3, 0},
{1, 3, 0, 2},
{1, 3, 2, 0},
{2, 0, 1, 3},
{2, 0, 3, 1},
{2, 1, 0, 3},
{2, 1, 3, 0},
{2, 3, 0, 1},
{2, 3, 1, 0},
{3, 0, 1, 2},
{3, 0, 2, 1},
{3, 1, 0, 2},
{3, 1, 2, 0},
{3, 2, 0, 1},
{3, 2, 1, 0},
}
)
func getGreasedChInvalidBrand(seed int) string {
return "\"Not" + clientHintGreaseNA[seed%len(clientHintGreaseNA)] + "A" + clientHintGreaseNA[(seed+1)%len(clientHintGreaseNA)] + "Brand\";v=\"" + clientHintVersionNA[seed%len(clientHintVersionNA)] + "\""
}
func getGreasedChOrder(brandLength int, seed int) []int {
switch brandLength {
case 1:
return []int{0}
case 2:
return []int{seed % brandLength, (seed + 1) % brandLength}
case 3:
return clientHintShuffle3[seed%len(clientHintShuffle3)][:]
default:
return clientHintShuffle4[seed%len(clientHintShuffle4)][:]
}
//return []int{}
}
func getUngreasedChUa(majorVersion int, forkName string) []string {
// Set the capacity to 4, the maximum allowed brand size, so Go will never allocate memory twice
baseChUa := make([]string, 0, 4)
baseChUa = append(baseChUa, getGreasedChInvalidBrand(majorVersion),
"\"Chromium\";v=\""+strconv.Itoa(majorVersion)+"\"")
switch forkName {
case "chrome":
baseChUa = append(baseChUa, "\"Google Chrome\";v=\""+strconv.Itoa(majorVersion)+"\"")
case "edge":
baseChUa = append(baseChUa, "\"Microsoft Edge\";v=\""+strconv.Itoa(majorVersion)+"\"")
}
return baseChUa
}
func getGreasedChUa(majorVersion int, forkName string) string {
ungreasedCh := getUngreasedChUa(majorVersion, forkName)
shuffleMap := getGreasedChOrder(len(ungreasedCh), majorVersion)
shuffledCh := make([]string, len(ungreasedCh))
for i, e := range shuffleMap {
shuffledCh[e] = ungreasedCh[i]
}
return strings.Join(shuffledCh, ", ")
}
// The code below provides a coherent default browser user agent string based on a CPU-seeded PRNG.
var (
CurlUA = "curl/" + CurlVersion()
AnchoredFirefoxVersion = strconv.Itoa(FirefoxVersion())
FirefoxUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:" + AnchoredFirefoxVersion + ".0) Gecko/20100101 Firefox/" + AnchoredFirefoxVersion + ".0"
SafariUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/" + SafariVersion() + " Safari/605.1.15"
)
// Chromium browsers.
var (
AnchoredChromeVersion = ChromeVersion()
ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0 Safari/537.36"
ChromeUACH = getGreasedChUa(AnchoredChromeVersion, "chrome")
MSEdgeUA = ChromeUA + "Edg/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0"
MSEdgeUACH = getGreasedChUa(AnchoredChromeVersion, "edge")
)
func applyMasqueradedHeaders(header http.Header, browser string, variant string) {
// Browser-specific.
switch browser {
case "chrome":
header["Sec-CH-UA"] = []string{ChromeUACH}
header["Sec-CH-UA-Mobile"] = []string{"?0"}
header["Sec-CH-UA-Platform"] = []string{"\"Windows\""}
header["DNT"] = []string{"1"}
header.Set("User-Agent", ChromeUA)
header.Set("Accept-Language", "en-US,en;q=0.9")
case "edge":
header["Sec-CH-UA"] = []string{MSEdgeUACH}
header["Sec-CH-UA-Mobile"] = []string{"?0"}
header["Sec-CH-UA-Platform"] = []string{"\"Windows\""}
header["DNT"] = []string{"1"}
header.Set("User-Agent", MSEdgeUA)
header.Set("Accept-Language", "en-US,en;q=0.9")
case "firefox":
header.Set("User-Agent", FirefoxUA)
header["DNT"] = []string{"1"}
header.Set("Accept-Language", "en-US,en;q=0.5")
case "safari":
header.Set("User-Agent", SafariUA)
header.Set("Accept-Language", "en-US,en;q=0.9")
case "golang":
// Expose the default net/http header.
header.Del("User-Agent")
return
case "curl":
header.Set("User-Agent", CurlUA)
return
}
// Context-specific.
switch variant {
case "nav":
if header.Get("Cache-Control") == "" {
switch browser {
case "chrome", "edge":
header.Set("Cache-Control", "max-age=0")
}
}
header.Set("Upgrade-Insecure-Requests", "1")
if header.Get("Accept") == "" {
switch browser {
case "chrome", "edge":
header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
case "firefox", "safari":
header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
}
}
header.Set("Sec-Fetch-Site", "none")
header.Set("Sec-Fetch-Mode", "navigate")
switch browser {
case "safari":
default:
header.Set("Sec-Fetch-User", "?1")
}
header.Set("Sec-Fetch-Dest", "document")
header.Set("Priority", "u=0, i")
case "ws":
header.Set("Sec-Fetch-Mode", "websocket")
switch browser {
case "safari":
// Safari is NOT web-compliant here!
header.Set("Sec-Fetch-Dest", "websocket")
default:
header.Set("Sec-Fetch-Dest", "empty")
}
header.Set("Sec-Fetch-Site", "same-origin")
if header.Get("Cache-Control") == "" {
header.Set("Cache-Control", "no-cache")
}
if header.Get("Pragma") == "" {
header.Set("Pragma", "no-cache")
}
if header.Get("Accept") == "" {
header.Set("Accept", "*/*")
}
case "fetch":
header.Set("Sec-Fetch-Mode", "cors")
header.Set("Sec-Fetch-Dest", "empty")
header.Set("Sec-Fetch-Site", "same-origin")
if header.Get("Priority") == "" {
switch browser {
case "chrome", "edge":
header.Set("Priority", "u=1, i")
case "firefox":
header.Set("Priority", "u=4")
case "safari":
header.Set("Priority", "u=3, i")
}
}
if header.Get("Cache-Control") == "" {
header.Set("Cache-Control", "no-cache")
}
if header.Get("Pragma") == "" {
header.Set("Pragma", "no-cache")
}
if header.Get("Accept") == "" {
header.Set("Accept", "*/*")
}
}
}
func TryDefaultHeadersWith(header http.Header, variant string) {
// The global UA special value handler for transports. Used to be called HandleTransportUASettings.
// Just a FYI to whoever needing to fix this piece of code after some spontaneous event, I tried to make the two methods separate to let the code be cleaner and more organized.
if len(header.Values("User-Agent")) < 1 {
applyMasqueradedHeaders(header, "chrome", variant)
} else {
switch header.Get("User-Agent") {
case "chrome":
applyMasqueradedHeaders(header, "chrome", variant)
case "firefox":
applyMasqueradedHeaders(header, "firefox", variant)
case "safari":
applyMasqueradedHeaders(header, "safari", variant)
case "edge":
applyMasqueradedHeaders(header, "edge", variant)
case "curl":
applyMasqueradedHeaders(header, "curl", variant)
case "golang":
applyMasqueradedHeaders(header, "golang", variant)
}
}
}
// ChromeUA provides default browser User-Agent based on CPU-seeded PRNG.
var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(ChromeVersion()) + ".0.0.0 Safari/537.36"

View File

@@ -29,7 +29,6 @@ const (
DNSTypeDHCP = "dhcp"
DNSTypeTailscale = "tailscale"
DNSTypeSDNS = "sdns"
DNSTypeFallback = "fallback"
)
const (

View File

@@ -1,9 +0,0 @@
package constant
const (
ManagerAPIServer = "server"
ManagerAPIClient = "client"
ManagerAPIProtocolHTTP = "http"
ManagerAPIProtocolGrpc = "grpc"
)

View File

@@ -1,6 +0,0 @@
package constant
const (
NodeManagerAPIServer = "server"
NodeManagerAPIClient = "client"
)

View File

@@ -1,20 +0,0 @@
package constant
const (
ProviderTypeInline = "inline"
ProviderTypeLocal = "local"
ProviderTypeRemote = "remote"
)
func ProviderDisplayName(providerType string) string {
switch providerType {
case ProviderTypeInline:
return "Inline"
case ProviderTypeLocal:
return "Local"
case ProviderTypeRemote:
return "Remote"
default:
return "Unknown"
}
}

View File

@@ -1,64 +1,43 @@
package constant
const (
TypeTun = "tun"
TypeRedirect = "redirect"
TypeTProxy = "tproxy"
TypeDirect = "direct"
TypeBlock = "block"
TypeDNS = "dns"
TypeSOCKS = "socks"
TypeHTTP = "http"
TypeMixed = "mixed"
TypeShadowsocks = "shadowsocks"
TypeVMess = "vmess"
TypeTrojan = "trojan"
TypeTrustTunnel = "trusttunnel"
TypeNaive = "naive"
TypeWireGuard = "wireguard"
TypeWARP = "warp"
TypeMASQUE = "masque"
TypeOpenVPN = "openvpn"
TypeMTProxy = "mtproxy"
TypeParser = "parser"
TypeHysteria = "hysteria"
TypeTor = "tor"
TypeSSH = "ssh"
TypeShadowTLS = "shadowtls"
TypeMieru = "mieru"
TypeAnyTLS = "anytls"
TypeSudoku = "sudoku"
TypeSnell = "snell"
TypeShadowsocksR = "shadowsocksr"
TypeVLESS = "vless"
TypeTUIC = "tuic"
TypeHysteria2 = "hysteria2"
TypeBond = "bond"
TypeFailover = "failover"
TypeVPNServer = "vpn-server"
TypeVPNClient = "vpn-client"
TypeTailscale = "tailscale"
TypeConnectionLimiter = "connection-limiter"
TypeBandwidthLimiter = "bandwidth-limiter"
TypeTrafficLimiter = "traffic-limiter"
TypeRateLimiter = "rate-limiter"
TypeFairQueue = "fair-queue"
TypeAdminPanel = "admin-panel"
TypeManagerAPI = "manager-api"
TypeNodeManagerAPI = "node-manager-api"
TypeDERP = "derp"
TypeManager = "manager"
TypeNode = "node"
TypeResolved = "resolved"
TypeSSMAPI = "ssm-api"
TypeCCM = "ccm"
TypeOCM = "ocm"
TypeOOMKiller = "oom-killer"
TypeProfiler = "profiler"
TypeTun = "tun"
TypeRedirect = "redirect"
TypeTProxy = "tproxy"
TypeDirect = "direct"
TypeBlock = "block"
TypeDNS = "dns"
TypeSOCKS = "socks"
TypeHTTP = "http"
TypeMixed = "mixed"
TypeShadowsocks = "shadowsocks"
TypeVMess = "vmess"
TypeTrojan = "trojan"
TypeNaive = "naive"
TypeWireGuard = "wireguard"
TypeWARP = "warp"
TypeHysteria = "hysteria"
TypeTor = "tor"
TypeSSH = "ssh"
TypeShadowTLS = "shadowtls"
TypeMieru = "mieru"
TypeAnyTLS = "anytls"
TypeShadowsocksR = "shadowsocksr"
TypeVLESS = "vless"
TypeTUIC = "tuic"
TypeHysteria2 = "hysteria2"
TypeTunnelClient = "tunnel_client"
TypeTunnelServer = "tunnel_server"
TypeTailscale = "tailscale"
TypeDERP = "derp"
TypeResolved = "resolved"
TypeSSMAPI = "ssm-api"
TypeCCM = "ccm"
TypeOCM = "ocm"
TypeOOMKiller = "oom-killer"
)
const (
TypeFallback = "fallback"
TypeSelector = "selector"
TypeURLTest = "urltest"
)
@@ -89,22 +68,12 @@ func ProxyDisplayName(proxyType string) string {
return "VMess"
case TypeTrojan:
return "Trojan"
case TypeTrustTunnel:
return "TrustTunnel"
case TypeNaive:
return "Naive"
case TypeWireGuard:
return "WireGuard"
case TypeWARP:
return "WARP"
case TypeMASQUE:
return "MASQUE"
case TypeOpenVPN:
return "OpenVPN"
case TypeMTProxy:
return "MTProxy"
case TypeParser:
return "Parser"
case TypeHysteria:
return "Hysteria"
case TypeTor:
@@ -121,42 +90,20 @@ func ProxyDisplayName(proxyType string) string {
return "TUIC"
case TypeHysteria2:
return "Hysteria2"
case TypeBond:
return "Bond"
case TypeFailover:
return "Failover"
case TypeMieru:
return "Mieru"
case TypeAnyTLS:
return "AnyTLS"
case TypeSudoku:
return "Sudoku"
case TypeSnell:
return "Snell"
case TypeFallback:
return "Fallback"
case TypeTailscale:
return "Tailscale"
case TypeSelector:
return "Selector"
case TypeURLTest:
return "URLTest"
case TypeConnectionLimiter:
return "Connection Limiter"
case TypeBandwidthLimiter:
return "Bandwidth Limiter"
case TypeTrafficLimiter:
return "Traffic Limiter"
case TypeRateLimiter:
return "Rate Limiter"
case TypeFairQueue:
return "Fair Queue"
case TypeVPNClient:
return "VPN Client"
case TypeVPNServer:
return "VPN Server"
case TypeProfiler:
return "Profiler"
case TypeTunnelClient:
return "Tunnel Client"
case TypeTunnelServer:
return "Tunnel Server"
default:
return "Unknown"
}

View File

@@ -10,7 +10,6 @@ const (
ReadPayloadTimeout = 300 * time.Millisecond
DNSTimeout = 10 * time.Second
UDPTimeout = 5 * time.Minute
ICMPTimeout = 10 * time.Second
DefaultURLTestInterval = 3 * time.Minute
DefaultURLTestIdleTimeout = 30 * time.Minute
StartTimeout = 10 * time.Second

View File

@@ -7,5 +7,4 @@ const (
V2RayTransportTypeGRPC = "grpc"
V2RayTransportTypeHTTPUpgrade = "httpupgrade"
V2RayTransportTypeXHTTP = "xhttp"
V2RayTransportTypeKCP = "mkcp"
)

20
constant/warp.go Normal file
View File

@@ -0,0 +1,20 @@
package constant
type WARPConfig struct {
PrivateKey string `json:"private_key"`
Interface struct {
Addresses struct {
V4 string `json:"v4"`
V6 string `json:"v6"`
} `json:"addresses"`
} `json:"interface"`
Peers []struct {
PublicKey string `json:"public_key"`
Endpoint struct {
V4 string `json:"v4"`
V6 string `json:"v6"`
Host string `json:"host"`
Ports []int `json:"ports"`
} `json:"endpoint"`
} `json:"peers"`
}

View File

@@ -603,7 +603,10 @@ func (s *StartedService) URLTest(ctx context.Context, request *URLTestRequest) (
return false
}
_, isGroup := it.(adapter.OutboundGroup)
return !isGroup
if isGroup {
return false
}
return true
})
b, _ := batch.New(boxService.ctx, batch.WithConcurrencyNum[any](10))
for _, detour := range outbounds {

Some files were not shown because too many files have changed in this diff Show More