From 6a051054dba8d5d2b3367bbdf7105cc863aa809b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 19 Apr 2025 18:38:12 +0800 Subject: [PATCH 01/94] release: Fix packages --- .github/workflows/build.yml | 2 +- .github/workflows/linux.yml | 2 + docs/installation/package-manager.md | 83 ++++++++-------- docs/installation/package-manager.zh.md | 43 ++++---- docs/installation/tools/install.sh | 127 ++++++++++++++---------- 5 files changed, 139 insertions(+), 118 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d8232bb..2a7c4b0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -181,7 +181,7 @@ jobs: fi echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}" PKG_VERSION="${{ needs.calculate_version.outputs.version }}" - PKG_VERSION="${PKG_VERSION//-/\~}-1" + PKG_VERSION="${PKG_VERSION//-/\~}" echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}" - name: Package DEB if: matrix.debian != '' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 965929df..7393e654 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -120,6 +120,7 @@ jobs: set -xeuo pipefail sudo gem install fpm sudo apt-get install -y debsigs + cp .fpm_systemd .fpm fpm -t deb \ --name "${NAME}" \ -v "$PKG_VERSION" \ @@ -138,6 +139,7 @@ jobs: run: |- set -xeuo pipefail sudo gem install fpm + cp .fpm_systemd .fpm fpm -t rpm \ --name "${NAME}" \ -v "$PKG_VERSION" \ diff --git a/docs/installation/package-manager.md b/docs/installation/package-manager.md index c54eee87..fe59c73a 100644 --- a/docs/installation/package-manager.md +++ b/docs/installation/package-manager.md @@ -8,56 +8,57 @@ icon: material/package === ":material-debian: Debian / APT" - ```bash - sudo mkdir -p /etc/apt/keyrings && - sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc && - sudo chmod a+r /etc/apt/keyrings/sagernet.asc && - echo ' - Types: deb - URIs: https://deb.sagernet.org/ - Suites: * - Components: * - Enabled: yes - Signed-By: /etc/apt/keyrings/sagernet.asc - ' | sudo tee /etc/apt/sources.list.d/sagernet.sources && - sudo apt-get update && - sudo apt-get install sing-box # or sing-box-beta - ``` + ```bash + sudo mkdir -p /etc/apt/keyrings && + sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc && + sudo chmod a+r /etc/apt/keyrings/sagernet.asc && + echo ' + Types: deb + URIs: https://deb.sagernet.org/ + Suites: * + Components: * + Enabled: yes + Signed-By: /etc/apt/keyrings/sagernet.asc + ' | sudo tee /etc/apt/sources.list.d/sagernet.sources && + sudo apt-get update && + sudo apt-get install sing-box # or sing-box-beta + ``` === ":material-redhat: Redhat / DNF 5" - ```bash - sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo && - sudo dnf install sing-box # or sing-box-beta - ``` + ```bash + sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo && + sudo dnf install sing-box # or sing-box-beta + ``` === ":material-redhat: Redhat / DNF 4" - ```bash - sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo && - sudo dnf -y install dnf-plugins-core && - sudo dnf install sing-box # or sing-box-beta - ``` + ```bash + sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo && + sudo dnf -y install dnf-plugins-core && + sudo dnf install sing-box # or sing-box-beta + ``` ## :material-download-box: Manual Installation - The script download and install the latest package from GitHub releases for deb or rpm based Linux distributions, ArchLinux and OpenWrt. - - ```shell - curl -fsSL https://sing-box.app/install.sh | sh - ``` - - or latest beta: - - ```shell - curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta - ``` - - or specific version: - - ```shell - curl -fsSL https://sing-box.app/install.sh | sh -s -- --version - ``` +The script download and install the latest package from GitHub releases +for deb or rpm based Linux distributions, ArchLinux and OpenWrt. + +```shell +curl -fsSL https://sing-box.app/install.sh | sh +``` + +or latest beta: + +```shell +curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta +``` + +or specific version: + +```shell +curl -fsSL https://sing-box.app/install.sh | sh -s -- --version +``` ## :material-book-lock-open: Managed Installation diff --git a/docs/installation/package-manager.zh.md b/docs/installation/package-manager.zh.md index b59e95cb..60905d1d 100644 --- a/docs/installation/package-manager.zh.md +++ b/docs/installation/package-manager.zh.md @@ -26,39 +26,38 @@ icon: material/package === ":material-redhat: Redhat / DNF 5" - ```bash - sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo && - sudo dnf install sing-box # or sing-box-beta - ``` + ```bash + sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo && + sudo dnf install sing-box # or sing-box-beta + ``` === ":material-redhat: Redhat / DNF 4" - ```bash - sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo && - sudo dnf -y install dnf-plugins-core && - sudo dnf install sing-box # or sing-box-beta - ``` + ```bash + sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo && + sudo dnf -y install dnf-plugins-core && + sudo dnf install sing-box # or sing-box-beta + ``` ## :material-download-box: 手动安装 -=== ":material-debian: Debian / DEB" +该脚本从 GitHub 发布中下载并安装最新的软件包,适用于基于 deb 或 rpm 的 Linux 发行版、ArchLinux 和 OpenWrt。 - ```bash - bash <(curl -fsSL https://sing-box.app/deb-install.sh) - ``` +```shell +curl -fsSL https://sing-box.app/install.sh | sh +``` -=== ":material-redhat: Redhat / RPM" +或最新测试版: - ```bash - bash <(curl -fsSL https://sing-box.app/rpm-install.sh) - ``` - (这适用于任何使用 `rpm` 和 `systemd` 的发行版。由于 `rpm` 定义依赖关系的方式,如果安装成功,就多半能用。) +```shell +curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta +``` -=== ":simple-archlinux: Archlinux / PKG" +或指定版本: - ```bash - bash <(curl -fsSL https://sing-box.app/arch-install.sh) - ``` +```shell +curl -fsSL https://sing-box.app/install.sh | sh -s -- --version +``` ## :material-book-lock-open: 托管安装 diff --git a/docs/installation/tools/install.sh b/docs/installation/tools/install.sh index e042dde8..74166f02 100755 --- a/docs/installation/tools/install.sh +++ b/docs/installation/tools/install.sh @@ -3,76 +3,92 @@ download_beta=false download_version="" -for arg in "$@"; do - if [[ "$arg" == "--beta" ]]; then - download_beta=true - elif [[ "$arg" == "--version" ]]; then - download_version=true - elif [[ "$download_version" == 'true' ]]; then - download_version="$arg" - else - echo "Unknown argument: $arg" - echo "Usage: $0 [--beta] [--version ]" - exit 1 - fi +while [ $# -gt 0 ]; do + case "$1" in + --beta) + download_beta=true + shift + ;; + --version) + shift + if [ $# -eq 0 ]; then + echo "Missing argument for --version" + echo "Usage: $0 [--beta] [--version ]" + exit 1 + fi + download_version="$1" + shift + ;; + *) + echo "Unknown argument: $1" + echo "Usage: $0 [--beta] [--version ]" + exit 1 + ;; + esac done -if [[ $(command -v dpkg) ]]; then - os="linux" - arch=$(dpkg --print-architecture) - package_suffix=".deb" - package_install="dpkg -i" -elif [[ $(command -v dnf) ]]; then - os="linux" - arch=$(uname -m) - package_suffix=".rpm" - package_install="dnf install -y" -elif [[ $(command -v rpm) ]]; then - os="linux" - arch=$(uname -m) - package_suffix=".rpm" - package_install="rpm -i" -elif [[ $(command -v pacman) ]]; then +if command -v pacman >/dev/null 2>&1; then os="linux" arch=$(uname -m) package_suffix=".pkg.tar.zst" package_install="pacman -U --noconfirm" -elif [[ $(command -v opkg) ]]; then +elif command -v dpkg >/dev/null 2>&1; then + os="linux" + arch=$(dpkg --print-architecture) + package_suffix=".deb" + package_install="dpkg -i" +elif command -v dnf >/dev/null 2>&1; then + os="linux" + arch=$(uname -m) + package_suffix=".rpm" + package_install="dnf install -y" +elif command -v rpm >/dev/null 2>&1; then + os="linux" + arch=$(uname -m) + package_suffix=".rpm" + package_install="rpm -i" +elif command -v opkg >/dev/null 2>&1; then os="openwrt" - source /etc/os-release + . /etc/os-release arch="$OPENWRT_ARCH" package_suffix=".ipk" - package_install="opkg update && opkg install -y" + package_install="opkg update && opkg install" else echo "Missing supported package manager." exit 1 fi -if [[ -z "$download_version" ]]; then - if [[ "$download_beta" != 'true' ]]; then - if [[ -n "$GITHUB_TOKEN" ]]; then - latest_release=$(curl -s --fail-with-body -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases/latest) +if [ -z "$download_version" ]; then + if [ "$download_beta" != "true" ]; then + if [ -n "$GITHUB_TOKEN" ]; then + latest_release=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases/latest) else - latest_release=$(curl -s --fail-with-body https://api.github.com/repos/SagerNet/sing-box/releases/latest) + latest_release=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases/latest) fi curl_exit_status=$? - if [[ $curl_exit_status -ne 0 ]]; then - echo "$latest_release" - exit $? + if [ $curl_exit_status -ne 0 ]; then + exit $curl_exit_status fi - download_version=$(echo "$latest_release" | grep tag_name | cut -d ":" -f2 | sed 's/\"//g;s/\,//g;s/\ //g;s/v//') + if [ "$(echo "$latest_release" | grep tag_name | wc -l)" -eq 0 ]; then + echo "$latest_release" + exit 1 + fi + download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | awk -F: '{print $2}' | sed 's/[", v]//g') else - if [[ -n "$GITHUB_TOKEN" ]]; then - latest_release=$(curl -s --fail-with-body -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases) + if [ -n "$GITHUB_TOKEN" ]; then + latest_release=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases) else - latest_release=$(curl -s --fail-with-body https://api.github.com/repos/SagerNet/sing-box/releases) + latest_release=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases) fi curl_exit_status=$? - if [[ $? -ne 0 ]]; then - echo "$latest_release" - exit $? + if [ $curl_exit_status -ne 0 ]; then + exit $curl_exit_status fi - download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | cut -d ":" -f2 | sed 's/\"//g;s/\,//g;s/\ //g;s/v//') + if [ "$(echo "$latest_release" | grep tag_name | wc -l)" -eq 0 ]; then + echo "$latest_release" + exit 1 + fi + download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | awk -F: '{print $2}' | sed 's/[", v]//g') fi fi @@ -80,18 +96,21 @@ package_name="sing-box_${download_version}_${os}_${arch}${package_suffix}" package_url="https://github.com/SagerNet/sing-box/releases/download/v${download_version}/${package_name}" echo "Downloading $package_url" -if [[ -n "$GITHUB_TOKEN" ]]; then - curl --fail-with-body -Lo "$package_name" -H "Authorization: token ${GITHUB_TOKEN}" "$package_url" +if [ -n "$GITHUB_TOKEN" ]; then + curl --fail -Lo "$package_name" -H "Authorization: token ${GITHUB_TOKEN}" "$package_url" else - curl --fail-with-body -Lo "$package_name" "$package_url" + curl --fail -Lo "$package_name" "$package_url" fi -if [[ $? -ne 0 ]]; then - exit $? +curl_exit_status=$? +if [ $curl_exit_status -ne 0 ]; then + exit $curl_exit_status fi -if [[ $(command -v sudo) ]]; then +if command -v sudo >/dev/null 2>&1; then package_install="sudo $package_install" fi -echo "$package_install $package_name" && $package_install "$package_name" && rm "$package_name" +echo "$package_install $package_name" +sh -c "$package_install \"$package_name\"" +rm -f "$package_name" From c54d50fd36b786cf1f7912b14e5ded6d5ee268c9 Mon Sep 17 00:00:00 2001 From: dyhkwong <50692134+dyhkwong@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:44:34 +0800 Subject: [PATCH 02/94] Fix websocket detour Signed-off-by: trimgop <20010323+trimgop@users.noreply.github.com> Co-authored-by: trimgop <20010323+trimgop@users.noreply.github.com> --- transport/v2raywebsocket/client.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/transport/v2raywebsocket/client.go b/transport/v2raywebsocket/client.go index 9c495076..748bae4c 100644 --- a/transport/v2raywebsocket/client.go +++ b/transport/v2raywebsocket/client.go @@ -91,10 +91,7 @@ func (c *Client) dialContext(ctx context.Context, requestURL *url.URL, headers h } else { deadlineConn = conn } - err = deadlineConn.SetDeadline(time.Now().Add(C.TCPTimeout)) - if err != nil { - return nil, E.Cause(err, "set read deadline") - } + deadlineConn.SetDeadline(time.Now().Add(C.TCPTimeout)) var protocols []string if protocolHeader := headers.Get("Sec-WebSocket-Protocol"); protocolHeader != "" { protocols = []string{protocolHeader} From f2bbf6b2aaafd10ab7a6ca52e790732a4bcf447f Mon Sep 17 00:00:00 2001 From: dyhkwong <50692134+dyhkwong@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:44:55 +0800 Subject: [PATCH 03/94] Fix sniffer errors override each others * Fix sniffer errors override each others * Do not return ErrNeedMoreData if header is not expected --- common/sniff/bittorrent.go | 11 ++++++++--- common/sniff/bittorrent_test.go | 21 +++++++++++++++++++++ common/sniff/dns.go | 22 ++++++++++++++++++---- common/sniff/dns_test.go | 30 ++++++++++++++++++++++++++++++ common/sniff/sniff.go | 2 +- common/sniff/ssh.go | 5 +++-- common/sniff/ssh_test.go | 21 +++++++++++++++++++++ 7 files changed, 102 insertions(+), 10 deletions(-) diff --git a/common/sniff/bittorrent.go b/common/sniff/bittorrent.go index 39c19598..e4d9f4b8 100644 --- a/common/sniff/bittorrent.go +++ b/common/sniff/bittorrent.go @@ -31,13 +31,18 @@ func BitTorrent(_ context.Context, metadata *adapter.InboundContext, reader io.R return os.ErrInvalid } + const header = "BitTorrent protocol" var protocol [19]byte - _, err = reader.Read(protocol[:]) + var n int + n, err = reader.Read(protocol[:]) + if string(protocol[:n]) != header[:n] { + return os.ErrInvalid + } if err != nil { return E.Cause1(ErrNeedMoreData, err) } - if string(protocol[:]) != "BitTorrent protocol" { - return os.ErrInvalid + if n < 19 { + return ErrNeedMoreData } metadata.Protocol = C.ProtocolBitTorrent diff --git a/common/sniff/bittorrent_test.go b/common/sniff/bittorrent_test.go index f4762e32..fcb5f6fa 100644 --- a/common/sniff/bittorrent_test.go +++ b/common/sniff/bittorrent_test.go @@ -32,6 +32,27 @@ func TestSniffBittorrent(t *testing.T) { } } +func TestSniffIncompleteBittorrent(t *testing.T) { + t.Parallel() + + pkt, err := hex.DecodeString("13426974546f7272656e74") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.BitTorrent(context.TODO(), &metadata, bytes.NewReader(pkt)) + require.ErrorIs(t, err, sniff.ErrNeedMoreData) +} + +func TestSniffNotBittorrent(t *testing.T) { + t.Parallel() + + pkt, err := hex.DecodeString("13426974546f7272656e75") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.BitTorrent(context.TODO(), &metadata, bytes.NewReader(pkt)) + require.NotEmpty(t, err) + require.NotErrorIs(t, err, sniff.ErrNeedMoreData) +} + func TestSniffUTP(t *testing.T) { t.Parallel() diff --git a/common/sniff/dns.go b/common/sniff/dns.go index 2e22f3d7..7125a08e 100644 --- a/common/sniff/dns.go +++ b/common/sniff/dns.go @@ -20,22 +20,36 @@ func StreamDomainNameQuery(readCtx context.Context, metadata *adapter.InboundCon if err != nil { return E.Cause1(ErrNeedMoreData, err) } - if length == 0 { + if length < 12 { return os.ErrInvalid } buffer := buf.NewSize(int(length)) defer buffer.Release() - _, err = buffer.ReadFullFrom(reader, buffer.FreeLen()) + var n int + n, err = buffer.ReadFullFrom(reader, buffer.FreeLen()) + packet := buffer.Bytes() + if n > 2 && packet[2]&0x80 != 0 { // QR + return os.ErrInvalid + } + if n > 5 && packet[4] == 0 && packet[5] == 0 { // QDCOUNT + return os.ErrInvalid + } + for i := 6; i < 10; i++ { + // ANCOUNT, NSCOUNT + if n > i && packet[i] != 0 { + return os.ErrInvalid + } + } if err != nil { return E.Cause1(ErrNeedMoreData, err) } - return DomainNameQuery(readCtx, metadata, buffer.Bytes()) + return DomainNameQuery(readCtx, metadata, packet) } func DomainNameQuery(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error { var msg mDNS.Msg err := msg.Unpack(packet) - if err != nil { + if err != nil || msg.Response || len(msg.Question) == 0 || len(msg.Answer) > 0 || len(msg.Ns) > 0 { return err } metadata.Protocol = C.ProtocolDNS diff --git a/common/sniff/dns_test.go b/common/sniff/dns_test.go index eaf4dd1a..d78b0bf5 100644 --- a/common/sniff/dns_test.go +++ b/common/sniff/dns_test.go @@ -1,6 +1,7 @@ package sniff_test import ( + "bytes" "context" "encoding/hex" "testing" @@ -21,3 +22,32 @@ func TestSniffDNS(t *testing.T) { require.NoError(t, err) require.Equal(t, C.ProtocolDNS, metadata.Protocol) } + +func TestSniffStreamDNS(t *testing.T) { + t.Parallel() + query, err := hex.DecodeString("001e740701000001000000000000012a06676f6f676c6503636f6d0000010001") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query)) + require.NoError(t, err) + require.Equal(t, C.ProtocolDNS, metadata.Protocol) +} + +func TestSniffIncompleteStreamDNS(t *testing.T) { + t.Parallel() + query, err := hex.DecodeString("001e740701000001000000000000") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query)) + require.ErrorIs(t, err, sniff.ErrNeedMoreData) +} + +func TestSniffNotStreamDNS(t *testing.T) { + t.Parallel() + query, err := hex.DecodeString("001e740701000000000000000000") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query)) + require.NotEmpty(t, err) + require.NotErrorIs(t, err, sniff.ErrNeedMoreData) +} diff --git a/common/sniff/sniff.go b/common/sniff/sniff.go index 59e81aaa..b3651e1f 100644 --- a/common/sniff/sniff.go +++ b/common/sniff/sniff.go @@ -68,7 +68,7 @@ func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net. } sniffError = E.Errors(sniffError, err) } - if !errors.Is(err, ErrNeedMoreData) { + if !errors.Is(sniffError, ErrNeedMoreData) { break } } diff --git a/common/sniff/ssh.go b/common/sniff/ssh.go index d373d292..dce5d54f 100644 --- a/common/sniff/ssh.go +++ b/common/sniff/ssh.go @@ -15,10 +15,11 @@ func SSH(_ context.Context, metadata *adapter.InboundContext, reader io.Reader) const sshPrefix = "SSH-2.0-" bReader := bufio.NewReader(reader) prefix, err := bReader.Peek(len(sshPrefix)) + if string(prefix[:]) != sshPrefix[:len(prefix)] { + return os.ErrInvalid + } if err != nil { return E.Cause1(ErrNeedMoreData, err) - } else if string(prefix) != sshPrefix { - return os.ErrInvalid } fistLine, _, err := bReader.ReadLine() if err != nil { diff --git a/common/sniff/ssh_test.go b/common/sniff/ssh_test.go index be530980..7cea5aab 100644 --- a/common/sniff/ssh_test.go +++ b/common/sniff/ssh_test.go @@ -24,3 +24,24 @@ func TestSniffSSH(t *testing.T) { require.Equal(t, C.ProtocolSSH, metadata.Protocol) require.Equal(t, "dropbear", metadata.Client) } + +func TestSniffIncompleteSSH(t *testing.T) { + t.Parallel() + + pkt, err := hex.DecodeString("5353482d322e30") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.SSH(context.TODO(), &metadata, bytes.NewReader(pkt)) + require.ErrorIs(t, err, sniff.ErrNeedMoreData) +} + +func TestSniffNotSSH(t *testing.T) { + t.Parallel() + + pkt, err := hex.DecodeString("5353482d322e31") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.SSH(context.TODO(), &metadata, bytes.NewReader(pkt)) + require.NotEmpty(t, err) + require.NotErrorIs(t, err, sniff.ErrNeedMoreData) +} From e6d19de58ab9cade6456c29bdede54b50ced9f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 22 Apr 2025 14:55:44 +0800 Subject: [PATCH 04/94] Fix overriding address --- route/route.go | 1 + 1 file changed, 1 insertion(+) diff --git a/route/route.go b/route/route.go index d6611b4f..f3e7a983 100644 --- a/route/route.go +++ b/route/route.go @@ -418,6 +418,7 @@ match: Port: metadata.Destination.Port, Fqdn: routeOptions.OverrideAddress.Fqdn, } + metadata.DestinationAddresses = nil } if routeOptions.OverridePort > 0 { metadata.Destination = M.Socksaddr{ From f600e02e471b5719f01d938edd73b08f78a4ae8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 22 Apr 2025 23:02:06 +0800 Subject: [PATCH 05/94] Fix DNS crash --- go.mod | 2 +- go.sum | 6 ++---- route/route_dns.go | 6 +++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 4cc25b6e..7713ea34 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/sagernet/quic-go v0.49.0-beta.1 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/sing v0.6.6 - github.com/sagernet/sing-dns v0.4.1 + github.com/sagernet/sing-dns v0.4.2 github.com/sagernet/sing-mux v0.3.1 github.com/sagernet/sing-quic v0.4.1 github.com/sagernet/sing-shadowsocks v0.2.7 diff --git a/go.sum b/go.sum index d639308f..1e1d615a 100644 --- a/go.sum +++ b/go.sum @@ -119,12 +119,10 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing v0.6.6-0.20250406121928-926a5a1e8bb7 h1:ZJauxLmH12Gzv3nucfjsSBQw9UA8t7Sxu8pYHBSP2TU= -github.com/sagernet/sing v0.6.6-0.20250406121928-926a5a1e8bb7/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.6.6 h1:3JkvJ0vqDj/jJcx0a+ve/6lMOrSzZm30I3wrIuZtmRE= github.com/sagernet/sing v0.6.6/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing-dns v0.4.1 h1:nozS7iqpxZ7aV73oHbkD/8haOvf3XXDCgT//8NdYirk= -github.com/sagernet/sing-dns v0.4.1/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8= +github.com/sagernet/sing-dns v0.4.2 h1:cWe2XPUBFLep2j9kJV4Epg3bctGhMvrrl/sWi9Wszfg= +github.com/sagernet/sing-dns v0.4.2/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8= github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI= github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78= github.com/sagernet/sing-quic v0.4.1 h1:pxlMa4efZu/M07RgGagNNDDyl6ZUwpmNUjRTpgHOWK4= diff --git a/route/route_dns.go b/route/route_dns.go index 3d7dc64f..62eff1c2 100644 --- a/route/route_dns.go +++ b/route/route_dns.go @@ -223,6 +223,9 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS err error ) printResult := func() { + if err == nil && len(responseAddrs) == 0 { + err = E.New("empty result") + } if err != nil { if errors.Is(err, dns.ErrResponseRejectedCached) { r.dnsLogger.DebugContext(ctx, "response rejected for ", domain, " (cached)") @@ -231,9 +234,6 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS } else { r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain)) } - } else if len(responseAddrs) == 0 { - r.dnsLogger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result") - err = dns.RCodeNameError } } responseAddrs, cached = r.dnsClient.LookupCache(ctx, domain, strategy) From 3a84acf122f9a985c0442b873e482b443651c22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 24 Apr 2025 21:46:23 +0800 Subject: [PATCH 06/94] Fix hysteria1 server panic --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7713ea34..97b5dfc2 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/sagernet/sing v0.6.6 github.com/sagernet/sing-dns v0.4.2 github.com/sagernet/sing-mux v0.3.1 - github.com/sagernet/sing-quic v0.4.1 + github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks2 v0.2.0 github.com/sagernet/sing-shadowtls v0.2.0 diff --git a/go.sum b/go.sum index 1e1d615a..a7fcb2be 100644 --- a/go.sum +++ b/go.sum @@ -125,8 +125,8 @@ github.com/sagernet/sing-dns v0.4.2 h1:cWe2XPUBFLep2j9kJV4Epg3bctGhMvrrl/sWi9Wsz github.com/sagernet/sing-dns v0.4.2/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8= github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI= github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78= -github.com/sagernet/sing-quic v0.4.1 h1:pxlMa4efZu/M07RgGagNNDDyl6ZUwpmNUjRTpgHOWK4= -github.com/sagernet/sing-quic v0.4.1/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY= +github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 h1:iwpCX6H3nZEOGUGwx0q5azcgYOA9f6v9YssihXoRKHk= +github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= From 56a7624618a2103e5a891f346b345aa55a61a377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 08:55:26 +0800 Subject: [PATCH 07/94] Fix vmess working with zero uuids --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 97b5dfc2..bdf364c9 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/sagernet/sing-shadowsocks2 v0.2.0 github.com/sagernet/sing-shadowtls v0.2.0 github.com/sagernet/sing-tun v0.6.4 - github.com/sagernet/sing-vmess v0.2.0 + github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/utls v1.6.7 github.com/sagernet/wireguard-go v0.0.1-beta.5 diff --git a/go.sum b/go.sum index a7fcb2be..00877c2d 100644 --- a/go.sum +++ b/go.sum @@ -135,8 +135,8 @@ github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDar github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo= github.com/sagernet/sing-tun v0.6.4 h1:3Iew6UtAf1+mucVeHKNhAEQI5xmq3CUCbGptUbjebts= github.com/sagernet/sing-tun v0.6.4/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= -github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI= -github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= +github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug= +github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= From 787b5f193192ff749c47db523bb6b351def80b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 09:02:20 +0800 Subject: [PATCH 08/94] Fix set wireguard reserved on Linux --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bdf364c9..95447301 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/utls v1.6.7 - github.com/sagernet/wireguard-go v0.0.1-beta.5 + github.com/sagernet/wireguard-go v0.0.1-beta.6 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.10.0 diff --git a/go.sum b/go.sum index 00877c2d..a84a94e7 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,8 @@ github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxe github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= -github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc= -github.com/sagernet/wireguard-go v0.0.1-beta.5/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo= +github.com/sagernet/wireguard-go v0.0.1-beta.6 h1:ID+kijtqBqZLRT+0oDsKidN5gGjS6MqSvwKsZKPdqzQ= +github.com/sagernet/wireguard-go v0.0.1-beta.6/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= From 03e8d029c26832970e95b86c1b6e035f740a931c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 09:07:41 +0800 Subject: [PATCH 09/94] release: Fix apt-get install --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a7c4b0d..025fe845 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -188,6 +188,7 @@ jobs: run: | set -xeuo pipefail sudo gem install fpm + sudo apt-get update sudo apt-get install -y debsigs cp .fpm_systemd .fpm fpm -t deb \ @@ -226,6 +227,7 @@ jobs: run: |- set -xeuo pipefail sudo gem install fpm + sudo apt-get update sudo apt-get install -y libarchive-tools cp .fpm_systemd .fpm fpm -t pacman \ From 7c7f5124058276107a881a9378cfca668fadc3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 09:47:45 +0800 Subject: [PATCH 10/94] option: Fix omitempty reject method --- option/rule_action.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/option/rule_action.go b/option/rule_action.go index b7003628..35f334d6 100644 --- a/option/rule_action.go +++ b/option/rule_action.go @@ -252,6 +252,14 @@ type _RejectActionOptions struct { type RejectActionOptions _RejectActionOptions +func (r RejectActionOptions) MarshalJSON() ([]byte, error) { + switch r.Method { + case C.RuleActionRejectMethodDefault: + r.Method = "" + } + return json.Marshal((_RejectActionOptions)(r)) +} + func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error { err := json.Unmarshal(bytes, (*_RejectActionOptions)(r)) if err != nil { From 300c961efadf75d56495538e044ff4974f4b3b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 09:51:54 +0800 Subject: [PATCH 11/94] option: Fix listable again and again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 95447301..2cb53398 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff github.com/sagernet/quic-go v0.49.0-beta.1 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.6.6 + github.com/sagernet/sing v0.6.7 github.com/sagernet/sing-dns v0.4.2 github.com/sagernet/sing-mux v0.3.1 github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 diff --git a/go.sum b/go.sum index a84a94e7..3bc20fa1 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing v0.6.6 h1:3JkvJ0vqDj/jJcx0a+ve/6lMOrSzZm30I3wrIuZtmRE= -github.com/sagernet/sing v0.6.6/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.6.7 h1:NIWBLZ9AUWDXAQBKGleKwsitbQrI9M0nqoheXhUKnrI= +github.com/sagernet/sing v0.6.7/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-dns v0.4.2 h1:cWe2XPUBFLep2j9kJV4Epg3bctGhMvrrl/sWi9Wszfg= github.com/sagernet/sing-dns v0.4.2/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8= github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI= From e5e81b4de12d030bd61e949eb566649cd752da67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 09:55:54 +0800 Subject: [PATCH 12/94] Fix wireguard listening --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2cb53398..13ac86fd 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/utls v1.6.7 - github.com/sagernet/wireguard-go v0.0.1-beta.6 + github.com/sagernet/wireguard-go v0.0.1-beta.7 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.10.0 diff --git a/go.sum b/go.sum index 3bc20fa1..755be1c0 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,8 @@ github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxe github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= -github.com/sagernet/wireguard-go v0.0.1-beta.6 h1:ID+kijtqBqZLRT+0oDsKidN5gGjS6MqSvwKsZKPdqzQ= -github.com/sagernet/wireguard-go v0.0.1-beta.6/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo= +github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI= +github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= From 134802d1ee080a01dad548ab374e0ccbad99c457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 11:35:06 +0800 Subject: [PATCH 13/94] Fix ssh outbound --- protocol/ssh/outbound.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/protocol/ssh/outbound.go b/protocol/ssh/outbound.go index eb9970b5..b329d4b3 100644 --- a/protocol/ssh/outbound.go +++ b/protocol/ssh/outbound.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter/outbound" @@ -191,9 +192,29 @@ func (s *Outbound) DialContext(ctx context.Context, network string, destination if err != nil { return nil, err } - return client.Dial(network, destination.String()) + conn, err := client.Dial(network, destination.String()) + if err != nil { + return nil, err + } + return &chanConnWrapper{Conn: conn}, nil } func (s *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } + +type chanConnWrapper struct { + net.Conn +} + +func (c *chanConnWrapper) SetDeadline(t time.Time) error { + return os.ErrInvalid +} + +func (c *chanConnWrapper) SetReadDeadline(t time.Time) error { + return os.ErrInvalid +} + +func (c *chanConnWrapper) SetWriteDeadline(t time.Time) error { + return os.ErrInvalid +} From d8b2d5142f922a9c7fab589b9b5083eb42116601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 11:49:25 +0800 Subject: [PATCH 14/94] Fix panic on some stupid input --- route/rule/rule_default.go | 5 ++++- route/rule/rule_dns.go | 5 ++++- route/rule/rule_headless.go | 5 ++++- route/rule/rule_item_domain.go | 15 +++++++++++++-- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/route/rule/rule_default.go b/route/rule/rule_default.go index 2794c287..56cf32dc 100644 --- a/route/rule/rule_default.go +++ b/route/rule/rule_default.go @@ -102,7 +102,10 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio rule.allItems = append(rule.allItems, item) } if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 { - item := NewDomainItem(options.Domain, options.DomainSuffix) + item, err := NewDomainItem(options.Domain, options.DomainSuffix) + if err != nil { + return nil, err + } rule.destinationAddressItems = append(rule.destinationAddressItems, item) rule.allItems = append(rule.allItems, item) } diff --git a/route/rule/rule_dns.go b/route/rule/rule_dns.go index fb8c6b78..d6eb7104 100644 --- a/route/rule/rule_dns.go +++ b/route/rule/rule_dns.go @@ -93,7 +93,10 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op rule.allItems = append(rule.allItems, item) } if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 { - item := NewDomainItem(options.Domain, options.DomainSuffix) + item, err := NewDomainItem(options.Domain, options.DomainSuffix) + if err != nil { + return nil, err + } rule.destinationAddressItems = append(rule.destinationAddressItems, item) rule.allItems = append(rule.allItems, item) } diff --git a/route/rule/rule_headless.go b/route/rule/rule_headless.go index 619856a5..ba17ca37 100644 --- a/route/rule/rule_headless.go +++ b/route/rule/rule_headless.go @@ -47,7 +47,10 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR rule.allItems = append(rule.allItems, item) } if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 { - item := NewDomainItem(options.Domain, options.DomainSuffix) + item, err := NewDomainItem(options.Domain, options.DomainSuffix) + if err != nil { + return nil, err + } rule.destinationAddressItems = append(rule.destinationAddressItems, item) rule.allItems = append(rule.allItems, item) } else if options.DomainMatcher != nil { diff --git a/route/rule/rule_item_domain.go b/route/rule/rule_item_domain.go index b7655a79..af790aa3 100644 --- a/route/rule/rule_item_domain.go +++ b/route/rule/rule_item_domain.go @@ -5,6 +5,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing/common/domain" + E "github.com/sagernet/sing/common/exceptions" ) var _ RuleItem = (*DomainItem)(nil) @@ -14,7 +15,17 @@ type DomainItem struct { description string } -func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem { +func NewDomainItem(domains []string, domainSuffixes []string) (*DomainItem, error) { + for _, domainItem := range domains { + if domainItem == "" { + return nil, E.New("domain: empty item is not allowed") + } + } + for _, domainSuffixItem := range domainSuffixes { + if domainSuffixItem == "" { + return nil, E.New("domain_suffix: empty item is not allowed") + } + } var description string if dLen := len(domains); dLen > 0 { if dLen == 1 { @@ -40,7 +51,7 @@ func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem { return &DomainItem{ domain.NewMatcher(domains, domainSuffixes, false), description, - } + }, nil } func NewRawDomainItem(matcher *domain.Matcher) *DomainItem { From 987899f94a7d32f144b23feb4d4060e404956bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 27 Apr 2025 21:29:23 +0800 Subject: [PATCH 15/94] Fix usages of wireguard listener --- common/dialer/default.go | 12 +++++++++++- transport/wireguard/endpoint.go | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/common/dialer/default.go b/common/dialer/default.go index 77536c43..bd8d9018 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -333,7 +333,17 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina } func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) { - return d.udpListener.ListenPacket(context.Background(), network, address) + udpListener := d.udpListener + udpListener.Control = control.Append(udpListener.Control, func(network, address string, conn syscall.RawConn) error { + for _, wgControlFn := range WgControlFns { + err := wgControlFn(network, address, conn) + if err != nil { + return err + } + } + return nil + }) + return udpListener.ListenPacket(context.Background(), network, address) } func trackConn(conn net.Conn, err error) (net.Conn, error) { diff --git a/transport/wireguard/endpoint.go b/transport/wireguard/endpoint.go index bddd2a12..3801640f 100644 --- a/transport/wireguard/endpoint.go +++ b/transport/wireguard/endpoint.go @@ -141,7 +141,7 @@ func (e *Endpoint) Start(resolve bool) error { return nil } var bind conn.Bind - wgListener, isWgListener := e.options.Dialer.(conn.Listener) + wgListener, isWgListener := common.Cast[conn.Listener](e.options.Dialer) if isWgListener { bind = conn.NewStdNetBind(wgListener) } else { From 348cc39975bc0d384c6b9d38c4ec0590d8ccf8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 27 Apr 2025 21:29:51 +0800 Subject: [PATCH 16/94] Bump version --- clients/android | 2 +- docs/changelog.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/clients/android b/clients/android index 55f31c29..6a15780c 160000 --- a/clients/android +++ b/clients/android @@ -1 +1 @@ -Subproject commit 55f31c29bb68895ce544e0dfbf852b4b3e32b530 +Subproject commit 6a15780ce1659a234816f7248cbc09e8ea54a1be diff --git a/docs/changelog.md b/docs/changelog.md index 864fdf7d..93964734 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,12 @@ icon: material/alert-decagram --- +### 1.11.9 + +* Fixes and improvements + +_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected)._ + ### 1.11.8 * Improve `auto_redirect` **1** From 5d2a70a92aab887e0ea365f8f33940fdf0b53594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 16 Mar 2025 14:50:44 +0800 Subject: [PATCH 17/94] refactor: DNS --- .goreleaser.fury.yaml | 4 +- adapter/dns.go | 73 +++ adapter/experimental.go | 3 +- adapter/fakeip.go | 3 +- adapter/inbound.go | 2 - adapter/outbound/manager.go | 17 +- adapter/router.go | 17 - adapter/rule.go | 1 - box.go | 107 +++- cmd/sing-box/cmd.go | 2 +- common/dialer/dialer.go | 9 +- common/dialer/resolve.go | 60 +- common/dialer/router.go | 15 +- common/tls/ech_client.go | 4 +- common/tls/std_client.go | 5 +- constant/dns.go | 29 + dns/client.go | 563 ++++++++++++++++++ dns/client_log.go | 69 +++ dns/client_truncate.go | 31 + dns/extension_edns0_subnet.go | 56 ++ dns/rcode.go | 33 + dns/router.go | 437 ++++++++++++++ .../server.go => dns/transport/dhcp/dhcp.go | 135 ++--- dns/transport/fakeip/fakeip.go | 67 +++ {transport => dns/transport}/fakeip/memory.go | 0 {transport => dns/transport}/fakeip/store.go | 0 dns/transport/hosts/hosts.go | 63 ++ dns/transport/hosts/hosts_file.go | 102 ++++ dns/transport/hosts/hosts_test.go | 16 + dns/transport/hosts/hosts_unix.go | 5 + dns/transport/hosts/hosts_windows.go | 17 + dns/transport/hosts/testdata/hosts | 2 + dns/transport/https.go | 204 +++++++ dns/transport/local/local.go | 197 ++++++ dns/transport/local/resolv.go | 146 +++++ dns/transport/local/resolv_unix.go | 175 ++++++ dns/transport/local/resolv_windows.go | 100 ++++ dns/transport/predefined.go | 83 +++ dns/transport/quic/http3.go | 167 ++++++ dns/transport/quic/quic.go | 174 ++++++ dns/transport/tcp.go | 99 +++ dns/transport/tls.go | 115 ++++ dns/transport/udp.go | 223 +++++++ dns/transport_adapter.go | 70 +++ dns/transport_dialer.go | 93 +++ dns/transport_manager.go | 288 +++++++++ dns/transport_registry.go | 72 +++ experimental/clashapi/dns.go | 6 +- experimental/clashapi/server.go | 16 +- experimental/deprecated/constants.go | 15 + experimental/libbox/config.go | 19 +- experimental/libbox/dns.go | 161 ++--- experimental/libbox/platform.go | 1 + experimental/libbox/service.go | 6 +- go.mod | 1 - go.sum | 2 - include/dhcp.go | 9 +- include/dhcp_stub.go | 12 +- include/quic.go | 8 +- include/quic_stub.go | 14 +- include/registry.go | 27 +- option/dns.go | 315 +++++++++- option/dns_record.go | 161 +++++ option/rule_action.go | 5 +- option/rule_dns.go | 1 + option/types.go | 37 +- protocol/direct/outbound.go | 29 +- protocol/dns/handle.go | 14 +- protocol/dns/outbound.go | 5 +- protocol/socks/outbound.go | 17 +- protocol/wireguard/endpoint.go | 12 +- protocol/wireguard/outbound.go | 14 +- release/config/config.json | 9 +- route/dns.go | 27 +- route/geo_resources.go | 246 -------- route/route.go | 46 +- route/route_dns.go | 348 ----------- route/router.go | 414 ++----------- route/rule/rule_abstract.go | 25 - route/rule/rule_action.go | 14 +- route/rule/rule_default.go | 12 +- route/rule/rule_dns.go | 17 +- route/rule/rule_item_geoip.go | 98 --- route/rule/rule_item_geosite.go | 61 -- route/rule/rule_item_ip_accept_any.go | 21 + route/rule_conds.go | 21 - test/domain_inbound_test.go | 3 +- transport/fakeip/server.go | 95 --- transport/v2rayhttp/server.go | 2 +- 89 files changed, 4786 insertions(+), 1733 deletions(-) create mode 100644 adapter/dns.go create mode 100644 dns/client.go create mode 100644 dns/client_log.go create mode 100644 dns/client_truncate.go create mode 100644 dns/extension_edns0_subnet.go create mode 100644 dns/rcode.go create mode 100644 dns/router.go rename transport/dhcp/server.go => dns/transport/dhcp/dhcp.go (66%) create mode 100644 dns/transport/fakeip/fakeip.go rename {transport => dns/transport}/fakeip/memory.go (100%) rename {transport => dns/transport}/fakeip/store.go (100%) create mode 100644 dns/transport/hosts/hosts.go create mode 100644 dns/transport/hosts/hosts_file.go create mode 100644 dns/transport/hosts/hosts_test.go create mode 100644 dns/transport/hosts/hosts_unix.go create mode 100644 dns/transport/hosts/hosts_windows.go create mode 100644 dns/transport/hosts/testdata/hosts create mode 100644 dns/transport/https.go create mode 100644 dns/transport/local/local.go create mode 100644 dns/transport/local/resolv.go create mode 100644 dns/transport/local/resolv_unix.go create mode 100644 dns/transport/local/resolv_windows.go create mode 100644 dns/transport/predefined.go create mode 100644 dns/transport/quic/http3.go create mode 100644 dns/transport/quic/quic.go create mode 100644 dns/transport/tcp.go create mode 100644 dns/transport/tls.go create mode 100644 dns/transport/udp.go create mode 100644 dns/transport_adapter.go create mode 100644 dns/transport_dialer.go create mode 100644 dns/transport_manager.go create mode 100644 dns/transport_registry.go create mode 100644 option/dns_record.go delete mode 100644 route/geo_resources.go delete mode 100644 route/route_dns.go delete mode 100644 route/rule/rule_item_geoip.go delete mode 100644 route/rule/rule_item_geosite.go create mode 100644 route/rule/rule_item_ip_accept_any.go delete mode 100644 transport/fakeip/server.go diff --git a/.goreleaser.fury.yaml b/.goreleaser.fury.yaml index 3212027a..4761a76a 100644 --- a/.goreleaser.fury.yaml +++ b/.goreleaser.fury.yaml @@ -6,7 +6,9 @@ builds: - -v - -trimpath ldflags: - - -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid= + - -X github.com/sagernet/sing-box/constant.Version={{ .Version }} + - -s + - -buildid= tags: - with_gvisor - with_quic diff --git a/adapter/dns.go b/adapter/dns.go new file mode 100644 index 00000000..e0f381b8 --- /dev/null +++ b/adapter/dns.go @@ -0,0 +1,73 @@ +package adapter + +import ( + "context" + "net/netip" + + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common/logger" + + "github.com/miekg/dns" +) + +type DNSRouter interface { + Lifecycle + Exchange(ctx context.Context, message *dns.Msg, options DNSQueryOptions) (*dns.Msg, error) + Lookup(ctx context.Context, domain string, options DNSQueryOptions) ([]netip.Addr, error) + ClearCache() + LookupReverseMapping(ip netip.Addr) (string, bool) + ResetNetwork() +} + +type DNSClient interface { + Start() + Exchange(ctx context.Context, transport DNSTransport, message *dns.Msg, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) + Lookup(ctx context.Context, transport DNSTransport, domain string, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error) + LookupCache(domain string, strategy C.DomainStrategy) ([]netip.Addr, bool) + ExchangeCache(ctx context.Context, message *dns.Msg) (*dns.Msg, bool) + ClearCache() +} + +type DNSQueryOptions struct { + Transport DNSTransport + Strategy C.DomainStrategy + DisableCache bool + RewriteTTL *uint32 + ClientSubnet netip.Prefix +} + +type RDRCStore interface { + LoadRDRC(transportName string, qName string, qType uint16) (rejected bool) + SaveRDRC(transportName string, qName string, qType uint16) error + SaveRDRCAsync(transportName string, qName string, qType uint16, logger logger.Logger) +} + +type DNSTransport interface { + Type() string + Tag() string + Dependencies() []string + Reset() + Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) +} + +type LegacyDNSTransport interface { + LegacyStrategy() C.DomainStrategy + LegacyClientSubnet() netip.Prefix +} + +type DNSTransportRegistry interface { + option.DNSTransportOptionsRegistry + CreateDNSTransport(ctx context.Context, logger log.ContextLogger, tag string, transportType string, options any) (DNSTransport, error) +} + +type DNSTransportManager interface { + Lifecycle + Transports() []DNSTransport + Transport(tag string) (DNSTransport, bool) + Default() DNSTransport + FakeIP() FakeIPTransport + Remove(tag string) error + Create(ctx context.Context, logger log.ContextLogger, tag string, outboundType string, options any) error +} diff --git a/adapter/experimental.go b/adapter/experimental.go index 648eb418..99d7c9a5 100644 --- a/adapter/experimental.go +++ b/adapter/experimental.go @@ -7,7 +7,6 @@ import ( "time" "github.com/sagernet/sing-box/common/urltest" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common/varbin" ) @@ -31,7 +30,7 @@ type CacheFile interface { FakeIPStorage StoreRDRC() bool - dns.RDRCStore + RDRCStore LoadMode() string StoreMode(mode string) error diff --git a/adapter/fakeip.go b/adapter/fakeip.go index 51247c32..97d1c3c0 100644 --- a/adapter/fakeip.go +++ b/adapter/fakeip.go @@ -3,7 +3,6 @@ package adapter import ( "net/netip" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common/logger" ) @@ -27,6 +26,6 @@ type FakeIPStorage interface { } type FakeIPTransport interface { - dns.Transport + DNSTransport Store() FakeIPStore } diff --git a/adapter/inbound.go b/adapter/inbound.go index 173dd0ee..11085099 100644 --- a/adapter/inbound.go +++ b/adapter/inbound.go @@ -78,8 +78,6 @@ type InboundContext struct { FallbackNetworkType []C.InterfaceType FallbackDelay time.Duration - DNSServer string - DestinationAddresses []netip.Addr SourceGeoIPCode string GeoIPCode string diff --git a/adapter/outbound/manager.go b/adapter/outbound/manager.go index c3941d02..977fe4ca 100644 --- a/adapter/outbound/manager.go +++ b/adapter/outbound/manager.go @@ -23,7 +23,7 @@ type Manager struct { registry adapter.OutboundRegistry endpoint adapter.EndpointManager defaultTag string - access sync.Mutex + access sync.RWMutex started bool stage adapter.StartStage outbounds []adapter.Outbound @@ -169,15 +169,15 @@ func (m *Manager) Close() error { } func (m *Manager) Outbounds() []adapter.Outbound { - m.access.Lock() - defer m.access.Unlock() + m.access.RLock() + defer m.access.RUnlock() return m.outbounds } func (m *Manager) Outbound(tag string) (adapter.Outbound, bool) { - m.access.Lock() + m.access.RLock() outbound, found := m.outboundByTag[tag] - m.access.Unlock() + m.access.RUnlock() if found { return outbound, true } @@ -185,8 +185,8 @@ func (m *Manager) Outbound(tag string) (adapter.Outbound, bool) { } func (m *Manager) Default() adapter.Outbound { - m.access.Lock() - defer m.access.Unlock() + m.access.RLock() + defer m.access.RUnlock() if m.defaultOutbound != nil { return m.defaultOutbound } else { @@ -196,9 +196,9 @@ func (m *Manager) Default() adapter.Outbound { func (m *Manager) Remove(tag string) error { m.access.Lock() + defer m.access.Unlock() outbound, found := m.outboundByTag[tag] if !found { - m.access.Unlock() return os.ErrInvalid } delete(m.outboundByTag, tag) @@ -232,7 +232,6 @@ func (m *Manager) Remove(tag string) error { }) } } - m.access.Unlock() if started { return common.Close(outbound) } diff --git a/adapter/router.go b/adapter/router.go index 687943cb..45e4f723 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -4,42 +4,25 @@ import ( "context" "net" "net/http" - "net/netip" "sync" - "github.com/sagernet/sing-box/common/geoip" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-dns" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/x/list" - mdns "github.com/miekg/dns" "go4.org/netipx" ) type Router interface { Lifecycle - - FakeIPStore() FakeIPStore - ConnectionRouter PreMatch(metadata InboundContext) error ConnectionRouterEx - - GeoIPReader() *geoip.Reader - LoadGeosite(code string) (Rule, error) RuleSet(tag string) (RuleSet, bool) NeedWIFIState() bool - - Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error) - Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) - LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) - ClearDNSCache() Rules() []Rule - AppendTracker(tracker ConnectionTracker) - ResetNetwork() } diff --git a/adapter/rule.go b/adapter/rule.go index f3737a25..2512a77b 100644 --- a/adapter/rule.go +++ b/adapter/rule.go @@ -13,7 +13,6 @@ type Rule interface { HeadlessRule Service Type() string - UpdateGeosite() error Action() RuleAction } diff --git a/box.go b/box.go index b9f04c87..e810b771 100644 --- a/box.go +++ b/box.go @@ -16,6 +16,8 @@ import ( "github.com/sagernet/sing-box/common/taskmonitor" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport/local" "github.com/sagernet/sing-box/experimental" "github.com/sagernet/sing-box/experimental/cachefile" "github.com/sagernet/sing-box/experimental/libbox/platform" @@ -34,17 +36,19 @@ import ( var _ adapter.Service = (*Box)(nil) type Box struct { - createdAt time.Time - logFactory log.Factory - logger log.ContextLogger - network *route.NetworkManager - endpoint *endpoint.Manager - inbound *inbound.Manager - outbound *outbound.Manager - connection *route.ConnectionManager - router *route.Router - services []adapter.LifecycleService - done chan struct{} + createdAt time.Time + logFactory log.Factory + logger log.ContextLogger + network *route.NetworkManager + endpoint *endpoint.Manager + inbound *inbound.Manager + outbound *outbound.Manager + dnsTransport *dns.TransportManager + dnsRouter *dns.Router + connection *route.ConnectionManager + router *route.Router + services []adapter.LifecycleService + done chan struct{} } type Options struct { @@ -58,6 +62,7 @@ func Context( inboundRegistry adapter.InboundRegistry, outboundRegistry adapter.OutboundRegistry, endpointRegistry adapter.EndpointRegistry, + dnsTransportRegistry adapter.DNSTransportRegistry, ) context.Context { if service.FromContext[option.InboundOptionsRegistry](ctx) == nil || service.FromContext[adapter.InboundRegistry](ctx) == nil { @@ -74,6 +79,10 @@ func Context( ctx = service.ContextWith[option.EndpointOptionsRegistry](ctx, endpointRegistry) ctx = service.ContextWith[adapter.EndpointRegistry](ctx, endpointRegistry) } + if service.FromContext[adapter.DNSTransportRegistry](ctx) == nil { + ctx = service.ContextWith[option.DNSTransportOptionsRegistry](ctx, dnsTransportRegistry) + ctx = service.ContextWith[adapter.DNSTransportRegistry](ctx, dnsTransportRegistry) + } return ctx } @@ -88,6 +97,7 @@ func New(options Options) (*Box, error) { endpointRegistry := service.FromContext[adapter.EndpointRegistry](ctx) inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx) outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx) + dnsTransportRegistry := service.FromContext[adapter.DNSTransportRegistry](ctx) if endpointRegistry == nil { return nil, E.New("missing endpoint registry in context") @@ -132,13 +142,17 @@ func New(options Options) (*Box, error) { } routeOptions := common.PtrValueOrDefault(options.Route) + dnsOptions := common.PtrValueOrDefault(options.DNS) 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) + dnsTransportManager := dns.NewTransportManager(logFactory.NewLogger("dns/transport"), dnsTransportRegistry, outboundManager, dnsOptions.Final) service.MustRegister[adapter.EndpointManager](ctx, endpointManager) service.MustRegister[adapter.InboundManager](ctx, inboundManager) service.MustRegister[adapter.OutboundManager](ctx, outboundManager) - + service.MustRegister[adapter.DNSTransportManager](ctx, dnsTransportManager) + dnsRouter := dns.NewRouter(ctx, logFactory, dnsOptions) + service.MustRegister[adapter.DNSRouter](ctx, dnsRouter) networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions) if err != nil { return nil, E.Cause(err, "initialize network manager") @@ -146,18 +160,40 @@ func New(options Options) (*Box, error) { service.MustRegister[adapter.NetworkManager](ctx, networkManager) connectionManager := route.NewConnectionManager(logFactory.NewLogger("connection")) service.MustRegister[adapter.ConnectionManager](ctx, connectionManager) - router, err := route.NewRouter(ctx, logFactory, routeOptions, common.PtrValueOrDefault(options.DNS)) + router := route.NewRouter(ctx, logFactory, routeOptions, dnsOptions) + service.MustRegister[adapter.Router](ctx, router) + err = router.Initialize(routeOptions.Rules, routeOptions.RuleSet) if err != nil { return nil, E.Cause(err, "initialize router") } - ntpOptions := common.PtrValueOrDefault(options.NTP) var timeService *tls.TimeServiceWrapper if ntpOptions.Enabled { timeService = new(tls.TimeServiceWrapper) service.MustRegister[ntp.TimeService](ctx, timeService) } - + for i, transportOptions := range dnsOptions.Servers { + var tag string + if transportOptions.Tag != "" { + tag = transportOptions.Tag + } else { + tag = F.ToString(i) + } + err = dnsTransportManager.Create( + ctx, + logFactory.NewLogger(F.ToString("dns/", transportOptions.Type, "[", tag, "]")), + tag, + transportOptions.Type, + transportOptions.Options, + ) + if err != nil { + return nil, E.Cause(err, "initialize inbound[", i, "]") + } + } + err = dnsRouter.Initialize(dnsOptions.Rules) + if err != nil { + return nil, E.Cause(err, "initialize dns router") + } for i, endpointOptions := range options.Endpoints { var tag string if endpointOptions.Tag != "" { @@ -238,6 +274,13 @@ func New(options Options) (*Box, error) { option.DirectOutboundOptions{}, ), )) + dnsTransportManager.Initialize(common.Must1( + local.NewTransport( + ctx, + logFactory.NewLogger("dns/local"), + "local", + option.LocalDNSServerOptions{}, + ))) if platformInterface != nil { err = platformInterface.Initialize(networkManager) if err != nil { @@ -289,17 +332,19 @@ func New(options Options) (*Box, error) { services = append(services, adapter.NewLifecycleService(ntpService, "ntp service")) } return &Box{ - network: networkManager, - endpoint: endpointManager, - inbound: inboundManager, - outbound: outboundManager, - connection: connectionManager, - router: router, - createdAt: createdAt, - logFactory: logFactory, - logger: logFactory.Logger(), - services: services, - done: make(chan struct{}), + network: networkManager, + endpoint: endpointManager, + inbound: inboundManager, + outbound: outboundManager, + dnsTransport: dnsTransportManager, + dnsRouter: dnsRouter, + connection: connectionManager, + router: router, + createdAt: createdAt, + logFactory: logFactory, + logger: logFactory.Logger(), + services: services, + done: make(chan struct{}), }, nil } @@ -353,11 +398,11 @@ func (s *Box) preStart() error { if err != nil { return err } - err = adapter.Start(adapter.StartStateInitialize, s.network, s.connection, s.router, s.outbound, s.inbound, s.endpoint) + err = adapter.Start(adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint) if err != nil { return err } - err = adapter.Start(adapter.StartStateStart, s.outbound, s.network, s.connection, s.router) + err = adapter.Start(adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router) if err != nil { return err } @@ -381,7 +426,7 @@ func (s *Box) start() error { if err != nil { return err } - err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.connection, s.router, s.inbound, s.endpoint) + err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint) if err != nil { return err } @@ -389,7 +434,7 @@ func (s *Box) start() error { if err != nil { return err } - err = adapter.Start(adapter.StartStateStarted, s.network, s.connection, s.router, s.outbound, s.inbound, s.endpoint) + err = adapter.Start(adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint) if err != nil { return err } @@ -408,7 +453,7 @@ func (s *Box) Close() error { close(s.done) } err := common.Close( - s.inbound, s.outbound, s.endpoint, s.router, s.connection, s.network, + s.inbound, s.outbound, s.endpoint, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network, ) for _, lifecycleService := range s.services { err = E.Append(err, lifecycleService.Close(), func(err error) error { diff --git a/cmd/sing-box/cmd.go b/cmd/sing-box/cmd.go index d55235b8..55fe1179 100644 --- a/cmd/sing-box/cmd.go +++ b/cmd/sing-box/cmd.go @@ -69,5 +69,5 @@ func preRun(cmd *cobra.Command, args []string) { configPaths = append(configPaths, "config.json") } globalCtx = service.ContextWith(globalCtx, deprecated.NewStderrManager(log.StdLogger())) - globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry()) + globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), include.DNSTransportRegistry()) } diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index 89d1eeab..f63e3864 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -9,7 +9,6 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-dns" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -37,13 +36,13 @@ func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) { dialer = NewDetour(outboundManager, options.Detour) } if options.Detour == "" { - router := service.FromContext[adapter.Router](ctx) + router := service.FromContext[adapter.DNSRouter](ctx) if router != nil { dialer = NewResolveDialer( router, dialer, options.Detour == "" && !options.TCPFastOpen, - dns.DomainStrategy(options.DomainStrategy), + C.DomainStrategy(options.DomainStrategy), time.Duration(options.FallbackDelay)) } } @@ -62,10 +61,10 @@ func NewDirect(ctx context.Context, options option.DialerOptions) (ParallelInter return nil, err } return NewResolveParallelInterfaceDialer( - service.FromContext[adapter.Router](ctx), + service.FromContext[adapter.DNSRouter](ctx), dialer, true, - dns.DomainStrategy(options.DomainStrategy), + C.DomainStrategy(options.DomainStrategy), time.Duration(options.FallbackDelay), ), nil } diff --git a/common/dialer/resolve.go b/common/dialer/resolve.go index ede1afd6..3d667a6c 100644 --- a/common/dialer/resolve.go +++ b/common/dialer/resolve.go @@ -3,13 +3,11 @@ package dialer import ( "context" "net" - "net/netip" "time" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common/bufio" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -23,12 +21,12 @@ var ( type resolveDialer struct { dialer N.Dialer parallel bool - router adapter.Router - strategy dns.DomainStrategy + router adapter.DNSRouter + strategy C.DomainStrategy fallbackDelay time.Duration } -func NewResolveDialer(router adapter.Router, dialer N.Dialer, parallel bool, strategy dns.DomainStrategy, fallbackDelay time.Duration) N.Dialer { +func NewResolveDialer(router adapter.DNSRouter, dialer N.Dialer, parallel bool, strategy C.DomainStrategy, fallbackDelay time.Duration) N.Dialer { return &resolveDialer{ dialer, parallel, @@ -43,7 +41,7 @@ type resolveParallelNetworkDialer struct { dialer ParallelInterfaceDialer } -func NewResolveParallelInterfaceDialer(router adapter.Router, dialer ParallelInterfaceDialer, parallel bool, strategy dns.DomainStrategy, fallbackDelay time.Duration) ParallelInterfaceDialer { +func NewResolveParallelInterfaceDialer(router adapter.DNSRouter, dialer ParallelInterfaceDialer, parallel bool, strategy C.DomainStrategy, fallbackDelay time.Duration) ParallelInterfaceDialer { return &resolveParallelNetworkDialer{ resolveDialer{ dialer, @@ -60,22 +58,13 @@ func (d *resolveDialer) DialContext(ctx context.Context, network string, destina if !destination.IsFqdn() { return d.dialer.DialContext(ctx, network, destination) } - ctx, metadata := adapter.ExtendContext(ctx) ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - metadata.Destination = destination - metadata.Domain = "" - var addresses []netip.Addr - var err error - if d.strategy == dns.DomainStrategyAsIS { - addresses, err = d.router.LookupDefault(ctx, destination.Fqdn) - } else { - addresses, err = d.router.Lookup(ctx, destination.Fqdn, d.strategy) - } + addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) if err != nil { return nil, err } if d.parallel { - return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == dns.DomainStrategyPreferIPv6, d.fallbackDelay) + return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, d.fallbackDelay) } else { return N.DialSerial(ctx, d.dialer, network, destination, addresses) } @@ -85,17 +74,8 @@ func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd if !destination.IsFqdn() { return d.dialer.ListenPacket(ctx, destination) } - ctx, metadata := adapter.ExtendContext(ctx) ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - metadata.Destination = destination - metadata.Domain = "" - var addresses []netip.Addr - var err error - if d.strategy == dns.DomainStrategyAsIS { - addresses, err = d.router.LookupDefault(ctx, destination.Fqdn) - } else { - addresses, err = d.router.Lookup(ctx, destination.Fqdn, d.strategy) - } + addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) if err != nil { return nil, err } @@ -110,17 +90,10 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context if !destination.IsFqdn() { return d.dialer.DialContext(ctx, network, destination) } - ctx, metadata := adapter.ExtendContext(ctx) ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - metadata.Destination = destination - metadata.Domain = "" - var addresses []netip.Addr - var err error - if d.strategy == dns.DomainStrategyAsIS { - addresses, err = d.router.LookupDefault(ctx, destination.Fqdn) - } else { - addresses, err = d.router.Lookup(ctx, destination.Fqdn, d.strategy) - } + addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{ + Strategy: d.strategy, + }) if err != nil { return nil, err } @@ -128,7 +101,7 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context fallbackDelay = d.fallbackDelay } if d.parallel { - return DialParallelNetwork(ctx, d.dialer, network, destination, addresses, d.strategy == dns.DomainStrategyPreferIPv6, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) + return DialParallelNetwork(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) } else { return DialSerialNetwork(ctx, d.dialer, network, destination, addresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) } @@ -138,17 +111,8 @@ func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.C if !destination.IsFqdn() { return d.dialer.ListenPacket(ctx, destination) } - ctx, metadata := adapter.ExtendContext(ctx) ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - metadata.Destination = destination - metadata.Domain = "" - var addresses []netip.Addr - var err error - if d.strategy == dns.DomainStrategyAsIS { - addresses, err = d.router.LookupDefault(ctx, destination.Fqdn) - } else { - addresses, err = d.router.Lookup(ctx, destination.Fqdn, d.strategy) - } + addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) if err != nil { return nil, err } diff --git a/common/dialer/router.go b/common/dialer/router.go index 3edce65b..801a36b1 100644 --- a/common/dialer/router.go +++ b/common/dialer/router.go @@ -7,24 +7,27 @@ import ( "github.com/sagernet/sing-box/adapter" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" ) type DefaultOutboundDialer struct { - outboundManager adapter.OutboundManager + outbound adapter.OutboundManager } -func NewDefaultOutbound(outboundManager adapter.OutboundManager) N.Dialer { - return &DefaultOutboundDialer{outboundManager: outboundManager} +func NewDefaultOutbound(ctx context.Context) N.Dialer { + return &DefaultOutboundDialer{ + outbound: service.FromContext[adapter.OutboundManager](ctx), + } } func (d *DefaultOutboundDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - return d.outboundManager.Default().DialContext(ctx, network, destination) + return d.outbound.Default().DialContext(ctx, network, destination) } func (d *DefaultOutboundDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return d.outboundManager.Default().ListenPacket(ctx, destination) + return d.outbound.Default().ListenPacket(ctx, destination) } func (d *DefaultOutboundDialer) Upstream() any { - return d.outboundManager.Default() + return d.outbound.Default() } diff --git a/common/tls/ech_client.go b/common/tls/ech_client.go index 0ae3997a..fff1873d 100644 --- a/common/tls/ech_client.go +++ b/common/tls/ech_client.go @@ -15,8 +15,8 @@ import ( cftls "github.com/sagernet/cloudflare-tls" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-dns" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/service" @@ -215,7 +215,7 @@ func fetchECHClientConfig(ctx context.Context) func(_ context.Context, serverNam }, }, } - response, err := service.FromContext[adapter.Router](ctx).Exchange(ctx, message) + response, err := service.FromContext[adapter.DNSRouter](ctx).Exchange(ctx, message, adapter.DNSQueryOptions{}) if err != nil { return nil, err } diff --git a/common/tls/std_client.go b/common/tls/std_client.go index 90f51821..7cd130a6 100644 --- a/common/tls/std_client.go +++ b/common/tls/std_client.go @@ -5,7 +5,6 @@ import ( "crypto/tls" "crypto/x509" "net" - "net/netip" "os" "strings" @@ -51,9 +50,7 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb if options.ServerName != "" { serverName = options.ServerName } else if serverAddress != "" { - if _, err := netip.ParseAddr(serverName); err != nil { - serverName = serverAddress - } + serverName = serverAddress } if serverName == "" && !options.Insecure { return nil, E.New("missing server_name or insecure=true") diff --git a/constant/dns.go b/constant/dns.go index 3907b8c1..99461a27 100644 --- a/constant/dns.go +++ b/constant/dns.go @@ -1,5 +1,34 @@ package constant +const ( + DefaultDNSTTL = 600 +) + +type DomainStrategy = uint8 + +const ( + DomainStrategyAsIS DomainStrategy = iota + DomainStrategyPreferIPv4 + DomainStrategyPreferIPv6 + DomainStrategyIPv4Only + DomainStrategyIPv6Only +) + +const ( + DNSTypeLegacy = "legacy" + DNSTypeUDP = "udp" + DNSTypeTCP = "tcp" + DNSTypeTLS = "tls" + DNSTypeHTTPS = "https" + DNSTypeQUIC = "quic" + DNSTypeHTTP3 = "h3" + DNSTypeHosts = "hosts" + DNSTypeLocal = "local" + DNSTypePreDefined = "predefined" + DNSTypeFakeIP = "fakeip" + DNSTypeDHCP = "dhcp" +) + const ( DNSProviderAliDNS = "alidns" DNSProviderCloudflare = "cloudflare" diff --git a/dns/client.go b/dns/client.go new file mode 100644 index 00000000..79b6fce5 --- /dev/null +++ b/dns/client.go @@ -0,0 +1,563 @@ +package dns + +import ( + "context" + "net" + "net/netip" + "strings" + "time" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/common/task" + "github.com/sagernet/sing/contrab/freelru" + "github.com/sagernet/sing/contrab/maphash" + + "github.com/miekg/dns" +) + +var ( + ErrNoRawSupport = E.New("no raw query support by current transport") + ErrNotCached = E.New("not cached") + ErrResponseRejected = E.New("response rejected") + ErrResponseRejectedCached = E.Extend(ErrResponseRejected, "cached") +) + +var _ adapter.DNSClient = (*Client)(nil) + +type Client struct { + timeout time.Duration + disableCache bool + disableExpire bool + independentCache bool + rdrc adapter.RDRCStore + initRDRCFunc func() adapter.RDRCStore + logger logger.ContextLogger + cache freelru.Cache[dns.Question, *dns.Msg] + transportCache freelru.Cache[transportCacheKey, *dns.Msg] +} + +type ClientOptions struct { + Timeout time.Duration + DisableCache bool + DisableExpire bool + IndependentCache bool + CacheCapacity uint32 + RDRC func() adapter.RDRCStore + Logger logger.ContextLogger +} + +func NewClient(options ClientOptions) *Client { + client := &Client{ + timeout: options.Timeout, + disableCache: options.DisableCache, + disableExpire: options.DisableExpire, + independentCache: options.IndependentCache, + initRDRCFunc: options.RDRC, + logger: options.Logger, + } + if client.timeout == 0 { + client.timeout = C.DNSTimeout + } + cacheCapacity := options.CacheCapacity + if cacheCapacity < 1024 { + cacheCapacity = 1024 + } + if !client.disableCache { + if !client.independentCache { + client.cache = common.Must1(freelru.NewSharded[dns.Question, *dns.Msg](cacheCapacity, maphash.NewHasher[dns.Question]().Hash32)) + } else { + client.transportCache = common.Must1(freelru.NewSharded[transportCacheKey, *dns.Msg](cacheCapacity, maphash.NewHasher[transportCacheKey]().Hash32)) + } + } + return client +} + +type transportCacheKey struct { + dns.Question + transportTag string +} + +func (c *Client) Start() { + if c.initRDRCFunc != nil { + c.rdrc = c.initRDRCFunc() + } +} + +func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, message *dns.Msg, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) { + if len(message.Question) == 0 { + if c.logger != nil { + c.logger.WarnContext(ctx, "bad question size: ", len(message.Question)) + } + responseMessage := dns.Msg{ + MsgHdr: dns.MsgHdr{ + Id: message.Id, + Response: true, + Rcode: dns.RcodeFormatError, + }, + Question: message.Question, + } + return &responseMessage, nil + } + question := message.Question[0] + if options.ClientSubnet.IsValid() { + message = SetClientSubnet(message, options.ClientSubnet, true) + } + isSimpleRequest := len(message.Question) == 1 && + len(message.Ns) == 0 && + len(message.Extra) == 0 && + !options.ClientSubnet.IsValid() + disableCache := !isSimpleRequest || c.disableCache || options.DisableCache + if !disableCache { + response, ttl := c.loadResponse(question, transport) + if response != nil { + logCachedResponse(c.logger, ctx, response, ttl) + response.Id = message.Id + return response, nil + } + } + if question.Qtype == dns.TypeA && options.Strategy == C.DomainStrategyIPv6Only || question.Qtype == dns.TypeAAAA && options.Strategy == C.DomainStrategyIPv4Only { + responseMessage := dns.Msg{ + MsgHdr: dns.MsgHdr{ + Id: message.Id, + Response: true, + Rcode: dns.RcodeSuccess, + }, + Question: []dns.Question{question}, + } + if c.logger != nil { + c.logger.DebugContext(ctx, "strategy rejected") + } + return &responseMessage, nil + } + messageId := message.Id + contextTransport, clientSubnetLoaded := transportTagFromContext(ctx) + if clientSubnetLoaded && transport.Tag() == contextTransport { + return nil, E.New("DNS query loopback in transport[", contextTransport, "]") + } + ctx = contextWithTransportTag(ctx, transport.Tag()) + if responseChecker != nil && c.rdrc != nil { + rejected := c.rdrc.LoadRDRC(transport.Tag(), question.Name, question.Qtype) + if rejected { + return nil, ErrResponseRejectedCached + } + } + ctx, cancel := context.WithTimeout(ctx, c.timeout) + response, err := transport.Exchange(ctx, message) + cancel() + if err != nil { + return nil, err + } + /*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA { + validResponse := response + loop: + for { + var ( + addresses int + queryCNAME string + ) + for _, rawRR := range validResponse.Answer { + switch rr := rawRR.(type) { + case *dns.A: + break loop + case *dns.AAAA: + break loop + case *dns.CNAME: + queryCNAME = rr.Target + } + } + if queryCNAME == "" { + break + } + exMessage := *message + exMessage.Question = []dns.Question{{ + Name: queryCNAME, + Qtype: question.Qtype, + }} + validResponse, err = c.Exchange(ctx, transport, &exMessage, options, responseChecker) + if err != nil { + return nil, err + } + } + if validResponse != response { + response.Answer = append(response.Answer, validResponse.Answer...) + } + }*/ + if responseChecker != nil { + addr, addrErr := MessageToAddresses(response) + if addrErr != nil || !responseChecker(addr) { + if c.rdrc != nil { + c.rdrc.SaveRDRCAsync(transport.Tag(), question.Name, question.Qtype, c.logger) + } + logRejectedResponse(c.logger, ctx, response) + return response, ErrResponseRejected + } + } + if question.Qtype == dns.TypeHTTPS { + if options.Strategy == C.DomainStrategyIPv4Only || options.Strategy == C.DomainStrategyIPv6Only { + for _, rr := range response.Answer { + https, isHTTPS := rr.(*dns.HTTPS) + if !isHTTPS { + continue + } + content := https.SVCB + content.Value = common.Filter(content.Value, func(it dns.SVCBKeyValue) bool { + if options.Strategy == C.DomainStrategyIPv4Only { + return it.Key() != dns.SVCB_IPV6HINT + } else { + return it.Key() != dns.SVCB_IPV4HINT + } + }) + https.SVCB = content + } + } + } + var timeToLive uint32 + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive { + timeToLive = record.Header().Ttl + } + } + } + if options.RewriteTTL != nil { + timeToLive = *options.RewriteTTL + } + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + record.Header().Ttl = timeToLive + } + } + response.Id = messageId + if !disableCache { + c.storeCache(transport, question, response, timeToLive) + } + logExchangedResponse(c.logger, ctx, response, timeToLive) + return response, err +} + +func (c *Client) Lookup(ctx context.Context, transport adapter.DNSTransport, domain string, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error) { + domain = FqdnToDomain(domain) + dnsName := dns.Fqdn(domain) + if options.Strategy == C.DomainStrategyIPv4Only { + return c.lookupToExchange(ctx, transport, dnsName, dns.TypeA, options, responseChecker) + } else if options.Strategy == C.DomainStrategyIPv6Only { + return c.lookupToExchange(ctx, transport, dnsName, dns.TypeAAAA, options, responseChecker) + } + var response4 []netip.Addr + var response6 []netip.Addr + var group task.Group + group.Append("exchange4", func(ctx context.Context) error { + response, err := c.lookupToExchange(ctx, transport, dnsName, dns.TypeA, options, responseChecker) + if err != nil { + return err + } + response4 = response + return nil + }) + group.Append("exchange6", func(ctx context.Context) error { + response, err := c.lookupToExchange(ctx, transport, dnsName, dns.TypeAAAA, options, responseChecker) + if err != nil { + return err + } + response6 = response + return nil + }) + err := group.Run(ctx) + if len(response4) == 0 && len(response6) == 0 { + return nil, err + } + return sortAddresses(response4, response6, options.Strategy), nil +} + +func (c *Client) ClearCache() { + if c.cache != nil { + c.cache.Purge() + } + if c.transportCache != nil { + c.transportCache.Purge() + } +} + +func (c *Client) LookupCache(domain string, strategy C.DomainStrategy) ([]netip.Addr, bool) { + if c.disableCache || c.independentCache { + return nil, false + } + if dns.IsFqdn(domain) { + domain = domain[:len(domain)-1] + } + dnsName := dns.Fqdn(domain) + if strategy == C.DomainStrategyIPv4Only { + response, err := c.questionCache(dns.Question{ + Name: dnsName, + Qtype: dns.TypeA, + Qclass: dns.ClassINET, + }, nil) + if err != ErrNotCached { + return response, true + } + } else if strategy == C.DomainStrategyIPv6Only { + response, err := c.questionCache(dns.Question{ + Name: dnsName, + Qtype: dns.TypeAAAA, + Qclass: dns.ClassINET, + }, nil) + if err != ErrNotCached { + return response, true + } + } else { + response4, _ := c.questionCache(dns.Question{ + Name: dnsName, + Qtype: dns.TypeA, + Qclass: dns.ClassINET, + }, nil) + response6, _ := c.questionCache(dns.Question{ + Name: dnsName, + Qtype: dns.TypeAAAA, + Qclass: dns.ClassINET, + }, nil) + if len(response4) > 0 || len(response6) > 0 { + return sortAddresses(response4, response6, strategy), true + } + } + return nil, false +} + +func (c *Client) ExchangeCache(ctx context.Context, message *dns.Msg) (*dns.Msg, bool) { + if c.disableCache || c.independentCache || len(message.Question) != 1 { + return nil, false + } + question := message.Question[0] + response, ttl := c.loadResponse(question, nil) + if response == nil { + return nil, false + } + logCachedResponse(c.logger, ctx, response, ttl) + response.Id = message.Id + return response, true +} + +func sortAddresses(response4 []netip.Addr, response6 []netip.Addr, strategy C.DomainStrategy) []netip.Addr { + if strategy == C.DomainStrategyPreferIPv6 { + return append(response6, response4...) + } else { + return append(response4, response6...) + } +} + +func (c *Client) storeCache(transport adapter.DNSTransport, question dns.Question, message *dns.Msg, timeToLive uint32) { + if timeToLive == 0 { + return + } + if c.disableExpire { + if !c.independentCache { + c.cache.Add(question, message) + } else { + c.transportCache.Add(transportCacheKey{ + Question: question, + transportTag: transport.Tag(), + }, message) + } + return + } + if !c.independentCache { + c.cache.AddWithLifetime(question, message, time.Second*time.Duration(timeToLive)) + } else { + c.transportCache.AddWithLifetime(transportCacheKey{ + Question: question, + transportTag: transport.Tag(), + }, message, time.Second*time.Duration(timeToLive)) + } +} + +func (c *Client) lookupToExchange(ctx context.Context, transport adapter.DNSTransport, name string, qType uint16, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error) { + question := dns.Question{ + Name: name, + Qtype: qType, + Qclass: dns.ClassINET, + } + disableCache := c.disableCache || options.DisableCache + if !disableCache { + cachedAddresses, err := c.questionCache(question, transport) + if err != ErrNotCached { + return cachedAddresses, err + } + } + message := dns.Msg{ + MsgHdr: dns.MsgHdr{ + RecursionDesired: true, + }, + Question: []dns.Question{question}, + } + response, err := c.Exchange(ctx, transport, &message, options, responseChecker) + if err != nil { + return nil, err + } + return MessageToAddresses(response) +} + +func (c *Client) questionCache(question dns.Question, transport adapter.DNSTransport) ([]netip.Addr, error) { + response, _ := c.loadResponse(question, transport) + if response == nil { + return nil, ErrNotCached + } + return MessageToAddresses(response) +} + +func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransport) (*dns.Msg, int) { + var ( + response *dns.Msg + loaded bool + ) + if c.disableExpire { + if !c.independentCache { + response, loaded = c.cache.Get(question) + } else { + response, loaded = c.transportCache.Get(transportCacheKey{ + Question: question, + transportTag: transport.Tag(), + }) + } + if !loaded { + return nil, 0 + } + return response.Copy(), 0 + } else { + var expireAt time.Time + if !c.independentCache { + response, expireAt, loaded = c.cache.GetWithLifetime(question) + } else { + response, expireAt, loaded = c.transportCache.GetWithLifetime(transportCacheKey{ + Question: question, + transportTag: transport.Tag(), + }) + } + if !loaded { + return nil, 0 + } + timeNow := time.Now() + if timeNow.After(expireAt) { + if !c.independentCache { + c.cache.Remove(question) + } else { + c.transportCache.Remove(transportCacheKey{ + Question: question, + transportTag: transport.Tag(), + }) + } + return nil, 0 + } + var originTTL int + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + if originTTL == 0 || record.Header().Ttl > 0 && int(record.Header().Ttl) < originTTL { + originTTL = int(record.Header().Ttl) + } + } + } + nowTTL := int(expireAt.Sub(timeNow).Seconds()) + if nowTTL < 0 { + nowTTL = 0 + } + response = response.Copy() + if originTTL > 0 { + duration := uint32(originTTL - nowTTL) + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + record.Header().Ttl = record.Header().Ttl - duration + } + } + } else { + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + record.Header().Ttl = uint32(nowTTL) + } + } + } + return response, nowTTL + } +} + +func MessageToAddresses(response *dns.Msg) ([]netip.Addr, error) { + if response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError { + return nil, RCodeError(response.Rcode) + } + addresses := make([]netip.Addr, 0, len(response.Answer)) + for _, rawAnswer := range response.Answer { + switch answer := rawAnswer.(type) { + case *dns.A: + addresses = append(addresses, M.AddrFromIP(answer.A)) + case *dns.AAAA: + addresses = append(addresses, M.AddrFromIP(answer.AAAA)) + case *dns.HTTPS: + for _, value := range answer.SVCB.Value { + if value.Key() == dns.SVCB_IPV4HINT || value.Key() == dns.SVCB_IPV6HINT { + addresses = append(addresses, common.Map(strings.Split(value.String(), ","), M.ParseAddr)...) + } + } + } + } + return addresses, nil +} + +func wrapError(err error) error { + switch dnsErr := err.(type) { + case *net.DNSError: + if dnsErr.IsNotFound { + return RCodeNameError + } + case *net.AddrError: + return RCodeNameError + } + return err +} + +type transportKey struct{} + +func contextWithTransportTag(ctx context.Context, transportTag string) context.Context { + return context.WithValue(ctx, transportKey{}, transportTag) +} + +func transportTagFromContext(ctx context.Context) (string, bool) { + value, loaded := ctx.Value(transportKey{}).(string) + return value, loaded +} + +func FixedResponse(id uint16, question dns.Question, addresses []netip.Addr, timeToLive uint32) *dns.Msg { + response := dns.Msg{ + MsgHdr: dns.MsgHdr{ + Id: id, + Rcode: dns.RcodeSuccess, + Response: true, + }, + Question: []dns.Question{question}, + } + for _, address := range addresses { + if address.Is4() { + response.Answer = append(response.Answer, &dns.A{ + Hdr: dns.RR_Header{ + Name: question.Name, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: timeToLive, + }, + A: address.AsSlice(), + }) + } else { + response.Answer = append(response.Answer, &dns.AAAA{ + Hdr: dns.RR_Header{ + Name: question.Name, + Rrtype: dns.TypeAAAA, + Class: dns.ClassINET, + Ttl: timeToLive, + }, + AAAA: address.AsSlice(), + }) + } + } + return &response +} diff --git a/dns/client_log.go b/dns/client_log.go new file mode 100644 index 00000000..67d00708 --- /dev/null +++ b/dns/client_log.go @@ -0,0 +1,69 @@ +package dns + +import ( + "context" + "strings" + + "github.com/sagernet/sing/common/logger" + + "github.com/miekg/dns" +) + +func logCachedResponse(logger logger.ContextLogger, ctx context.Context, response *dns.Msg, ttl int) { + if logger == nil || len(response.Question) == 0 { + return + } + domain := FqdnToDomain(response.Question[0].Name) + logger.DebugContext(ctx, "cached ", domain, " ", dns.RcodeToString[response.Rcode], " ", ttl) + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + logger.InfoContext(ctx, "cached ", dns.Type(record.Header().Rrtype).String(), " ", FormatQuestion(record.String())) + } + } +} + +func logExchangedResponse(logger logger.ContextLogger, ctx context.Context, response *dns.Msg, ttl uint32) { + if logger == nil || len(response.Question) == 0 { + return + } + domain := FqdnToDomain(response.Question[0].Name) + logger.DebugContext(ctx, "exchanged ", domain, " ", dns.RcodeToString[response.Rcode], " ", ttl) + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + logger.InfoContext(ctx, "exchanged ", dns.Type(record.Header().Rrtype).String(), " ", FormatQuestion(record.String())) + } + } +} + +func logRejectedResponse(logger logger.ContextLogger, ctx context.Context, response *dns.Msg) { + if logger == nil || len(response.Question) == 0 { + return + } + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + logger.InfoContext(ctx, "rejected ", dns.Type(record.Header().Rrtype).String(), " ", FormatQuestion(record.String())) + } + } +} + +func FqdnToDomain(fqdn string) string { + if dns.IsFqdn(fqdn) { + return fqdn[:len(fqdn)-1] + } + return fqdn +} + +func FormatQuestion(string string) string { + for strings.HasPrefix(string, ";") { + string = string[1:] + } + string = strings.ReplaceAll(string, "\t", " ") + string = strings.ReplaceAll(string, "\n", " ") + string = strings.ReplaceAll(string, ";; ", " ") + string = strings.ReplaceAll(string, "; ", " ") + + for strings.Contains(string, " ") { + string = strings.ReplaceAll(string, " ", " ") + } + return strings.TrimSpace(string) +} diff --git a/dns/client_truncate.go b/dns/client_truncate.go new file mode 100644 index 00000000..f00023f2 --- /dev/null +++ b/dns/client_truncate.go @@ -0,0 +1,31 @@ +package dns + +import ( + "github.com/sagernet/sing/common/buf" + + "github.com/miekg/dns" +) + +func TruncateDNSMessage(request *dns.Msg, response *dns.Msg, headroom int) (*buf.Buffer, error) { + maxLen := 512 + if edns0Option := request.IsEdns0(); edns0Option != nil { + if udpSize := int(edns0Option.UDPSize()); udpSize > 512 { + maxLen = udpSize + } + } + responseLen := response.Len() + if responseLen > maxLen { + copyResponse := *response + response = ©Response + response.Truncate(maxLen) + } + buffer := buf.NewSize(headroom*2 + 1 + responseLen) + buffer.Resize(headroom, 0) + rawMessage, err := response.PackBuffer(buffer.FreeBytes()) + if err != nil { + buffer.Release() + return nil, err + } + buffer.Truncate(len(rawMessage)) + return buffer, nil +} diff --git a/dns/extension_edns0_subnet.go b/dns/extension_edns0_subnet.go new file mode 100644 index 00000000..1c4033d3 --- /dev/null +++ b/dns/extension_edns0_subnet.go @@ -0,0 +1,56 @@ +package dns + +import ( + "net/netip" + + "github.com/miekg/dns" +) + +func SetClientSubnet(message *dns.Msg, clientSubnet netip.Prefix, override bool) *dns.Msg { + var ( + optRecord *dns.OPT + subnetOption *dns.EDNS0_SUBNET + ) +findExists: + for _, record := range message.Extra { + var isOPTRecord bool + if optRecord, isOPTRecord = record.(*dns.OPT); isOPTRecord { + for _, option := range optRecord.Option { + var isEDNS0Subnet bool + subnetOption, isEDNS0Subnet = option.(*dns.EDNS0_SUBNET) + if isEDNS0Subnet { + if !override { + return message + } + break findExists + } + } + } + } + if optRecord == nil { + exMessage := *message + message = &exMessage + optRecord = &dns.OPT{ + Hdr: dns.RR_Header{ + Name: ".", + Rrtype: dns.TypeOPT, + }, + } + message.Extra = append(message.Extra, optRecord) + } else { + message = message.Copy() + } + if subnetOption == nil { + subnetOption = new(dns.EDNS0_SUBNET) + optRecord.Option = append(optRecord.Option, subnetOption) + } + subnetOption.Code = dns.EDNS0SUBNET + if clientSubnet.Addr().Is4() { + subnetOption.Family = 1 + } else { + subnetOption.Family = 2 + } + subnetOption.SourceNetmask = uint8(clientSubnet.Bits()) + subnetOption.Address = clientSubnet.Addr().AsSlice() + return message +} diff --git a/dns/rcode.go b/dns/rcode.go new file mode 100644 index 00000000..5b7e52cc --- /dev/null +++ b/dns/rcode.go @@ -0,0 +1,33 @@ +package dns + +import F "github.com/sagernet/sing/common/format" + +const ( + RCodeSuccess RCodeError = 0 // NoError + RCodeFormatError RCodeError = 1 // FormErr + RCodeServerFailure RCodeError = 2 // ServFail + RCodeNameError RCodeError = 3 // NXDomain + RCodeNotImplemented RCodeError = 4 // NotImp + RCodeRefused RCodeError = 5 // Refused +) + +type RCodeError uint16 + +func (e RCodeError) Error() string { + switch e { + case RCodeSuccess: + return "success" + case RCodeFormatError: + return "format error" + case RCodeServerFailure: + return "server failure" + case RCodeNameError: + return "name error" + case RCodeNotImplemented: + return "not implemented" + case RCodeRefused: + return "refused" + default: + return F.ToString("unknown error: ", uint16(e)) + } +} diff --git a/dns/router.go b/dns/router.go new file mode 100644 index 00000000..8ecb8891 --- /dev/null +++ b/dns/router.go @@ -0,0 +1,437 @@ +package dns + +import ( + "context" + "errors" + "net/netip" + "strings" + "time" + + "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/experimental/libbox/platform" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + R "github.com/sagernet/sing-box/route/rule" + "github.com/sagernet/sing-tun" + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + F "github.com/sagernet/sing/common/format" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/contrab/freelru" + "github.com/sagernet/sing/contrab/maphash" + "github.com/sagernet/sing/service" + + mDNS "github.com/miekg/dns" +) + +var _ adapter.DNSRouter = (*Router)(nil) + +type Router struct { + ctx context.Context + logger logger.ContextLogger + transport adapter.DNSTransportManager + outbound adapter.OutboundManager + client adapter.DNSClient + rules []adapter.DNSRule + defaultDomainStrategy C.DomainStrategy + dnsReverseMapping freelru.Cache[netip.Addr, string] + platformInterface platform.Interface +} + +func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOptions) *Router { + router := &Router{ + ctx: ctx, + logger: logFactory.NewLogger("dns"), + transport: service.FromContext[adapter.DNSTransportManager](ctx), + outbound: service.FromContext[adapter.OutboundManager](ctx), + rules: make([]adapter.DNSRule, 0, len(options.Rules)), + defaultDomainStrategy: C.DomainStrategy(options.Strategy), + } + router.client = NewClient(ClientOptions{ + DisableCache: options.DNSClientOptions.DisableCache, + DisableExpire: options.DNSClientOptions.DisableExpire, + IndependentCache: options.DNSClientOptions.IndependentCache, + CacheCapacity: options.DNSClientOptions.CacheCapacity, + RDRC: func() adapter.RDRCStore { + cacheFile := service.FromContext[adapter.CacheFile](ctx) + if cacheFile == nil { + return nil + } + if !cacheFile.StoreRDRC() { + return nil + } + return cacheFile + }, + Logger: router.logger, + }) + if options.ReverseMapping { + router.dnsReverseMapping = common.Must1(freelru.NewSharded[netip.Addr, string](1024, maphash.NewHasher[netip.Addr]().Hash32)) + } + return router +} + +func (r *Router) Initialize(rules []option.DNSRule) error { + for i, ruleOptions := range rules { + dnsRule, err := R.NewDNSRule(r.ctx, r.logger, ruleOptions, true) + if err != nil { + return E.Cause(err, "parse dns rule[", i, "]") + } + r.rules = append(r.rules, dnsRule) + } + return nil +} + +func (r *Router) Start(stage adapter.StartStage) error { + monitor := taskmonitor.New(r.logger, C.StartTimeout) + switch stage { + case adapter.StartStateStart: + monitor.Start("initialize DNS client") + r.client.Start() + monitor.Finish() + + for i, rule := range r.rules { + monitor.Start("initialize DNS rule[", i, "]") + err := rule.Start() + monitor.Finish() + if err != nil { + return E.Cause(err, "initialize DNS rule[", i, "]") + } + } + } + return nil +} + +func (r *Router) Close() error { + monitor := taskmonitor.New(r.logger, C.StopTimeout) + var err error + for i, rule := range r.rules { + monitor.Start("close dns rule[", i, "]") + err = E.Append(err, rule.Close(), func(err error) error { + return E.Cause(err, "close dns rule[", i, "]") + }) + monitor.Finish() + } + return err +} + +func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, isAddressQuery bool, options *adapter.DNSQueryOptions) (adapter.DNSTransport, adapter.DNSRule, int) { + metadata := adapter.ContextFrom(ctx) + if metadata == nil { + panic("no context") + } + var currentRuleIndex int + if ruleIndex != -1 { + currentRuleIndex = ruleIndex + 1 + } + for ; currentRuleIndex < len(r.rules); currentRuleIndex++ { + currentRule := r.rules[currentRuleIndex] + if currentRule.WithAddressLimit() && !isAddressQuery { + continue + } + metadata.ResetRuleCache() + if currentRule.Match(metadata) { + displayRuleIndex := currentRuleIndex + if displayRuleIndex != -1 { + displayRuleIndex += displayRuleIndex + 1 + } + ruleDescription := currentRule.String() + if ruleDescription != "" { + r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] ", currentRule, " => ", currentRule.Action()) + } else { + r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action()) + } + switch action := currentRule.Action().(type) { + case *R.RuleActionDNSRoute: + transport, loaded := r.transport.Transport(action.Server) + if !loaded { + r.logger.ErrorContext(ctx, "transport not found: ", action.Server) + continue + } + isFakeIP := transport.Type() == C.DNSTypeFakeIP + if isFakeIP && !allowFakeIP { + continue + } + if action.Strategy != C.DomainStrategyAsIS { + options.Strategy = action.Strategy + } + if isFakeIP || action.DisableCache { + options.DisableCache = true + } + if action.RewriteTTL != nil { + options.RewriteTTL = action.RewriteTTL + } + if action.ClientSubnet.IsValid() { + options.ClientSubnet = action.ClientSubnet + } + if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy { + if options.Strategy == C.DomainStrategyAsIS { + options.Strategy = legacyTransport.LegacyStrategy() + } + if !options.ClientSubnet.IsValid() { + options.ClientSubnet = legacyTransport.LegacyClientSubnet() + } + } + r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action()) + return transport, currentRule, currentRuleIndex + case *R.RuleActionDNSRouteOptions: + if action.Strategy != C.DomainStrategyAsIS { + options.Strategy = action.Strategy + } + if action.DisableCache { + options.DisableCache = true + } + if action.RewriteTTL != nil { + options.RewriteTTL = action.RewriteTTL + } + if action.ClientSubnet.IsValid() { + options.ClientSubnet = action.ClientSubnet + } + r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action()) + case *R.RuleActionReject: + r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action()) + return nil, currentRule, currentRuleIndex + } + } + } + return r.transport.Default(), nil, -1 +} + +func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapter.DNSQueryOptions) (*mDNS.Msg, error) { + if len(message.Question) != 1 { + r.logger.WarnContext(ctx, "bad question size: ", len(message.Question)) + responseMessage := mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: message.Id, + Response: true, + Rcode: mDNS.RcodeFormatError, + }, + Question: message.Question, + } + return &responseMessage, nil + } + r.logger.DebugContext(ctx, "exchange ", FormatQuestion(message.Question[0].String())) + var ( + transport adapter.DNSTransport + err error + ) + response, cached := r.client.ExchangeCache(ctx, message) + if !cached { + var metadata *adapter.InboundContext + ctx, metadata = adapter.ExtendContext(ctx) + metadata.Destination = M.Socksaddr{} + metadata.QueryType = message.Question[0].Qtype + switch metadata.QueryType { + case mDNS.TypeA: + metadata.IPVersion = 4 + case mDNS.TypeAAAA: + metadata.IPVersion = 6 + } + metadata.Domain = FqdnToDomain(message.Question[0].Name) + if options.Transport != nil { + transport = options.Transport + if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy { + if options.Strategy == C.DomainStrategyAsIS { + options.Strategy = legacyTransport.LegacyStrategy() + } + if !options.ClientSubnet.IsValid() { + options.ClientSubnet = legacyTransport.LegacyClientSubnet() + } + } + if options.Strategy == C.DomainStrategyAsIS { + options.Strategy = r.defaultDomainStrategy + } + response, err = r.client.Exchange(ctx, transport, message, options, nil) + } else { + var ( + rule adapter.DNSRule + ruleIndex int + ) + ruleIndex = -1 + for { + dnsCtx := adapter.OverrideContext(ctx) + dnsOptions := options + transport, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message), &dnsOptions) + if rule != nil { + switch action := rule.Action().(type) { + case *R.RuleActionReject: + switch action.Method { + case C.RuleActionRejectMethodDefault: + return FixedResponse(message.Id, message.Question[0], nil, 0), nil + case C.RuleActionRejectMethodDrop: + return nil, tun.ErrDrop + } + } + } + var responseCheck func(responseAddrs []netip.Addr) bool + if rule != nil && rule.WithAddressLimit() { + responseCheck = func(responseAddrs []netip.Addr) bool { + metadata.DestinationAddresses = responseAddrs + return rule.MatchAddressLimit(metadata) + } + } + if dnsOptions.Strategy == C.DomainStrategyAsIS { + dnsOptions.Strategy = r.defaultDomainStrategy + } + response, err = r.client.Exchange(dnsCtx, transport, message, dnsOptions, responseCheck) + var rejected bool + if err != nil { + if errors.Is(err, ErrResponseRejectedCached) { + rejected = true + r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())), " (cached)") + } else if errors.Is(err, ErrResponseRejected) { + rejected = true + r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String()))) + } else if len(message.Question) > 0 { + r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", FormatQuestion(message.Question[0].String()))) + } else { + r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for ")) + } + } + if responseCheck != nil && rejected { + continue + } + break + } + } + } + if err != nil { + return nil, err + } + if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 { + if transport == nil || transport.Type() != C.DNSTypeFakeIP { + for _, answer := range response.Answer { + switch record := answer.(type) { + case *mDNS.A: + r.dnsReverseMapping.AddWithLifetime(M.AddrFromIP(record.A), FqdnToDomain(record.Hdr.Name), time.Duration(record.Hdr.Ttl)*time.Second) + case *mDNS.AAAA: + r.dnsReverseMapping.AddWithLifetime(M.AddrFromIP(record.AAAA), FqdnToDomain(record.Hdr.Name), time.Duration(record.Hdr.Ttl)*time.Second) + } + } + } + } + return response, nil +} + +func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQueryOptions) ([]netip.Addr, error) { + var ( + responseAddrs []netip.Addr + cached bool + err error + ) + printResult := func() { + if err != nil { + if errors.Is(err, ErrResponseRejectedCached) { + r.logger.DebugContext(ctx, "response rejected for ", domain, " (cached)") + } else if errors.Is(err, ErrResponseRejected) { + r.logger.DebugContext(ctx, "response rejected for ", domain) + } else { + r.logger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain)) + } + } else if len(responseAddrs) == 0 { + r.logger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result") + err = RCodeNameError + } + } + responseAddrs, cached = r.client.LookupCache(domain, options.Strategy) + if cached { + if len(responseAddrs) == 0 { + return nil, RCodeNameError + } + return responseAddrs, nil + } + r.logger.DebugContext(ctx, "lookup domain ", domain) + ctx, metadata := adapter.ExtendContext(ctx) + metadata.Destination = M.Socksaddr{} + metadata.Domain = FqdnToDomain(domain) + if options.Transport != nil { + transport := options.Transport + if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy { + if options.Strategy == C.DomainStrategyAsIS { + options.Strategy = r.defaultDomainStrategy + } + if !options.ClientSubnet.IsValid() { + options.ClientSubnet = legacyTransport.LegacyClientSubnet() + } + } + if options.Strategy == C.DomainStrategyAsIS { + options.Strategy = r.defaultDomainStrategy + } + responseAddrs, err = r.client.Lookup(ctx, transport, domain, options, nil) + } else { + var ( + transport adapter.DNSTransport + rule adapter.DNSRule + ruleIndex int + ) + ruleIndex = -1 + for { + dnsCtx := adapter.OverrideContext(ctx) + transport, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true, &options) + if rule != nil { + switch action := rule.Action().(type) { + case *R.RuleActionReject: + switch action.Method { + case C.RuleActionRejectMethodDefault: + return nil, nil + case C.RuleActionRejectMethodDrop: + return nil, tun.ErrDrop + } + } + } + var responseCheck func(responseAddrs []netip.Addr) bool + if rule != nil && rule.WithAddressLimit() { + responseCheck = func(responseAddrs []netip.Addr) bool { + metadata.DestinationAddresses = responseAddrs + return rule.MatchAddressLimit(metadata) + } + } + if options.Strategy == C.DomainStrategyAsIS { + options.Strategy = r.defaultDomainStrategy + } + responseAddrs, err = r.client.Lookup(dnsCtx, transport, domain, options, responseCheck) + if responseCheck == nil || err == nil { + break + } + printResult() + } + } + printResult() + if len(responseAddrs) > 0 { + r.logger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(responseAddrs), " ")) + } + return responseAddrs, err +} + +func isAddressQuery(message *mDNS.Msg) bool { + for _, question := range message.Question { + if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA || question.Qtype == mDNS.TypeHTTPS { + return true + } + } + return false +} + +func (r *Router) ClearCache() { + r.client.ClearCache() + if r.platformInterface != nil { + r.platformInterface.ClearDNSCache() + } +} + +func (r *Router) LookupReverseMapping(ip netip.Addr) (string, bool) { + if r.dnsReverseMapping == nil { + return "", false + } + domain, loaded := r.dnsReverseMapping.Get(ip) + return domain, loaded +} + +func (r *Router) ResetNetwork() { + r.ClearCache() + for _, transport := range r.transport.Transports() { + transport.Reset() + } +} diff --git a/transport/dhcp/server.go b/dns/transport/dhcp/dhcp.go similarity index 66% rename from transport/dhcp/server.go rename to dns/transport/dhcp/dhcp.go index 8b9187f0..c75d7369 100644 --- a/transport/dhcp/server.go +++ b/dns/transport/dhcp/dhcp.go @@ -3,9 +3,6 @@ package dhcp import ( "context" "net" - "net/netip" - "net/url" - "os" "runtime" "strings" "sync" @@ -14,13 +11,18 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/task" "github.com/sagernet/sing/common/x/list" "github.com/sagernet/sing/service" @@ -29,76 +31,70 @@ import ( mDNS "github.com/miekg/dns" ) -func init() { - dns.RegisterTransport([]string{"dhcp"}, func(options dns.TransportOptions) (dns.Transport, error) { - return NewTransport(options) - }) +func RegisterTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.DHCPDNSServerOptions](registry, C.DNSTypeDHCP, NewTransport) } +var _ adapter.DNSTransport = (*Transport)(nil) + type Transport struct { - options dns.TransportOptions - router adapter.Router + dns.TransportAdapter + ctx context.Context + dialer N.Dialer + logger logger.ContextLogger networkManager adapter.NetworkManager interfaceName string - autoInterface bool interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback] - transports []dns.Transport + transports []adapter.DNSTransport updateAccess sync.Mutex updatedAt time.Time } -func NewTransport(options dns.TransportOptions) (*Transport, error) { - linkURL, err := url.Parse(options.Address) +func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.DHCPDNSServerOptions) (adapter.DNSTransport, error) { + transportDialer, err := dns.NewLocalDialer(ctx, options.LocalDNSServerOptions) if err != nil { return nil, err } - if linkURL.Host == "" { - return nil, E.New("missing interface name for DHCP") - } - transport := &Transport{ - options: options, - networkManager: service.FromContext[adapter.NetworkManager](options.Context), - interfaceName: linkURL.Host, - autoInterface: linkURL.Host == "auto", - } - return transport, nil + return &Transport{ + TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeDHCP, tag, options.LocalDNSServerOptions), + ctx: ctx, + dialer: transportDialer, + logger: logger, + networkManager: service.FromContext[adapter.NetworkManager](ctx), + interfaceName: options.Interface, + }, nil } -func (t *Transport) Name() string { - return t.options.Name -} - -func (t *Transport) Start() error { +func (t *Transport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } err := t.fetchServers() if err != nil { return err } - if t.autoInterface { + if t.interfaceName == "" { t.interfaceCallback = t.networkManager.InterfaceMonitor().RegisterCallback(t.interfaceUpdated) } return nil } +func (t *Transport) Close() error { + for _, transport := range t.transports { + transport.Reset() + } + if t.interfaceCallback != nil { + t.networkManager.InterfaceMonitor().UnregisterCallback(t.interfaceCallback) + } + return nil +} + func (t *Transport) Reset() { for _, transport := range t.transports { transport.Reset() } } -func (t *Transport) Close() error { - for _, transport := range t.transports { - transport.Close() - } - if t.interfaceCallback != nil { - t.networkManager.InterfaceMonitor().UnregisterCallback(t.interfaceCallback) - } - return nil -} - -func (t *Transport) Raw() bool { - return true -} - func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { err := t.fetchServers() if err != nil { @@ -120,7 +116,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, } func (t *Transport) fetchInterface() (*control.Interface, error) { - if t.autoInterface { + if t.interfaceName == "" { if t.networkManager.InterfaceMonitor() == nil { return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface") } @@ -152,8 +148,8 @@ func (t *Transport) updateServers() error { return E.Cause(err, "dhcp: prepare interface") } - t.options.Logger.Info("dhcp: query DNS servers on ", iface.Name) - fetchCtx, cancel := context.WithTimeout(t.options.Context, C.DHCPTimeout) + t.logger.Info("dhcp: query DNS servers on ", iface.Name) + fetchCtx, cancel := context.WithTimeout(t.ctx, C.DHCPTimeout) err = t.fetchServers0(fetchCtx, iface) cancel() if err != nil { @@ -169,7 +165,7 @@ func (t *Transport) updateServers() error { func (t *Transport) interfaceUpdated(defaultInterface *control.Interface, flags int) { err := t.updateServers() if err != nil { - t.options.Logger.Error("update servers: ", err) + t.logger.Error("update servers: ", err) } } @@ -181,7 +177,7 @@ func (t *Transport) fetchServers0(ctx context.Context, iface *control.Interface) if runtime.GOOS == "linux" || runtime.GOOS == "android" { listenAddr = "255.255.255.255:68" } - packetConn, err := listener.ListenPacket(t.options.Context, "udp4", listenAddr) + packetConn, err := listener.ListenPacket(t.ctx, "udp4", listenAddr) if err != nil { return err } @@ -219,17 +215,17 @@ func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn ne dhcpPacket, err := dhcpv4.FromBytes(buffer.Bytes()) if err != nil { - t.options.Logger.Trace("dhcp: parse DHCP response: ", err) + t.logger.Trace("dhcp: parse DHCP response: ", err) return err } if dhcpPacket.MessageType() != dhcpv4.MessageTypeOffer { - t.options.Logger.Trace("dhcp: expected OFFER response, but got ", dhcpPacket.MessageType()) + t.logger.Trace("dhcp: expected OFFER response, but got ", dhcpPacket.MessageType()) continue } if dhcpPacket.TransactionID != transactionID { - t.options.Logger.Trace("dhcp: expected transaction ID ", transactionID, ", but got ", dhcpPacket.TransactionID) + t.logger.Trace("dhcp: expected transaction ID ", transactionID, ", but got ", dhcpPacket.TransactionID) continue } @@ -237,44 +233,27 @@ func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn ne if len(dns) == 0 { return nil } - - var addrs []netip.Addr - for _, ip := range dns { - addr, _ := netip.AddrFromSlice(ip) - addrs = append(addrs, addr.Unmap()) - } - return t.recreateServers(iface, addrs) + return t.recreateServers(iface, common.Map(dns, func(it net.IP) M.Socksaddr { + return M.SocksaddrFrom(M.AddrFromIP(it), 53) + })) } } -func (t *Transport) recreateServers(iface *control.Interface, serverAddrs []netip.Addr) error { +func (t *Transport) recreateServers(iface *control.Interface, serverAddrs []M.Socksaddr) error { if len(serverAddrs) > 0 { - t.options.Logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, func(it netip.Addr) string { - return it.String() - }), ","), "]") + t.logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, M.Socksaddr.String), ","), "]") } - serverDialer := common.Must1(dialer.NewDefault(t.options.Context, option.DialerOptions{ + serverDialer := common.Must1(dialer.NewDefault(t.ctx, option.DialerOptions{ BindInterface: iface.Name, UDPFragmentDefault: true, })) - var transports []dns.Transport + var transports []adapter.DNSTransport for _, serverAddr := range serverAddrs { - newOptions := t.options - newOptions.Address = serverAddr.String() - newOptions.Dialer = serverDialer - serverTransport, err := dns.NewUDPTransport(newOptions) - if err != nil { - return E.Cause(err, "create UDP transport from DHCP result: ", serverAddr) - } - transports = append(transports, serverTransport) + transports = append(transports, transport.NewUDPRaw(t.logger, t.TransportAdapter, serverDialer, serverAddr)) } for _, transport := range t.transports { - transport.Close() + transport.Reset() } t.transports = transports return nil } - -func (t *Transport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { - return nil, os.ErrInvalid -} diff --git a/dns/transport/fakeip/fakeip.go b/dns/transport/fakeip/fakeip.go new file mode 100644 index 00000000..07f0fd09 --- /dev/null +++ b/dns/transport/fakeip/fakeip.go @@ -0,0 +1,67 @@ +package fakeip + +import ( + "context" + "net/netip" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + + mDNS "github.com/miekg/dns" +) + +func RegisterTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.FakeIPDNSServerOptions](registry, C.DNSTypeFakeIP, NewTransport) +} + +var _ adapter.FakeIPTransport = (*Transport)(nil) + +type Transport struct { + dns.TransportAdapter + logger logger.ContextLogger + store adapter.FakeIPStore +} + +func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.FakeIPDNSServerOptions) (adapter.DNSTransport, error) { + store := NewStore(ctx, logger, options.Inet4Range.Build(netip.Prefix{}), options.Inet6Range.Build(netip.Prefix{})) + return &Transport{ + TransportAdapter: dns.NewTransportAdapter(C.DNSTypeFakeIP, tag, nil), + logger: logger, + store: store, + }, nil +} + +func (t *Transport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return t.store.Start() +} + +func (t *Transport) Close() error { + return t.store.Close() +} + +func (t *Transport) Reset() { +} + +func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + question := message.Question[0] + if question.Qtype != mDNS.TypeA && question.Qtype != mDNS.TypeAAAA { + return nil, E.New("only IP queries are supported by fakeip") + } + address, err := t.store.Create(dns.FqdnToDomain(question.Name), question.Qtype == mDNS.TypeAAAA) + if err != nil { + return nil, err + } + return dns.FixedResponse(message.Id, question, []netip.Addr{address}, C.DefaultDNSTTL), nil +} + +func (t *Transport) Store() adapter.FakeIPStore { + return t.store +} diff --git a/transport/fakeip/memory.go b/dns/transport/fakeip/memory.go similarity index 100% rename from transport/fakeip/memory.go rename to dns/transport/fakeip/memory.go diff --git a/transport/fakeip/store.go b/dns/transport/fakeip/store.go similarity index 100% rename from transport/fakeip/store.go rename to dns/transport/fakeip/store.go diff --git a/dns/transport/hosts/hosts.go b/dns/transport/hosts/hosts.go new file mode 100644 index 00000000..29f6778a --- /dev/null +++ b/dns/transport/hosts/hosts.go @@ -0,0 +1,63 @@ +package hosts + +import ( + "context" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + + mDNS "github.com/miekg/dns" +) + +func RegisterTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.HostsDNSServerOptions](registry, C.DNSTypeHosts, NewTransport) +} + +var _ adapter.DNSTransport = (*Transport)(nil) + +type Transport struct { + dns.TransportAdapter + files []*File +} + +func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.HostsDNSServerOptions) (adapter.DNSTransport, error) { + var files []*File + if len(options.Path) == 0 { + files = append(files, NewFile(DefaultPath)) + } else { + for _, path := range options.Path { + files = append(files, NewFile(path)) + } + } + return &Transport{ + TransportAdapter: dns.NewTransportAdapter(C.DNSTypeHosts, tag, nil), + files: files, + }, nil +} + +func (t *Transport) Reset() { +} + +func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + question := message.Question[0] + domain := dns.FqdnToDomain(question.Name) + if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA { + for _, file := range t.files { + addresses := file.Lookup(domain) + if len(addresses) > 0 { + return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil + } + } + } + return &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: message.Id, + Rcode: mDNS.RcodeNameError, + Response: true, + }, + Question: []mDNS.Question{question}, + }, nil +} diff --git a/dns/transport/hosts/hosts_file.go b/dns/transport/hosts/hosts_file.go new file mode 100644 index 00000000..7ff34f69 --- /dev/null +++ b/dns/transport/hosts/hosts_file.go @@ -0,0 +1,102 @@ +package hosts + +import ( + "bufio" + "errors" + "io" + "net/netip" + "os" + "strings" + "sync" + "time" + + "github.com/miekg/dns" +) + +const cacheMaxAge = 5 * time.Second + +type File struct { + path string + access sync.Mutex + byName map[string][]netip.Addr + expire time.Time + modTime time.Time + size int64 +} + +func NewFile(path string) *File { + return &File{ + path: path, + } +} + +func (f *File) Lookup(name string) []netip.Addr { + f.access.Lock() + defer f.access.Unlock() + f.update() + return f.byName[name] +} + +func (f *File) update() { + now := time.Now() + if now.Before(f.expire) && len(f.byName) > 0 { + return + } + stat, err := os.Stat(f.path) + if err != nil { + return + } + if f.modTime.Equal(stat.ModTime()) && f.size == stat.Size() { + f.expire = now.Add(cacheMaxAge) + return + } + byName := make(map[string][]netip.Addr) + file, err := os.Open(f.path) + if err != nil { + return + } + defer file.Close() + reader := bufio.NewReader(file) + var ( + prefix []byte + line []byte + isPrefix bool + ) + for { + line, isPrefix, err = reader.ReadLine() + if err != nil { + if errors.Is(err, io.EOF) { + break + } + return + } + if isPrefix { + prefix = append(prefix, line...) + continue + } else if len(prefix) > 0 { + line = append(prefix, line...) + prefix = nil + } + commentIndex := strings.IndexRune(string(line), '#') + if commentIndex != -1 { + line = line[:commentIndex] + } + fields := strings.Fields(string(line)) + if len(fields) < 2 { + continue + } + var addr netip.Addr + addr, err = netip.ParseAddr(fields[0]) + if err != nil { + continue + } + for index := 1; index < len(fields); index++ { + canonicalName := dns.CanonicalName(fields[index]) + byName[canonicalName] = append(byName[canonicalName], addr) + } + } + f.expire = now.Add(cacheMaxAge) + f.modTime = stat.ModTime() + f.size = stat.Size() + f.byName = byName +} diff --git a/dns/transport/hosts/hosts_test.go b/dns/transport/hosts/hosts_test.go new file mode 100644 index 00000000..944aa437 --- /dev/null +++ b/dns/transport/hosts/hosts_test.go @@ -0,0 +1,16 @@ +package hosts_test + +import ( + "net/netip" + "testing" + + "github.com/sagernet/sing-box/dns/transport/hosts" + + "github.com/stretchr/testify/require" +) + +func TestHosts(t *testing.T) { + t.Parallel() + require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, hosts.NewFile("testdata/hosts").Lookup("localhost.")) + require.NotEmpty(t, hosts.NewFile(hosts.DefaultPath).Lookup("localhost.")) +} diff --git a/dns/transport/hosts/hosts_unix.go b/dns/transport/hosts/hosts_unix.go new file mode 100644 index 00000000..4caed8b4 --- /dev/null +++ b/dns/transport/hosts/hosts_unix.go @@ -0,0 +1,5 @@ +//go:build !windows + +package hosts + +var DefaultPath = "/etc/hosts" diff --git a/dns/transport/hosts/hosts_windows.go b/dns/transport/hosts/hosts_windows.go new file mode 100644 index 00000000..3144e50d --- /dev/null +++ b/dns/transport/hosts/hosts_windows.go @@ -0,0 +1,17 @@ +package hosts + +import ( + "path/filepath" + + "golang.org/x/sys/windows" +) + +var DefaultPath string + +func init() { + systemDirectory, err := windows.GetSystemDirectory() + if err != nil { + systemDirectory = "C:\\Windows\\System32" + } + DefaultPath = filepath.Join(systemDirectory, "Drivers/etc/hosts") +} diff --git a/dns/transport/hosts/testdata/hosts b/dns/transport/hosts/testdata/hosts new file mode 100644 index 00000000..9ddcc8c1 --- /dev/null +++ b/dns/transport/hosts/testdata/hosts @@ -0,0 +1,2 @@ +127.0.0.1 localhost +::1 localhost diff --git a/dns/transport/https.go b/dns/transport/https.go new file mode 100644 index 00000000..1cfb2574 --- /dev/null +++ b/dns/transport/https.go @@ -0,0 +1,204 @@ +package transport + +import ( + "bytes" + "context" + "io" + "net" + "net/http" + "net/url" + "strconv" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/tls" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + aTLS "github.com/sagernet/sing/common/tls" + sHTTP "github.com/sagernet/sing/protocol/http" + + mDNS "github.com/miekg/dns" + "golang.org/x/net/http2" +) + +const MimeType = "application/dns-message" + +var _ adapter.DNSTransport = (*HTTPSTransport)(nil) + +func RegisterHTTPS(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.RemoteHTTPSDNSServerOptions](registry, C.DNSTypeHTTPS, NewHTTPS) +} + +type HTTPSTransport struct { + dns.TransportAdapter + logger logger.ContextLogger + dialer N.Dialer + destination *url.URL + headers http.Header + transport *http.Transport +} + +func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteHTTPSDNSServerOptions) (adapter.DNSTransport, error) { + transportDialer, err := dns.NewRemoteDialer(ctx, options.RemoteDNSServerOptions) + if err != nil { + return nil, err + } + tlsOptions := common.PtrValueOrDefault(options.TLS) + tlsOptions.Enabled = true + tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions) + if err != nil { + return nil, err + } + if common.Error(tlsConfig.Config()) == nil && !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) { + tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), http2.NextProtoTLS)) + } + if !common.Contains(tlsConfig.NextProtos(), "http/1.1") { + tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), "http/1.1")) + } + headers := options.Headers.Build() + host := headers.Get("Host") + if host != "" { + headers.Del("Host") + } else { + if tlsConfig.ServerName() != "" { + host = tlsConfig.ServerName() + } else { + host = options.Server + } + } + destinationURL := url.URL{ + Scheme: "https", + Host: host, + } + if destinationURL.Host == "" { + destinationURL.Host = options.Server + } + if options.ServerPort != 0 && options.ServerPort != 443 { + destinationURL.Host = net.JoinHostPort(destinationURL.Host, strconv.Itoa(int(options.ServerPort))) + } + path := options.Path + if path == "" { + path = "/dns-query" + } + err = sHTTP.URLSetPath(&destinationURL, path) + if err != nil { + return nil, err + } + serverAddr := options.ServerOptions.Build() + if serverAddr.Port == 0 { + serverAddr.Port = 443 + } + return NewHTTPSRaw( + dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeHTTPS, tag, options.RemoteDNSServerOptions), + logger, + transportDialer, + &destinationURL, + headers, + serverAddr, + tlsConfig, + ), nil +} + +func NewHTTPSRaw( + adapter dns.TransportAdapter, + logger log.ContextLogger, + dialer N.Dialer, + destination *url.URL, + headers http.Header, + serverAddr M.Socksaddr, + tlsConfig tls.Config, +) *HTTPSTransport { + var transport *http.Transport + if tlsConfig != nil { + transport = &http.Transport{ + ForceAttemptHTTP2: true, + DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + tcpConn, hErr := dialer.DialContext(ctx, network, serverAddr) + if hErr != nil { + return nil, hErr + } + tlsConn, hErr := aTLS.ClientHandshake(ctx, tcpConn, tlsConfig) + if hErr != nil { + tcpConn.Close() + return nil, hErr + } + return tlsConn, nil + }, + } + } else { + transport = &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return dialer.DialContext(ctx, network, serverAddr) + }, + } + } + return &HTTPSTransport{ + TransportAdapter: adapter, + logger: logger, + dialer: dialer, + destination: destination, + headers: headers, + transport: transport, + } +} + +func (t *HTTPSTransport) Reset() { + t.transport.CloseIdleConnections() + t.transport = t.transport.Clone() +} + +func (t *HTTPSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + exMessage := *message + exMessage.Id = 0 + exMessage.Compress = true + requestBuffer := buf.NewSize(1 + message.Len()) + rawMessage, err := exMessage.PackBuffer(requestBuffer.FreeBytes()) + if err != nil { + requestBuffer.Release() + return nil, err + } + request, err := http.NewRequestWithContext(ctx, http.MethodPost, t.destination.String(), bytes.NewReader(rawMessage)) + if err != nil { + requestBuffer.Release() + return nil, err + } + request.Header = t.headers.Clone() + request.Header.Set("Content-Type", MimeType) + request.Header.Set("Accept", MimeType) + response, err := t.transport.RoundTrip(request) + requestBuffer.Release() + if err != nil { + return nil, err + } + defer response.Body.Close() + if response.StatusCode != http.StatusOK { + return nil, E.New("unexpected status: ", response.Status) + } + var responseMessage mDNS.Msg + if response.ContentLength > 0 { + responseBuffer := buf.NewSize(int(response.ContentLength)) + _, err = responseBuffer.ReadFullFrom(response.Body, int(response.ContentLength)) + if err != nil { + return nil, err + } + err = responseMessage.Unpack(responseBuffer.Bytes()) + responseBuffer.Release() + } else { + rawMessage, err = io.ReadAll(response.Body) + if err != nil { + return nil, err + } + err = responseMessage.Unpack(rawMessage) + } + if err != nil { + return nil, err + } + return &responseMessage, nil +} diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go new file mode 100644 index 00000000..e5e8aef9 --- /dev/null +++ b/dns/transport/local/local.go @@ -0,0 +1,197 @@ +package local + +import ( + "context" + "math/rand" + "time" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport/hosts" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + + mDNS "github.com/miekg/dns" +) + +func RegisterTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.LocalDNSServerOptions](registry, C.DNSTypeLocal, NewTransport) +} + +var _ adapter.DNSTransport = (*Transport)(nil) + +type Transport struct { + dns.TransportAdapter + hosts *hosts.File + dialer N.Dialer +} + +func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.LocalDNSServerOptions) (adapter.DNSTransport, error) { + transportDialer, err := dns.NewLocalDialer(ctx, options) + if err != nil { + return nil, err + } + return &Transport{ + TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options), + hosts: hosts.NewFile(hosts.DefaultPath), + dialer: transportDialer, + }, nil +} + +func (t *Transport) Reset() { +} + +func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + question := message.Question[0] + domain := dns.FqdnToDomain(question.Name) + if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA { + addresses := t.hosts.Lookup(domain) + if len(addresses) > 0 { + return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil + } + } + systemConfig := getSystemDNSConfig() + if systemConfig.singleRequest || !(message.Question[0].Qtype == mDNS.TypeA || message.Question[0].Qtype == mDNS.TypeAAAA) { + return t.exchangeSingleRequest(ctx, systemConfig, message, domain) + } else { + return t.exchangeParallel(ctx, systemConfig, message, domain) + } +} + +func (t *Transport) exchangeSingleRequest(ctx context.Context, systemConfig *dnsConfig, message *mDNS.Msg, domain string) (*mDNS.Msg, error) { + var lastErr error + for _, fqdn := range systemConfig.nameList(domain) { + response, err := t.tryOneName(ctx, systemConfig, fqdn, message) + if err != nil { + lastErr = err + continue + } + return response, nil + } + return nil, lastErr +} + +func (t *Transport) exchangeParallel(ctx context.Context, systemConfig *dnsConfig, message *mDNS.Msg, domain string) (*mDNS.Msg, error) { + returned := make(chan struct{}) + defer close(returned) + type queryResult struct { + response *mDNS.Msg + err error + } + results := make(chan queryResult) + startRacer := func(ctx context.Context, fqdn string) { + response, err := t.tryOneName(ctx, systemConfig, fqdn, message) + if err == nil { + addresses, _ := dns.MessageToAddresses(response) + if len(addresses) == 0 { + err = E.New(fqdn, ": empty result") + } + } + select { + case results <- queryResult{response, err}: + case <-returned: + } + } + queryCtx, queryCancel := context.WithCancel(ctx) + defer queryCancel() + var nameCount int + for _, fqdn := range systemConfig.nameList(domain) { + nameCount++ + go startRacer(queryCtx, fqdn) + } + var errors []error + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case result := <-results: + if result.err == nil { + return result.response, nil + } + errors = append(errors, result.err) + if len(errors) == nameCount { + return nil, E.Errors(errors...) + } + } + } +} + +func (t *Transport) tryOneName(ctx context.Context, config *dnsConfig, fqdn string, message *mDNS.Msg) (*mDNS.Msg, error) { + serverOffset := config.serverOffset() + sLen := uint32(len(config.servers)) + var lastErr error + for i := 0; i < config.attempts; i++ { + for j := uint32(0); j < sLen; j++ { + server := config.servers[(serverOffset+j)%sLen] + question := message.Question[0] + question.Name = fqdn + response, err := t.exchangeOne(ctx, M.ParseSocksaddr(server), question, config.timeout, config.useTCP, config.trustAD) + if err != nil { + lastErr = err + continue + } + return response, nil + } + } + return nil, E.Cause(lastErr, fqdn) +} + +func (t *Transport) exchangeOne(ctx context.Context, server M.Socksaddr, question mDNS.Question, timeout time.Duration, useTCP, ad bool) (*mDNS.Msg, error) { + var networks []string + if useTCP { + networks = []string{N.NetworkTCP} + } else { + networks = []string{N.NetworkUDP, N.NetworkTCP} + } + request := &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: uint16(rand.Uint32()), + RecursionDesired: true, + AuthenticatedData: ad, + }, + Question: []mDNS.Question{question}, + Compress: true, + } + request.SetEdns0(maxDNSPacketSize, false) + buffer := buf.Get(buf.UDPBufferSize) + defer buf.Put(buffer) + for _, network := range networks { + ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout)) + defer cancel() + conn, err := t.dialer.DialContext(ctx, network, server) + if err != nil { + return nil, err + } + defer conn.Close() + if deadline, loaded := ctx.Deadline(); loaded && !deadline.IsZero() { + conn.SetDeadline(deadline) + } + rawMessage, err := request.PackBuffer(buffer) + if err != nil { + return nil, E.Cause(err, "pack request") + } + _, err = conn.Write(rawMessage) + if err != nil { + return nil, E.Cause(err, "write request") + } + n, err := conn.Read(buffer) + if err != nil { + return nil, E.Cause(err, "read response") + } + var response mDNS.Msg + err = response.Unpack(buffer[:n]) + if err != nil { + return nil, E.Cause(err, "unpack response") + } + if response.Truncated && network == N.NetworkUDP { + continue + } + return &response, nil + } + panic("unexpected") +} diff --git a/dns/transport/local/resolv.go b/dns/transport/local/resolv.go new file mode 100644 index 00000000..5484d0ec --- /dev/null +++ b/dns/transport/local/resolv.go @@ -0,0 +1,146 @@ +package local + +import ( + "os" + "runtime" + "strings" + "sync" + "sync/atomic" + "time" +) + +const ( + // net.maxDNSPacketSize + maxDNSPacketSize = 1232 +) + +type resolverConfig struct { + initOnce sync.Once + ch chan struct{} + lastChecked time.Time + dnsConfig atomic.Pointer[dnsConfig] +} + +var resolvConf resolverConfig + +func getSystemDNSConfig() *dnsConfig { + resolvConf.tryUpdate("/etc/resolv.conf") + return resolvConf.dnsConfig.Load() +} + +func (conf *resolverConfig) init() { + conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf")) + conf.lastChecked = time.Now() + conf.ch = make(chan struct{}, 1) +} + +func (conf *resolverConfig) tryUpdate(name string) { + conf.initOnce.Do(conf.init) + + if conf.dnsConfig.Load().noReload { + return + } + if !conf.tryAcquireSema() { + return + } + defer conf.releaseSema() + + now := time.Now() + if conf.lastChecked.After(now.Add(-5 * time.Second)) { + return + } + conf.lastChecked = now + if runtime.GOOS != "windows" { + var mtime time.Time + if fi, err := os.Stat(name); err == nil { + mtime = fi.ModTime() + } + if mtime.Equal(conf.dnsConfig.Load().mtime) { + return + } + } + dnsConf := dnsReadConfig(name) + conf.dnsConfig.Store(dnsConf) +} + +func (conf *resolverConfig) tryAcquireSema() bool { + select { + case conf.ch <- struct{}{}: + return true + default: + return false + } +} + +func (conf *resolverConfig) releaseSema() { + <-conf.ch +} + +type dnsConfig struct { + servers []string + search []string + ndots int + timeout time.Duration + attempts int + rotate bool + unknownOpt bool + lookup []string + err error + mtime time.Time + soffset uint32 + singleRequest bool + useTCP bool + trustAD bool + noReload bool +} + +func (c *dnsConfig) serverOffset() uint32 { + if c.rotate { + return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start + } + return 0 +} + +func (conf *dnsConfig) nameList(name string) []string { + l := len(name) + rooted := l > 0 && name[l-1] == '.' + if l > 254 || l == 254 && !rooted { + return nil + } + + if rooted { + if avoidDNS(name) { + return nil + } + return []string{name} + } + + hasNdots := strings.Count(name, ".") >= conf.ndots + name += "." + // l++ + + names := make([]string, 0, 1+len(conf.search)) + if hasNdots && !avoidDNS(name) { + names = append(names, name) + } + for _, suffix := range conf.search { + fqdn := name + suffix + if !avoidDNS(fqdn) && len(fqdn) <= 254 { + names = append(names, fqdn) + } + } + if !hasNdots && !avoidDNS(name) { + names = append(names, name) + } + return names +} + +func avoidDNS(name string) bool { + if name == "" { + return true + } + if name[len(name)-1] == '.' { + name = name[:len(name)-1] + } + return strings.HasSuffix(name, ".onion") +} diff --git a/dns/transport/local/resolv_unix.go b/dns/transport/local/resolv_unix.go new file mode 100644 index 00000000..6594ae41 --- /dev/null +++ b/dns/transport/local/resolv_unix.go @@ -0,0 +1,175 @@ +//go:build !windows + +package local + +import ( + "bufio" + "net" + "net/netip" + "os" + "strings" + "time" + _ "unsafe" +) + +func dnsReadConfig(name string) *dnsConfig { + conf := &dnsConfig{ + ndots: 1, + timeout: 5 * time.Second, + attempts: 2, + } + file, err := os.Open(name) + if err != nil { + conf.servers = defaultNS + conf.search = dnsDefaultSearch() + conf.err = err + return conf + } + defer file.Close() + fi, err := file.Stat() + if err == nil { + conf.mtime = fi.ModTime() + } else { + conf.servers = defaultNS + conf.search = dnsDefaultSearch() + conf.err = err + return conf + } + reader := bufio.NewReader(file) + var ( + prefix []byte + line []byte + isPrefix bool + ) + for { + line, isPrefix, err = reader.ReadLine() + if err != nil { + break + } + if isPrefix { + prefix = append(prefix, line...) + continue + } else if len(prefix) > 0 { + line = append(prefix, line...) + prefix = nil + } + if len(line) > 0 && (line[0] == ';' || line[0] == '#') { + continue + } + f := strings.Fields(string(line)) + if len(f) < 1 { + continue + } + switch f[0] { + case "nameserver": + if len(f) > 1 && len(conf.servers) < 3 { + if _, err := netip.ParseAddr(f[1]); err == nil { + conf.servers = append(conf.servers, net.JoinHostPort(f[1], "53")) + } + } + case "domain": + if len(f) > 1 { + conf.search = []string{ensureRooted(f[1])} + } + + case "search": + conf.search = make([]string, 0, len(f)-1) + for i := 1; i < len(f); i++ { + name := ensureRooted(f[i]) + if name == "." { + continue + } + conf.search = append(conf.search, name) + } + + case "options": + for _, s := range f[1:] { + switch { + case strings.HasPrefix(s, "ndots:"): + n, _, _ := dtoi(s[6:]) + if n < 0 { + n = 0 + } else if n > 15 { + n = 15 + } + conf.ndots = n + case strings.HasPrefix(s, "timeout:"): + n, _, _ := dtoi(s[8:]) + if n < 1 { + n = 1 + } + conf.timeout = time.Duration(n) * time.Second + case strings.HasPrefix(s, "attempts:"): + n, _, _ := dtoi(s[9:]) + if n < 1 { + n = 1 + } + conf.attempts = n + case s == "rotate": + conf.rotate = true + case s == "single-request" || s == "single-request-reopen": + conf.singleRequest = true + case s == "use-vc" || s == "usevc" || s == "tcp": + conf.useTCP = true + case s == "trust-ad": + conf.trustAD = true + case s == "edns0": + case s == "no-reload": + conf.noReload = true + default: + conf.unknownOpt = true + } + } + + case "lookup": + conf.lookup = f[1:] + + default: + conf.unknownOpt = true + } + } + if len(conf.servers) == 0 { + conf.servers = defaultNS + } + if len(conf.search) == 0 { + conf.search = dnsDefaultSearch() + } + return conf +} + +//go:linkname defaultNS net.defaultNS +var defaultNS []string + +func dnsDefaultSearch() []string { + hn, err := os.Hostname() + if err != nil { + return nil + } + if i := strings.IndexRune(hn, '.'); i >= 0 && i < len(hn)-1 { + return []string{ensureRooted(hn[i+1:])} + } + return nil +} + +func ensureRooted(s string) string { + if len(s) > 0 && s[len(s)-1] == '.' { + return s + } + return s + "." +} + +const big = 0xFFFFFF + +func dtoi(s string) (n int, i int, ok bool) { + n = 0 + for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + n = n*10 + int(s[i]-'0') + if n >= big { + return big, i, false + } + } + if i == 0 { + return 0, 0, false + } + return n, i, true +} diff --git a/dns/transport/local/resolv_windows.go b/dns/transport/local/resolv_windows.go new file mode 100644 index 00000000..577e7a12 --- /dev/null +++ b/dns/transport/local/resolv_windows.go @@ -0,0 +1,100 @@ +package local + +import ( + "net" + "net/netip" + "os" + "syscall" + "time" + "unsafe" + + "golang.org/x/sys/windows" +) + +func dnsReadConfig(_ string) *dnsConfig { + conf := &dnsConfig{ + ndots: 1, + timeout: 5 * time.Second, + attempts: 2, + } + defer func() { + if len(conf.servers) == 0 { + conf.servers = defaultNS + } + }() + aas, err := adapterAddresses() + if err != nil { + return nil + } + + for _, aa := range aas { + // Only take interfaces whose OperStatus is IfOperStatusUp(0x01) into DNS configs. + if aa.OperStatus != windows.IfOperStatusUp { + continue + } + + // Only take interfaces which have at least one gateway + if aa.FirstGatewayAddress == nil { + continue + } + + for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next { + sa, err := dns.Address.Sockaddr.Sockaddr() + if err != nil { + continue + } + var ip netip.Addr + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + ip = netip.AddrFrom4([4]byte{sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]}) + case *syscall.SockaddrInet6: + var addr16 [16]byte + copy(addr16[:], sa.Addr[:]) + if addr16[0] == 0xfe && addr16[1] == 0xc0 { + // fec0/10 IPv6 addresses are site local anycast DNS + // addresses Microsoft sets by default if no other + // IPv6 DNS address is set. Site local anycast is + // deprecated since 2004, see + // https://datatracker.ietf.org/doc/html/rfc3879 + continue + } + ip = netip.AddrFrom16(addr16) + default: + // Unexpected type. + continue + } + conf.servers = append(conf.servers, net.JoinHostPort(ip.String(), "53")) + } + } + return conf +} + +//go:linkname defaultNS net.defaultNS +var defaultNS []string + +func adapterAddresses() ([]*windows.IpAdapterAddresses, error) { + var b []byte + l := uint32(15000) // recommended initial size + for { + b = make([]byte, l) + const flags = windows.GAA_FLAG_INCLUDE_PREFIX | windows.GAA_FLAG_INCLUDE_GATEWAYS + err := windows.GetAdaptersAddresses(syscall.AF_UNSPEC, flags, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l) + if err == nil { + if l == 0 { + return nil, nil + } + break + } + if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW { + return nil, os.NewSyscallError("getadaptersaddresses", err) + } + if l <= uint32(len(b)) { + return nil, os.NewSyscallError("getadaptersaddresses", err) + } + } + var aas []*windows.IpAdapterAddresses + for aa := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); aa != nil; aa = aa.Next { + aas = append(aas, aa) + } + return aas, nil +} diff --git a/dns/transport/predefined.go b/dns/transport/predefined.go new file mode 100644 index 00000000..3f112886 --- /dev/null +++ b/dns/transport/predefined.go @@ -0,0 +1,83 @@ +package transport + +import ( + "context" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + + mDNS "github.com/miekg/dns" +) + +var _ adapter.DNSTransport = (*PredefinedTransport)(nil) + +func RegisterPredefined(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.PredefinedDNSServerOptions](registry, C.DNSTypePreDefined, NewPredefined) +} + +type PredefinedTransport struct { + dns.TransportAdapter + responses []*predefinedResponse +} + +type predefinedResponse struct { + questions []mDNS.Question + answer *mDNS.Msg +} + +func NewPredefined(ctx context.Context, logger log.ContextLogger, tag string, options option.PredefinedDNSServerOptions) (adapter.DNSTransport, error) { + var responses []*predefinedResponse + for _, response := range options.Responses { + questions, msg, err := response.Build() + if err != nil { + return nil, err + } + responses = append(responses, &predefinedResponse{ + questions: questions, + answer: msg, + }) + } + if len(responses) == 0 { + return nil, E.New("empty predefined responses") + } + return &PredefinedTransport{ + TransportAdapter: dns.NewTransportAdapter(C.DNSTypePreDefined, tag, nil), + responses: responses, + }, nil +} + +func (t *PredefinedTransport) Reset() { +} + +func (t *PredefinedTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + for _, response := range t.responses { + for _, question := range response.questions { + if func() bool { + if question.Name == "" && question.Qtype == mDNS.TypeNone { + return true + } else if question.Name == "" { + return common.Any(message.Question, func(it mDNS.Question) bool { + return it.Qtype == question.Qtype + }) + } else if question.Qtype == mDNS.TypeNone { + return common.Any(message.Question, func(it mDNS.Question) bool { + return it.Name == question.Name + }) + } else { + return common.Contains(message.Question, question) + } + }() { + copyAnswer := *response.answer + copyAnswer.Id = message.Id + copyAnswer.Question = message.Question + return ©Answer, nil + } + } + } + return nil, dns.RCodeNameError +} diff --git a/dns/transport/quic/http3.go b/dns/transport/quic/http3.go new file mode 100644 index 00000000..a5181ae0 --- /dev/null +++ b/dns/transport/quic/http3.go @@ -0,0 +1,167 @@ +package quic + +import ( + "bytes" + "context" + "io" + "net" + "net/http" + "net/url" + "strconv" + + "github.com/sagernet/quic-go" + "github.com/sagernet/quic-go/http3" + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/tls" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + sHTTP "github.com/sagernet/sing/protocol/http" + + mDNS "github.com/miekg/dns" +) + +var _ adapter.DNSTransport = (*HTTP3Transport)(nil) + +func RegisterHTTP3Transport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.RemoteHTTPSDNSServerOptions](registry, C.DNSTypeHTTP3, NewHTTP3) +} + +type HTTP3Transport struct { + dns.TransportAdapter + logger logger.ContextLogger + dialer N.Dialer + destination *url.URL + headers http.Header + transport *http3.Transport +} + +func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteHTTPSDNSServerOptions) (adapter.DNSTransport, error) { + transportDialer, err := dns.NewRemoteDialer(ctx, options.RemoteDNSServerOptions) + if err != nil { + return nil, err + } + tlsOptions := common.PtrValueOrDefault(options.TLS) + tlsOptions.Enabled = true + tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions) + if err != nil { + return nil, err + } + stdConfig, err := tlsConfig.Config() + if err != nil { + return nil, err + } + headers := options.Headers.Build() + host := headers.Get("Host") + if host != "" { + headers.Del("Host") + } else { + if tlsConfig.ServerName() != "" { + host = tlsConfig.ServerName() + } else { + host = options.Server + } + } + destinationURL := url.URL{ + Scheme: "https", + Host: host, + } + if destinationURL.Host == "" { + destinationURL.Host = options.Server + } + if options.ServerPort != 0 && options.ServerPort != 443 { + destinationURL.Host = net.JoinHostPort(destinationURL.Host, strconv.Itoa(int(options.ServerPort))) + } + path := options.Path + if path == "" { + path = "/dns-query" + } + err = sHTTP.URLSetPath(&destinationURL, path) + if err != nil { + return nil, err + } + serverAddr := options.ServerOptions.Build() + if serverAddr.Port == 0 { + serverAddr.Port = 443 + } + return &HTTP3Transport{ + TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeHTTP3, tag, options.RemoteDNSServerOptions), + logger: logger, + dialer: transportDialer, + destination: &destinationURL, + headers: headers, + transport: &http3.Transport{ + Dial: func(ctx context.Context, addr string, tlsCfg *tls.STDConfig, cfg *quic.Config) (quic.EarlyConnection, error) { + destinationAddr := M.ParseSocksaddr(addr) + conn, dialErr := transportDialer.DialContext(ctx, N.NetworkUDP, destinationAddr) + if dialErr != nil { + return nil, dialErr + } + return quic.DialEarly(ctx, bufio.NewUnbindPacketConn(conn), conn.RemoteAddr(), tlsCfg, cfg) + }, + TLSClientConfig: stdConfig, + }, + }, nil +} + +func (t *HTTP3Transport) Reset() { + t.transport.Close() +} + +func (t *HTTP3Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + exMessage := *message + exMessage.Id = 0 + exMessage.Compress = true + requestBuffer := buf.NewSize(1 + message.Len()) + rawMessage, err := exMessage.PackBuffer(requestBuffer.FreeBytes()) + if err != nil { + requestBuffer.Release() + return nil, err + } + request, err := http.NewRequestWithContext(ctx, http.MethodPost, t.destination.String(), bytes.NewReader(rawMessage)) + if err != nil { + requestBuffer.Release() + return nil, err + } + request.Header = t.headers.Clone() + request.Header.Set("Content-Type", transport.MimeType) + request.Header.Set("Accept", transport.MimeType) + response, err := t.transport.RoundTrip(request) + requestBuffer.Release() + if err != nil { + return nil, err + } + defer response.Body.Close() + if response.StatusCode != http.StatusOK { + return nil, E.New("unexpected status: ", response.Status) + } + var responseMessage mDNS.Msg + if response.ContentLength > 0 { + responseBuffer := buf.NewSize(int(response.ContentLength)) + _, err = responseBuffer.ReadFullFrom(response.Body, int(response.ContentLength)) + if err != nil { + return nil, err + } + err = responseMessage.Unpack(responseBuffer.Bytes()) + responseBuffer.Release() + } else { + rawMessage, err = io.ReadAll(response.Body) + if err != nil { + return nil, err + } + err = responseMessage.Unpack(rawMessage) + } + if err != nil { + return nil, err + } + return &responseMessage, nil +} diff --git a/dns/transport/quic/quic.go b/dns/transport/quic/quic.go new file mode 100644 index 00000000..d3844c2b --- /dev/null +++ b/dns/transport/quic/quic.go @@ -0,0 +1,174 @@ +package quic + +import ( + "context" + "errors" + "sync" + + "github.com/sagernet/quic-go" + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/tls" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + sQUIC "github.com/sagernet/sing-quic" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/bufio" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + + mDNS "github.com/miekg/dns" +) + +var _ adapter.DNSTransport = (*Transport)(nil) + +func RegisterTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.RemoteTLSDNSServerOptions](registry, C.DNSTypeQUIC, NewQUIC) +} + +type Transport struct { + dns.TransportAdapter + ctx context.Context + logger logger.ContextLogger + dialer N.Dialer + serverAddr M.Socksaddr + tlsConfig tls.Config + access sync.Mutex + connection quic.EarlyConnection +} + +func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteTLSDNSServerOptions) (adapter.DNSTransport, error) { + transportDialer, err := dns.NewRemoteDialer(ctx, options.RemoteDNSServerOptions) + if err != nil { + return nil, err + } + tlsOptions := common.PtrValueOrDefault(options.TLS) + tlsOptions.Enabled = true + tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions) + if err != nil { + return nil, err + } + if len(tlsConfig.NextProtos()) == 0 { + tlsConfig.SetNextProtos([]string{"doq"}) + } + serverAddr := options.ServerOptions.Build() + if serverAddr.Port == 0 { + serverAddr.Port = 853 + } + return &Transport{ + TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeQUIC, tag, options.RemoteDNSServerOptions), + ctx: ctx, + logger: logger, + dialer: transportDialer, + serverAddr: serverAddr, + tlsConfig: tlsConfig, + }, nil +} + +func (t *Transport) Reset() { + t.access.Lock() + defer t.access.Unlock() + connection := t.connection + if connection != nil { + connection.CloseWithError(0, "") + } +} + +func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + var ( + conn quic.Connection + err error + response *mDNS.Msg + ) + for i := 0; i < 2; i++ { + conn, err = t.openConnection() + if err != nil { + return nil, err + } + response, err = t.exchange(ctx, message, conn) + if err == nil { + return response, nil + } else if !isQUICRetryError(err) { + return nil, err + } else { + conn.CloseWithError(quic.ApplicationErrorCode(0), "") + continue + } + } + return nil, err +} + +func (t *Transport) openConnection() (quic.EarlyConnection, error) { + connection := t.connection + if connection != nil && !common.Done(connection.Context()) { + return connection, nil + } + t.access.Lock() + defer t.access.Unlock() + connection = t.connection + if connection != nil && !common.Done(connection.Context()) { + return connection, nil + } + conn, err := t.dialer.DialContext(t.ctx, N.NetworkUDP, t.serverAddr) + if err != nil { + return nil, err + } + earlyConnection, err := sQUIC.DialEarly( + t.ctx, + bufio.NewUnbindPacketConn(conn), + t.serverAddr.UDPAddr(), + t.tlsConfig, + nil, + ) + if err != nil { + return nil, err + } + t.connection = earlyConnection + return earlyConnection, nil +} + +func (t *Transport) exchange(ctx context.Context, message *mDNS.Msg, conn quic.Connection) (*mDNS.Msg, error) { + stream, err := conn.OpenStreamSync(ctx) + if err != nil { + return nil, err + } + defer stream.Close() + defer stream.CancelRead(0) + err = transport.WriteMessage(stream, 0, message) + if err != nil { + return nil, err + } + return transport.ReadMessage(stream) +} + +// https://github.com/AdguardTeam/dnsproxy/blob/fd1868577652c639cce3da00e12ca548f421baf1/upstream/upstream_quic.go#L394 +func isQUICRetryError(err error) (ok bool) { + var qAppErr *quic.ApplicationError + if errors.As(err, &qAppErr) && qAppErr.ErrorCode == 0 { + return true + } + + var qIdleErr *quic.IdleTimeoutError + if errors.As(err, &qIdleErr) { + return true + } + + var resetErr *quic.StatelessResetError + if errors.As(err, &resetErr) { + return true + } + + var qTransportError *quic.TransportError + if errors.As(err, &qTransportError) && qTransportError.ErrorCode == quic.NoError { + return true + } + + if errors.Is(err, quic.Err0RTTRejected) { + return true + } + + return false +} diff --git a/dns/transport/tcp.go b/dns/transport/tcp.go new file mode 100644 index 00000000..6061585e --- /dev/null +++ b/dns/transport/tcp.go @@ -0,0 +1,99 @@ +package transport + +import ( + "context" + "encoding/binary" + "io" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + + mDNS "github.com/miekg/dns" +) + +var _ adapter.DNSTransport = (*TCPTransport)(nil) + +func RegisterTCP(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.RemoteDNSServerOptions](registry, C.DNSTypeTCP, NewTCP) +} + +type TCPTransport struct { + dns.TransportAdapter + dialer N.Dialer + serverAddr M.Socksaddr +} + +func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteDNSServerOptions) (adapter.DNSTransport, error) { + transportDialer, err := dns.NewRemoteDialer(ctx, options) + if err != nil { + return nil, err + } + serverAddr := options.ServerOptions.Build() + if serverAddr.Port == 0 { + serverAddr.Port = 53 + } + return &TCPTransport{ + TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeTCP, tag, options), + dialer: transportDialer, + serverAddr: serverAddr, + }, nil +} + +func (t *TCPTransport) Reset() { +} + +func (t *TCPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + conn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) + if err != nil { + return nil, err + } + defer conn.Close() + err = WriteMessage(conn, 0, message) + if err != nil { + return nil, err + } + return ReadMessage(conn) +} + +func ReadMessage(reader io.Reader) (*mDNS.Msg, error) { + var responseLen uint16 + err := binary.Read(reader, binary.BigEndian, &responseLen) + if err != nil { + return nil, err + } + if responseLen < 10 { + return nil, mDNS.ErrShortRead + } + buffer := buf.NewSize(int(responseLen)) + defer buffer.Release() + _, err = buffer.ReadFullFrom(reader, int(responseLen)) + if err != nil { + return nil, err + } + var message mDNS.Msg + err = message.Unpack(buffer.Bytes()) + return &message, err +} + +func WriteMessage(writer io.Writer, messageId uint16, message *mDNS.Msg) error { + requestLen := message.Len() + buffer := buf.NewSize(3 + requestLen) + defer buffer.Release() + common.Must(binary.Write(buffer, binary.BigEndian, uint16(requestLen))) + exMessage := *message + exMessage.Id = messageId + exMessage.Compress = true + rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes()) + if err != nil { + return err + } + buffer.Truncate(2 + len(rawMessage)) + return common.Error(writer.Write(buffer.Bytes())) +} diff --git a/dns/transport/tls.go b/dns/transport/tls.go new file mode 100644 index 00000000..28fa885a --- /dev/null +++ b/dns/transport/tls.go @@ -0,0 +1,115 @@ +package transport + +import ( + "context" + "sync" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/tls" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/x/list" + + mDNS "github.com/miekg/dns" +) + +var _ adapter.DNSTransport = (*TLSTransport)(nil) + +func RegisterTLS(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.RemoteTLSDNSServerOptions](registry, C.DNSTypeTLS, NewTLS) +} + +type TLSTransport struct { + dns.TransportAdapter + logger logger.ContextLogger + dialer N.Dialer + serverAddr M.Socksaddr + tlsConfig tls.Config + access sync.Mutex + connections list.List[*tlsDNSConn] +} + +type tlsDNSConn struct { + tls.Conn + queryId uint16 +} + +func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteTLSDNSServerOptions) (adapter.DNSTransport, error) { + transportDialer, err := dns.NewRemoteDialer(ctx, options.RemoteDNSServerOptions) + if err != nil { + return nil, err + } + tlsOptions := common.PtrValueOrDefault(options.TLS) + tlsOptions.Enabled = true + tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions) + if err != nil { + return nil, err + } + serverAddr := options.ServerOptions.Build() + if serverAddr.Port == 0 { + serverAddr.Port = 853 + } + return &TLSTransport{ + TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeTLS, tag, options.RemoteDNSServerOptions), + logger: logger, + dialer: transportDialer, + serverAddr: serverAddr, + tlsConfig: tlsConfig, + }, nil +} + +func (t *TLSTransport) Reset() { + t.access.Lock() + defer t.access.Unlock() + for connection := t.connections.Front(); connection != nil; connection = connection.Next() { + connection.Value.Close() + } + t.connections.Init() +} + +func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + t.access.Lock() + conn := t.connections.PopFront() + t.access.Unlock() + if conn != nil { + response, err := t.exchange(message, conn) + if err == nil { + return response, nil + } + } + tcpConn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) + if err != nil { + return nil, err + } + tlsConn, err := tls.ClientHandshake(ctx, tcpConn, t.tlsConfig) + if err != nil { + tcpConn.Close() + return nil, err + } + return t.exchange(message, &tlsDNSConn{Conn: tlsConn}) +} + +func (t *TLSTransport) exchange(message *mDNS.Msg, conn *tlsDNSConn) (*mDNS.Msg, error) { + conn.queryId++ + err := WriteMessage(conn, conn.queryId, message) + if err != nil { + conn.Close() + return nil, E.Cause(err, "write request") + } + response, err := ReadMessage(conn) + if err != nil { + conn.Close() + return nil, E.Cause(err, "read response") + } + t.access.Lock() + t.connections.PushBack(conn) + t.access.Unlock() + return response, nil +} diff --git a/dns/transport/udp.go b/dns/transport/udp.go new file mode 100644 index 00000000..5099c6f6 --- /dev/null +++ b/dns/transport/udp.go @@ -0,0 +1,223 @@ +package transport + +import ( + "context" + "net" + "os" + "sync" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + + mDNS "github.com/miekg/dns" +) + +var _ adapter.DNSTransport = (*UDPTransport)(nil) + +func RegisterUDP(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.RemoteDNSServerOptions](registry, C.DNSTypeUDP, NewUDP) +} + +type UDPTransport struct { + dns.TransportAdapter + logger logger.ContextLogger + dialer N.Dialer + serverAddr M.Socksaddr + udpSize int + tcpTransport *TCPTransport + access sync.Mutex + conn *dnsConnection + done chan struct{} +} + +func NewUDP(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteDNSServerOptions) (adapter.DNSTransport, error) { + transportDialer, err := dns.NewRemoteDialer(ctx, options) + if err != nil { + return nil, err + } + serverAddr := options.ServerOptions.Build() + if serverAddr.Port == 0 { + serverAddr.Port = 53 + } + return NewUDPRaw(logger, dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeUDP, tag, options), transportDialer, serverAddr), nil +} + +func NewUDPRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer N.Dialer, serverAddr M.Socksaddr) *UDPTransport { + return &UDPTransport{ + TransportAdapter: adapter, + logger: logger, + dialer: dialer, + serverAddr: serverAddr, + udpSize: 512, + tcpTransport: &TCPTransport{ + dialer: dialer, + serverAddr: serverAddr, + }, + done: make(chan struct{}), + } +} + +func (t *UDPTransport) Reset() { + t.access.Lock() + defer t.access.Unlock() + close(t.done) + t.done = make(chan struct{}) +} + +func (t *UDPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + response, err := t.exchange(ctx, message) + if err != nil { + return nil, err + } + if response.Truncated { + t.logger.InfoContext(ctx, "response truncated, retrying with TCP") + return t.tcpTransport.Exchange(ctx, message) + } + return response, nil +} + +func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + conn, err := t.open(ctx) + if err != nil { + return nil, err + } + if edns0Opt := message.IsEdns0(); edns0Opt != nil { + if udpSize := int(edns0Opt.UDPSize()); udpSize > t.udpSize { + t.udpSize = udpSize + } + } + buffer := buf.NewSize(1 + message.Len()) + defer buffer.Release() + exMessage := *message + exMessage.Compress = true + messageId := message.Id + callback := &dnsCallback{ + done: make(chan struct{}), + } + conn.access.Lock() + conn.queryId++ + exMessage.Id = conn.queryId + conn.callbacks[exMessage.Id] = callback + conn.access.Unlock() + defer func() { + conn.access.Lock() + delete(conn.callbacks, messageId) + conn.access.Unlock() + callback.access.Lock() + select { + case <-callback.done: + default: + close(callback.done) + } + callback.access.Unlock() + }() + rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes()) + if err != nil { + return nil, err + } + _, err = conn.Write(rawMessage) + if err != nil { + conn.Close(err) + return nil, err + } + select { + case <-callback.done: + callback.message.Id = messageId + return callback.message, nil + case <-conn.done: + return nil, conn.err + case <-t.done: + return nil, os.ErrClosed + case <-ctx.Done(): + conn.Close(ctx.Err()) + return nil, ctx.Err() + } +} + +func (t *UDPTransport) open(ctx context.Context) (*dnsConnection, error) { + t.access.Lock() + defer t.access.Unlock() + if t.conn != nil { + select { + case <-t.conn.done: + default: + return t.conn, nil + } + } + conn, err := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr) + if err != nil { + return nil, err + } + dnsConn := &dnsConnection{ + Conn: conn, + done: make(chan struct{}), + callbacks: make(map[uint16]*dnsCallback), + } + go t.recvLoop(dnsConn) + t.conn = dnsConn + return dnsConn, nil +} + +func (t *UDPTransport) recvLoop(conn *dnsConnection) { + for { + buffer := buf.NewSize(t.udpSize) + _, err := buffer.ReadOnceFrom(conn) + if err != nil { + buffer.Release() + conn.Close(err) + return + } + var message mDNS.Msg + err = message.Unpack(buffer.Bytes()) + buffer.Release() + if err != nil { + conn.Close(err) + return + } + conn.access.RLock() + callback, loaded := conn.callbacks[message.Id] + conn.access.RUnlock() + if !loaded { + continue + } + callback.access.Lock() + select { + case <-callback.done: + default: + callback.message = &message + close(callback.done) + } + callback.access.Unlock() + } +} + +type dnsConnection struct { + net.Conn + access sync.RWMutex + done chan struct{} + closeOnce sync.Once + err error + queryId uint16 + callbacks map[uint16]*dnsCallback +} + +func (c *dnsConnection) Close(err error) { + c.closeOnce.Do(func() { + close(c.done) + c.err = err + }) + c.Conn.Close() +} + +type dnsCallback struct { + access sync.Mutex + message *mDNS.Msg + done chan struct{} +} diff --git a/dns/transport_adapter.go b/dns/transport_adapter.go new file mode 100644 index 00000000..02c84621 --- /dev/null +++ b/dns/transport_adapter.go @@ -0,0 +1,70 @@ +package dns + +import ( + "net/netip" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" +) + +var _ adapter.LegacyDNSTransport = (*TransportAdapter)(nil) + +type TransportAdapter struct { + transportType string + transportTag string + dependencies []string + strategy C.DomainStrategy + clientSubnet netip.Prefix +} + +func NewTransportAdapter(transportType string, transportTag string, dependencies []string) TransportAdapter { + return TransportAdapter{ + transportType: transportType, + transportTag: transportTag, + dependencies: dependencies, + } +} + +func NewTransportAdapterWithLocalOptions(transportType string, transportTag string, localOptions option.LocalDNSServerOptions) TransportAdapter { + return TransportAdapter{ + transportType: transportType, + transportTag: transportTag, + strategy: C.DomainStrategy(localOptions.LegacyStrategy), + clientSubnet: localOptions.LegacyClientSubnet, + } +} + +func NewTransportAdapterWithRemoteOptions(transportType string, transportTag string, remoteOptions option.RemoteDNSServerOptions) TransportAdapter { + var dependencies []string + if remoteOptions.AddressResolver != "" { + dependencies = []string{remoteOptions.AddressResolver} + } + return TransportAdapter{ + transportType: transportType, + transportTag: transportTag, + dependencies: dependencies, + strategy: C.DomainStrategy(remoteOptions.LegacyStrategy), + clientSubnet: remoteOptions.LegacyClientSubnet, + } +} + +func (a *TransportAdapter) Type() string { + return a.transportType +} + +func (a *TransportAdapter) Tag() string { + return a.transportTag +} + +func (a *TransportAdapter) Dependencies() []string { + return a.dependencies +} + +func (a *TransportAdapter) LegacyStrategy() C.DomainStrategy { + return a.strategy +} + +func (a *TransportAdapter) LegacyClientSubnet() netip.Prefix { + return a.clientSubnet +} diff --git a/dns/transport_dialer.go b/dns/transport_dialer.go new file mode 100644 index 00000000..14e1188d --- /dev/null +++ b/dns/transport_dialer.go @@ -0,0 +1,93 @@ +package dns + +import ( + "context" + "net" + "time" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/dialer" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" +) + +func NewLocalDialer(ctx context.Context, options option.LocalDNSServerOptions) (N.Dialer, error) { + if options.LegacyDefaultDialer { + return dialer.NewDefaultOutbound(ctx), nil + } else { + return dialer.New(ctx, options.DialerOptions) + } +} + +func NewRemoteDialer(ctx context.Context, options option.RemoteDNSServerOptions) (N.Dialer, error) { + transportDialer, err := NewLocalDialer(ctx, options.LocalDNSServerOptions) + if err != nil { + return nil, err + } + if options.AddressResolver != "" { + transport := service.FromContext[adapter.DNSTransportManager](ctx) + resolverTransport, loaded := transport.Transport(options.AddressResolver) + if !loaded { + return nil, E.New("address resolver not found: ", options.AddressResolver) + } + transportDialer = NewTransportDialer(transportDialer, service.FromContext[adapter.DNSRouter](ctx), resolverTransport, C.DomainStrategy(options.AddressStrategy), time.Duration(options.AddressFallbackDelay)) + } else if M.IsDomainName(options.Server) { + return nil, E.New("missing address resolver for server: ", options.Server) + } + return transportDialer, nil +} + +type TransportDialer struct { + dialer N.Dialer + dnsRouter adapter.DNSRouter + transport adapter.DNSTransport + strategy C.DomainStrategy + fallbackDelay time.Duration +} + +func NewTransportDialer(dialer N.Dialer, dnsRouter adapter.DNSRouter, transport adapter.DNSTransport, strategy C.DomainStrategy, fallbackDelay time.Duration) *TransportDialer { + return &TransportDialer{ + dialer, + dnsRouter, + transport, + strategy, + fallbackDelay, + } +} + +func (d *TransportDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + if destination.IsIP() { + return d.dialer.DialContext(ctx, network, destination) + } + addresses, err := d.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{ + Transport: d.transport, + Strategy: d.strategy, + }) + if err != nil { + return nil, err + } + return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, d.fallbackDelay) +} + +func (d *TransportDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + if destination.IsIP() { + return d.dialer.ListenPacket(ctx, destination) + } + addresses, err := d.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{ + Transport: d.transport, + Strategy: d.strategy, + }) + if err != nil { + return nil, err + } + conn, _, err := N.ListenSerial(ctx, d.dialer, destination, addresses) + return conn, err +} + +func (d *TransportDialer) Upstream() any { + return d.dialer +} diff --git a/dns/transport_manager.go b/dns/transport_manager.go new file mode 100644 index 00000000..4497923b --- /dev/null +++ b/dns/transport_manager.go @@ -0,0 +1,288 @@ +package dns + +import ( + "context" + "io" + "os" + "strings" + "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.DNSTransportManager = (*TransportManager)(nil) + +type TransportManager struct { + logger log.ContextLogger + registry adapter.DNSTransportRegistry + outbound adapter.OutboundManager + defaultTag string + access sync.RWMutex + started bool + stage adapter.StartStage + transports []adapter.DNSTransport + transportByTag map[string]adapter.DNSTransport + dependByTag map[string][]string + defaultTransport adapter.DNSTransport + defaultTransportFallback adapter.DNSTransport + fakeIPTransport adapter.FakeIPTransport +} + +func NewTransportManager(logger logger.ContextLogger, registry adapter.DNSTransportRegistry, outbound adapter.OutboundManager, defaultTag string) *TransportManager { + return &TransportManager{ + logger: logger, + registry: registry, + outbound: outbound, + defaultTag: defaultTag, + transportByTag: make(map[string]adapter.DNSTransport), + dependByTag: make(map[string][]string), + } +} + +func (m *TransportManager) Initialize(defaultTransportFallback adapter.DNSTransport) { + m.defaultTransportFallback = defaultTransportFallback +} + +func (m *TransportManager) Start(stage adapter.StartStage) error { + m.access.Lock() + if m.started && m.stage >= stage { + panic("already started") + } + m.started = true + m.stage = stage + outbounds := m.transports + m.access.Unlock() + if stage == adapter.StartStateStart { + return m.startTransports(m.transports) + } else { + for _, outbound := range outbounds { + err := adapter.LegacyStart(outbound, stage) + if err != nil { + return E.Cause(err, stage, " dns/", outbound.Type(), "[", outbound.Tag(), "]") + } + } + } + return nil +} + +func (m *TransportManager) startTransports(transports []adapter.DNSTransport) error { + monitor := taskmonitor.New(m.logger, C.StartTimeout) + started := make(map[string]bool) + for { + canContinue := false + startOne: + for _, transportToStart := range transports { + transportTag := transportToStart.Tag() + if started[transportTag] { + continue + } + dependencies := transportToStart.Dependencies() + for _, dependency := range dependencies { + if !started[dependency] { + continue startOne + } + } + started[transportTag] = true + canContinue = true + if starter, isStarter := transportToStart.(adapter.Lifecycle); isStarter { + monitor.Start("start dns/", transportToStart.Type(), "[", transportTag, "]") + err := starter.Start(adapter.StartStateStart) + monitor.Finish() + if err != nil { + return E.Cause(err, "start dns/", transportToStart.Type(), "[", transportTag, "]") + } + } + } + if len(started) == len(transports) { + break + } + if canContinue { + continue + } + currentTransport := common.Find(transports, func(it adapter.DNSTransport) bool { + return !started[it.Tag()] + }) + var lintTransport func(oTree []string, oCurrent adapter.DNSTransport) error + lintTransport = func(oTree []string, oCurrent adapter.DNSTransport) error { + problemTransportTag := common.Find(oCurrent.Dependencies(), func(it string) bool { + return !started[it] + }) + if common.Contains(oTree, problemTransportTag) { + return E.New("circular server dependency: ", strings.Join(oTree, " -> "), " -> ", problemTransportTag) + } + m.access.Lock() + problemTransport := m.transportByTag[problemTransportTag] + m.access.Unlock() + if problemTransport == nil { + return E.New("dependency[", problemTransportTag, "] not found for server[", oCurrent.Tag(), "]") + } + return lintTransport(append(oTree, problemTransportTag), problemTransport) + } + return lintTransport([]string{currentTransport.Tag()}, currentTransport) + } + return nil +} + +func (m *TransportManager) Close() error { + monitor := taskmonitor.New(m.logger, C.StopTimeout) + m.access.Lock() + if !m.started { + m.access.Unlock() + return nil + } + m.started = false + transports := m.transports + m.transports = nil + m.access.Unlock() + var err error + for _, transport := range transports { + if closer, isCloser := transport.(io.Closer); isCloser { + monitor.Start("close server/", transport.Type(), "[", transport.Tag(), "]") + err = E.Append(err, closer.Close(), func(err error) error { + return E.Cause(err, "close server/", transport.Type(), "[", transport.Tag(), "]") + }) + monitor.Finish() + } + } + return nil +} + +func (m *TransportManager) Transports() []adapter.DNSTransport { + m.access.RLock() + defer m.access.RUnlock() + return m.transports +} + +func (m *TransportManager) Transport(tag string) (adapter.DNSTransport, bool) { + m.access.RLock() + outbound, found := m.transportByTag[tag] + m.access.RUnlock() + return outbound, found +} + +func (m *TransportManager) Default() adapter.DNSTransport { + m.access.RLock() + defer m.access.RUnlock() + if m.defaultTransport != nil { + return m.defaultTransport + } else { + return m.defaultTransportFallback + } +} + +func (m *TransportManager) FakeIP() adapter.FakeIPTransport { + m.access.RLock() + defer m.access.RUnlock() + return m.fakeIPTransport +} + +func (m *TransportManager) Remove(tag string) error { + m.access.Lock() + defer m.access.Unlock() + transport, found := m.transportByTag[tag] + if !found { + return os.ErrInvalid + } + delete(m.transportByTag, tag) + index := common.Index(m.transports, func(it adapter.DNSTransport) bool { + return it == transport + }) + if index == -1 { + panic("invalid inbound index") + } + m.transports = append(m.transports[:index], m.transports[index+1:]...) + started := m.started + if m.defaultTransport == transport { + if len(m.transports) > 0 { + nextTransport := m.transports[0] + if nextTransport.Type() != C.DNSTypeFakeIP { + return E.New("default server cannot be fakeip") + } + m.defaultTransport = nextTransport + m.logger.Info("updated default server to ", m.defaultTransport.Tag()) + } else { + m.defaultTransport = nil + } + } + dependBy := m.dependByTag[tag] + if len(dependBy) > 0 { + return E.New("server[", tag, "] is depended by ", strings.Join(dependBy, ", ")) + } + dependencies := transport.Dependencies() + for _, dependency := range dependencies { + if len(m.dependByTag[dependency]) == 1 { + delete(m.dependByTag, dependency) + } else { + m.dependByTag[dependency] = common.Filter(m.dependByTag[dependency], func(it string) bool { + return it != tag + }) + } + } + if started { + transport.Reset() + } + return nil +} + +func (m *TransportManager) Create(ctx context.Context, logger log.ContextLogger, tag string, transportType string, options any) error { + if tag == "" { + return os.ErrInvalid + } + transport, err := m.registry.CreateDNSTransport(ctx, logger, tag, transportType, options) + if err != nil { + return err + } + m.access.Lock() + defer m.access.Unlock() + if m.started { + for _, stage := range adapter.ListStartStages { + err = adapter.LegacyStart(transport, stage) + if err != nil { + return E.Cause(err, stage, " dns/", transport.Type(), "[", transport.Tag(), "]") + } + } + } + if existsTransport, loaded := m.transportByTag[tag]; loaded { + if m.started { + err = common.Close(existsTransport) + if err != nil { + return E.Cause(err, "close dns/", existsTransport.Type(), "[", existsTransport.Tag(), "]") + } + } + existsIndex := common.Index(m.transports, func(it adapter.DNSTransport) bool { + return it == existsTransport + }) + if existsIndex == -1 { + panic("invalid inbound index") + } + m.transports = append(m.transports[:existsIndex], m.transports[existsIndex+1:]...) + } + m.transports = append(m.transports, transport) + m.transportByTag[tag] = transport + dependencies := transport.Dependencies() + for _, dependency := range dependencies { + m.dependByTag[dependency] = append(m.dependByTag[dependency], tag) + } + if tag == m.defaultTag || (m.defaultTag == "" && m.defaultTransport == nil) { + if transport.Type() == C.DNSTypeFakeIP { + return E.New("default server cannot be fakeip") + } + m.defaultTransport = transport + if m.started { + m.logger.Info("updated default server to ", transport.Tag()) + } + } + if transport.Type() == C.DNSTypeFakeIP { + if m.fakeIPTransport != nil { + return E.New("multiple fakeip server are not supported") + } + m.fakeIPTransport = transport.(adapter.FakeIPTransport) + } + return nil +} diff --git a/dns/transport_registry.go b/dns/transport_registry.go new file mode 100644 index 00000000..d838158b --- /dev/null +++ b/dns/transport_registry.go @@ -0,0 +1,72 @@ +package dns + +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 TransportConstructorFunc[T any] func(ctx context.Context, logger log.ContextLogger, tag string, options T) (adapter.DNSTransport, error) + +func RegisterTransport[Options any](registry *TransportRegistry, transportType string, constructor TransportConstructorFunc[Options]) { + registry.register(transportType, func() any { + return new(Options) + }, func(ctx context.Context, logger log.ContextLogger, tag string, rawOptions any) (adapter.DNSTransport, error) { + var options *Options + if rawOptions != nil { + options = rawOptions.(*Options) + } + return constructor(ctx, logger, tag, common.PtrValueOrDefault(options)) + }) +} + +var _ adapter.DNSTransportRegistry = (*TransportRegistry)(nil) + +type ( + optionsConstructorFunc func() any + constructorFunc func(ctx context.Context, logger log.ContextLogger, tag string, options any) (adapter.DNSTransport, error) +) + +type TransportRegistry struct { + access sync.Mutex + optionsType map[string]optionsConstructorFunc + constructors map[string]constructorFunc +} + +func NewTransportRegistry() *TransportRegistry { + return &TransportRegistry{ + optionsType: make(map[string]optionsConstructorFunc), + constructors: make(map[string]constructorFunc), + } +} + +func (r *TransportRegistry) CreateOptions(transportType string) (any, bool) { + r.access.Lock() + defer r.access.Unlock() + optionsConstructor, loaded := r.optionsType[transportType] + if !loaded { + return nil, false + } + return optionsConstructor(), true +} + +func (r *TransportRegistry) CreateDNSTransport(ctx context.Context, logger log.ContextLogger, tag string, transportType string, options any) (adapter.DNSTransport, error) { + r.access.Lock() + defer r.access.Unlock() + constructor, loaded := r.constructors[transportType] + if !loaded { + return nil, E.New("transport type not found: " + transportType) + } + return constructor(ctx, logger, tag, options) +} + +func (r *TransportRegistry) register(transportType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) { + r.access.Lock() + defer r.access.Unlock() + r.optionsType[transportType] = optionsConstructor + r.constructors[transportType] = constructor +} diff --git a/experimental/clashapi/dns.go b/experimental/clashapi/dns.go index 2a21a7c1..4f850f82 100644 --- a/experimental/clashapi/dns.go +++ b/experimental/clashapi/dns.go @@ -13,13 +13,13 @@ import ( "github.com/miekg/dns" ) -func dnsRouter(router adapter.Router) http.Handler { +func dnsRouter(router adapter.DNSRouter) http.Handler { r := chi.NewRouter() r.Get("/query", queryDNS(router)) return r } -func queryDNS(router adapter.Router) func(w http.ResponseWriter, r *http.Request) { +func queryDNS(router adapter.DNSRouter) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { name := r.URL.Query().Get("name") qTypeStr := r.URL.Query().Get("type") @@ -39,7 +39,7 @@ func queryDNS(router adapter.Router) func(w http.ResponseWriter, r *http.Request msg := dns.Msg{} msg.SetQuestion(dns.Fqdn(name), qType) - resp, err := router.Exchange(ctx, &msg) + resp, err := router.Exchange(ctx, &msg, adapter.DNSQueryOptions{}) if err != nil { render.Status(r, http.StatusInternalServerError) render.JSON(w, r, newError(err.Error())) diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index efe2ce36..d01e180a 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -42,6 +42,7 @@ var _ adapter.ClashServer = (*Server)(nil) type Server struct { ctx context.Context router adapter.Router + dnsRouter adapter.DNSRouter outbound adapter.OutboundManager endpoint adapter.EndpointManager logger log.Logger @@ -62,11 +63,12 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op trafficManager := trafficontrol.NewManager() chiRouter := chi.NewRouter() s := &Server{ - ctx: ctx, - router: service.FromContext[adapter.Router](ctx), - outbound: service.FromContext[adapter.OutboundManager](ctx), - endpoint: service.FromContext[adapter.EndpointManager](ctx), - logger: logFactory.NewLogger("clash-api"), + ctx: ctx, + router: service.FromContext[adapter.Router](ctx), + dnsRouter: service.FromContext[adapter.DNSRouter](ctx), + outbound: service.FromContext[adapter.OutboundManager](ctx), + endpoint: service.FromContext[adapter.EndpointManager](ctx), + logger: logFactory.NewLogger("clash-api"), httpServer: &http.Server{ Addr: options.ExternalController, Handler: chiRouter, @@ -121,7 +123,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op r.Mount("/script", scriptRouter()) r.Mount("/profile", profileRouter()) r.Mount("/cache", cacheRouter(ctx)) - r.Mount("/dns", dnsRouter(s.router)) + r.Mount("/dns", dnsRouter(s.dnsRouter)) s.setupMetaAPI(r) }) @@ -221,7 +223,7 @@ func (s *Server) SetMode(newMode string) { default: } } - s.router.ClearDNSCache() + s.dnsRouter.ClearCache() cacheFile := service.FromContext[adapter.CacheFile](s.ctx) if cacheFile != nil { err := cacheFile.StoreMode(newMode) diff --git a/experimental/deprecated/constants.go b/experimental/deprecated/constants.go index 68aa9aca..bf648365 100644 --- a/experimental/deprecated/constants.go +++ b/experimental/deprecated/constants.go @@ -146,6 +146,21 @@ var OptionTUNGSO = Note{ EnvName: "TUN_GSO", } +var OptionLegacyDNSTransport = Note{ + Name: "legacy-dns-transport", + Description: "legacy DNS transport", + DeprecatedVersion: "1.12.0", + ScheduledVersion: "1.14.0", + EnvName: "LEGACY_DNS_TRANSPORT", +} + +var OptionLegacyDNSFakeIPOptions = Note{ + Name: "legacy-dns-fakeip-options", + Description: "legacy DNS fakeip options", + DeprecatedVersion: "1.12.0", + ScheduledVersion: "1.14.0", +} + var Options = []Note{ OptionBadMatchSource, OptionGEOIP, diff --git a/experimental/libbox/config.go b/experimental/libbox/config.go index 159fd8f6..603d8b6d 100644 --- a/experimental/libbox/config.go +++ b/experimental/libbox/config.go @@ -9,8 +9,11 @@ import ( "github.com/sagernet/sing-box" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/process" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/include" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common/control" @@ -21,6 +24,18 @@ import ( "github.com/sagernet/sing/service" ) +func BaseContext(platformInterface PlatformInterface) context.Context { + dnsRegistry := include.DNSTransportRegistry() + if platformInterface != nil { + if localTransport := platformInterface.LocalDNSTransport(); localTransport != nil { + dns.RegisterTransport[option.LocalDNSServerOptions](dnsRegistry, C.DNSTypeLocal, func(ctx context.Context, logger log.ContextLogger, tag string, options option.LocalDNSServerOptions) (adapter.DNSTransport, error) { + return newPlatformTransport(localTransport, tag, options), nil + }) + } + } + return box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry) +} + func parseConfig(ctx context.Context, configContent string) (option.Options, error) { options, err := json.UnmarshalExtendedContext[option.Options](ctx, []byte(configContent)) if err != nil { @@ -30,7 +45,7 @@ func parseConfig(ctx context.Context, configContent string) (option.Options, err } func CheckConfig(configContent string) error { - ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry()) + ctx := BaseContext(nil) options, err := parseConfig(ctx, configContent) if err != nil { return err @@ -131,7 +146,7 @@ func (s *platformInterfaceStub) SendNotification(notification *platform.Notifica } func FormatConfig(configContent string) (*StringBox, error) { - options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry()), configContent) + options, err := parseConfig(BaseContext(nil), configContent) if err != nil { return nil, err } diff --git a/experimental/libbox/dns.go b/experimental/libbox/dns.go index a46d9b42..a7ccd2a2 100644 --- a/experimental/libbox/dns.go +++ b/experimental/libbox/dns.go @@ -6,7 +6,10 @@ import ( "strings" "syscall" - "github.com/sagernet/sing-dns" + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" @@ -21,118 +24,80 @@ type LocalDNSTransport interface { Exchange(ctx *ExchangeContext, message []byte) error } -func RegisterLocalDNSTransport(transport LocalDNSTransport) { - if transport == nil { - dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) { - return dns.NewLocalTransport(options), nil - }) - } else { - dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) { - return &platformLocalDNSTransport{ - iif: transport, - }, nil - }) - } -} +var _ adapter.DNSTransport = (*platformTransport)(nil) -var _ dns.Transport = (*platformLocalDNSTransport)(nil) - -type platformLocalDNSTransport struct { +type platformTransport struct { + dns.TransportAdapter iif LocalDNSTransport } -func (p *platformLocalDNSTransport) Name() string { - return "local" -} - -func (p *platformLocalDNSTransport) Start() error { - return nil -} - -func (p *platformLocalDNSTransport) Reset() { -} - -func (p *platformLocalDNSTransport) Close() error { - return nil -} - -func (p *platformLocalDNSTransport) Raw() bool { - return p.iif.Raw() -} - -func (p *platformLocalDNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { - messageBytes, err := message.Pack() - if err != nil { - return nil, err +func newPlatformTransport(iif LocalDNSTransport, tag string, options option.LocalDNSServerOptions) *platformTransport { + return &platformTransport{ + TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options), + iif: iif, } +} + +func (p *platformTransport) Reset() { +} + +func (p *platformTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { response := &ExchangeContext{ context: ctx, } - var responseMessage *mDNS.Msg - var group task.Group - group.Append0(func(ctx context.Context) error { - err = p.iif.Exchange(response, messageBytes) + if p.iif.Raw() { + messageBytes, err := message.Pack() if err != nil { - return err + return nil, err } - if response.error != nil { - return response.error - } - responseMessage = &response.message - return nil - }) - err = group.Run(ctx) - if err != nil { - return nil, err - } - return responseMessage, nil -} - -func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { - var network string - switch strategy { - case dns.DomainStrategyUseIPv4: - network = "ip4" - case dns.DomainStrategyPreferIPv6: - network = "ip6" - default: - network = "ip" - } - response := &ExchangeContext{ - context: ctx, - } - var responseAddr []netip.Addr - var group task.Group - group.Append0(func(ctx context.Context) error { - err := p.iif.Lookup(response, network, domain) + var responseMessage *mDNS.Msg + var group task.Group + group.Append0(func(ctx context.Context) error { + err = p.iif.Exchange(response, messageBytes) + if err != nil { + return err + } + if response.error != nil { + return response.error + } + responseMessage = &response.message + return nil + }) + err = group.Run(ctx) if err != nil { - return err + return nil, err } - if response.error != nil { - return response.error - } - switch strategy { - case dns.DomainStrategyUseIPv4: - responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool { - return it.Is4() - }) - case dns.DomainStrategyPreferIPv6: - responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool { - return it.Is6() - }) + return responseMessage, nil + } else { + question := message.Question[0] + var network string + switch question.Qtype { + case mDNS.TypeA: + network = "ip4" + case mDNS.TypeAAAA: + network = "ip6" default: - responseAddr = response.addresses + return nil, E.New("only IP queries are supported by current version of Android") } - /*if len(responseAddr) == 0 { - response.error = dns.RCodeSuccess - }*/ - return nil - }) - err := group.Run(ctx) - if err != nil { - return nil, err + var responseAddrs []netip.Addr + var group task.Group + group.Append0(func(ctx context.Context) error { + err := p.iif.Lookup(response, network, question.Name) + if err != nil { + return err + } + if response.error != nil { + return response.error + } + responseAddrs = response.addresses + return nil + }) + err := group.Run(ctx) + if err != nil { + return nil, err + } + return dns.FixedResponse(message.Id, question, responseAddrs, C.DefaultDNSTTL), nil } - return responseAddr, nil } type Func interface { diff --git a/experimental/libbox/platform.go b/experimental/libbox/platform.go index d5951cd3..f0590367 100644 --- a/experimental/libbox/platform.go +++ b/experimental/libbox/platform.go @@ -6,6 +6,7 @@ import ( ) type PlatformInterface interface { + LocalDNSTransport() LocalDNSTransport UsePlatformAutoDetectInterfaceControl() bool AutoDetectInterfaceControl(fd int32) error OpenTun(options TunOptions) (int32, error) diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index 16c04a1f..bd891f60 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -18,7 +18,6 @@ import ( "github.com/sagernet/sing-box/experimental/deprecated" "github.com/sagernet/sing-box/experimental/libbox/internal/procfs" "github.com/sagernet/sing-box/experimental/libbox/platform" - "github.com/sagernet/sing-box/include" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-tun" @@ -44,7 +43,7 @@ type BoxService struct { } func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) { - ctx := box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry()) + ctx := BaseContext(platformInterface) ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID) service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager)) options, err := parseConfig(ctx, configContent) @@ -192,6 +191,9 @@ func (w *platformInterfaceWrapper) Interfaces() ([]adapter.NetworkInterface, err continue } w.defaultInterfaceAccess.Lock() + // (GOOS=windows) SA4006: this value of `isDefault` is never used + // Why not used? + //nolint:staticcheck isDefault := w.defaultInterface != nil && int(netInterface.Index) == w.defaultInterface.Index w.defaultInterfaceAccess.Unlock() interfaces = append(interfaces, adapter.NetworkInterface{ diff --git a/go.mod b/go.mod index 13ac86fd..0572fe40 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,6 @@ require ( github.com/sagernet/quic-go v0.49.0-beta.1 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/sing v0.6.7 - github.com/sagernet/sing-dns v0.4.2 github.com/sagernet/sing-mux v0.3.1 github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 github.com/sagernet/sing-shadowsocks v0.2.7 diff --git a/go.sum b/go.sum index 755be1c0..9725f6be 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,6 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4Wk github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing v0.6.7 h1:NIWBLZ9AUWDXAQBKGleKwsitbQrI9M0nqoheXhUKnrI= github.com/sagernet/sing v0.6.7/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing-dns v0.4.2 h1:cWe2XPUBFLep2j9kJV4Epg3bctGhMvrrl/sWi9Wszfg= -github.com/sagernet/sing-dns v0.4.2/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8= github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI= github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78= github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 h1:iwpCX6H3nZEOGUGwx0q5azcgYOA9f6v9YssihXoRKHk= diff --git a/include/dhcp.go b/include/dhcp.go index 0e4b4ccf..8cf074be 100644 --- a/include/dhcp.go +++ b/include/dhcp.go @@ -2,4 +2,11 @@ package include -import _ "github.com/sagernet/sing-box/transport/dhcp" +import ( + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport/dhcp" +) + +func registerDHCPTransport(registry *dns.TransportRegistry) { + dhcp.RegisterTransport(registry) +} diff --git a/include/dhcp_stub.go b/include/dhcp_stub.go index 47a19d2e..272f313a 100644 --- a/include/dhcp_stub.go +++ b/include/dhcp_stub.go @@ -3,12 +3,18 @@ package include import ( - "github.com/sagernet/sing-dns" + "context" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" ) -func init() { - dns.RegisterTransport([]string{"dhcp"}, func(options dns.TransportOptions) (dns.Transport, error) { +func registerDHCPTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.DHCPDNSServerOptions](registry, C.DNSTypeDHCP, func(ctx context.Context, logger log.ContextLogger, tag string, options option.DHCPDNSServerOptions) (adapter.DNSTransport, error) { return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`) }) } diff --git a/include/quic.go b/include/quic.go index 980b4581..6a3f3017 100644 --- a/include/quic.go +++ b/include/quic.go @@ -5,12 +5,13 @@ package include import ( "github.com/sagernet/sing-box/adapter/inbound" "github.com/sagernet/sing-box/adapter/outbound" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport/quic" "github.com/sagernet/sing-box/protocol/hysteria" "github.com/sagernet/sing-box/protocol/hysteria2" _ "github.com/sagernet/sing-box/protocol/naive/quic" "github.com/sagernet/sing-box/protocol/tuic" _ "github.com/sagernet/sing-box/transport/v2rayquic" - _ "github.com/sagernet/sing-dns/quic" ) func registerQUICInbounds(registry *inbound.Registry) { @@ -24,3 +25,8 @@ func registerQUICOutbounds(registry *outbound.Registry) { tuic.RegisterOutbound(registry) hysteria2.RegisterOutbound(registry) } + +func registerQUICTransports(registry *dns.TransportRegistry) { + quic.RegisterTransport(registry) + quic.RegisterHTTP3Transport(registry) +} diff --git a/include/quic_stub.go b/include/quic_stub.go index 66c08590..c20a5114 100644 --- a/include/quic_stub.go +++ b/include/quic_stub.go @@ -13,20 +13,17 @@ import ( "github.com/sagernet/sing-box/common/listener" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/protocol/naive" "github.com/sagernet/sing-box/transport/v2ray" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) func init() { - dns.RegisterTransport([]string{"quic", "h3"}, func(options dns.TransportOptions) (dns.Transport, error) { - return nil, C.ErrQUICNotIncluded - }) v2ray.RegisterQUICConstructor( func(ctx context.Context, logger logger.ContextLogger, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { return nil, C.ErrQUICNotIncluded @@ -63,3 +60,12 @@ func registerQUICOutbounds(registry *outbound.Registry) { return nil, C.ErrQUICNotIncluded }) } + +func registerQUICTransports(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.RemoteTLSDNSServerOptions](registry, C.DNSTypeQUIC, func(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteTLSDNSServerOptions) (adapter.DNSTransport, error) { + return nil, C.ErrQUICNotIncluded + }) + dns.RegisterTransport[option.RemoteHTTPSDNSServerOptions](registry, C.DNSTypeHTTP3, func(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteHTTPSDNSServerOptions) (adapter.DNSTransport, error) { + return nil, C.ErrQUICNotIncluded + }) +} diff --git a/include/registry.go b/include/registry.go index e71ffb0c..cbf793f4 100644 --- a/include/registry.go +++ b/include/registry.go @@ -8,11 +8,16 @@ import ( "github.com/sagernet/sing-box/adapter/inbound" "github.com/sagernet/sing-box/adapter/outbound" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport" + "github.com/sagernet/sing-box/dns/transport/fakeip" + "github.com/sagernet/sing-box/dns/transport/hosts" + "github.com/sagernet/sing-box/dns/transport/local" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/protocol/block" "github.com/sagernet/sing-box/protocol/direct" - "github.com/sagernet/sing-box/protocol/dns" + protocolDNS "github.com/sagernet/sing-box/protocol/dns" "github.com/sagernet/sing-box/protocol/group" "github.com/sagernet/sing-box/protocol/http" "github.com/sagernet/sing-box/protocol/mixed" @@ -61,7 +66,7 @@ func OutboundRegistry() *outbound.Registry { direct.RegisterOutbound(registry) block.RegisterOutbound(registry) - dns.RegisterOutbound(registry) + protocolDNS.RegisterOutbound(registry) group.RegisterSelector(registry) group.RegisterURLTest(registry) @@ -91,6 +96,24 @@ func EndpointRegistry() *endpoint.Registry { return registry } +func DNSTransportRegistry() *dns.TransportRegistry { + registry := dns.NewTransportRegistry() + + transport.RegisterTCP(registry) + transport.RegisterUDP(registry) + transport.RegisterTLS(registry) + transport.RegisterHTTPS(registry) + transport.RegisterPredefined(registry) + hosts.RegisterTransport(registry) + local.RegisterTransport(registry) + fakeip.RegisterTransport(registry) + + registerQUICTransports(registry) + registerDHCPTransport(registry) + + return registry +} + func registerStubForRemovedInbounds(registry *inbound.Registry) { inbound.Register[option.ShadowsocksInboundOptions](registry, C.TypeShadowsocksR, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (adapter.Inbound, error) { return nil, E.New("ShadowsocksR is deprecated and removed in sing-box 1.6.0") diff --git a/option/dns.go b/option/dns.go index 272c5180..8c9b8bda 100644 --- a/option/dns.go +++ b/option/dns.go @@ -1,29 +1,53 @@ package option import ( + "context" "net/netip" + "net/url" + "os" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/experimental/deprecated" + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" "github.com/sagernet/sing/common/json/badoption" + M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/service" + + "github.com/miekg/dns" ) -type DNSOptions struct { - Servers []DNSServerOptions `json:"servers,omitempty"` - Rules []DNSRule `json:"rules,omitempty"` - Final string `json:"final,omitempty"` - ReverseMapping bool `json:"reverse_mapping,omitempty"` - FakeIP *DNSFakeIPOptions `json:"fakeip,omitempty"` +type RawDNSOptions struct { + Servers []NewDNSServerOptions `json:"servers,omitempty"` + Rules []DNSRule `json:"rules,omitempty"` + Final string `json:"final,omitempty"` + ReverseMapping bool `json:"reverse_mapping,omitempty"` DNSClientOptions } -type DNSServerOptions struct { - Tag string `json:"tag,omitempty"` - Address string `json:"address"` - AddressResolver string `json:"address_resolver,omitempty"` - AddressStrategy DomainStrategy `json:"address_strategy,omitempty"` - AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"` - Strategy DomainStrategy `json:"strategy,omitempty"` - Detour string `json:"detour,omitempty"` - ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` +type LegacyDNSOptions struct { + FakeIP *LegacyDNSFakeIPOptions `json:"fakeip,omitempty"` +} + +type DNSOptions struct { + RawDNSOptions + LegacyDNSOptions +} + +func (o *DNSOptions) UnmarshalJSONContext(ctx context.Context, content []byte) error { + err := json.UnmarshalContext(ctx, content, &o.LegacyDNSOptions) + if err != nil { + return err + } + if o.FakeIP != nil && o.FakeIP.Enabled { + deprecated.Report(ctx, deprecated.OptionLegacyDNSFakeIPOptions) + ctx = context.WithValue(ctx, (*LegacyDNSFakeIPOptions)(nil), o.FakeIP) + } + legacyOptions := o.LegacyDNSOptions + o.LegacyDNSOptions = LegacyDNSOptions{} + return badjson.UnmarshallExcludedContext(ctx, content, legacyOptions, &o.RawDNSOptions) } type DNSClientOptions struct { @@ -35,8 +59,261 @@ type DNSClientOptions struct { ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` } -type DNSFakeIPOptions struct { - Enabled bool `json:"enabled,omitempty"` - Inet4Range *netip.Prefix `json:"inet4_range,omitempty"` - Inet6Range *netip.Prefix `json:"inet6_range,omitempty"` +type LegacyDNSFakeIPOptions struct { + Enabled bool `json:"enabled,omitempty"` + Inet4Range *badoption.Prefix `json:"inet4_range,omitempty"` + Inet6Range *badoption.Prefix `json:"inet6_range,omitempty"` +} + +type DNSTransportOptionsRegistry interface { + CreateOptions(transportType string) (any, bool) +} + +type _NewDNSServerOptions struct { + Type string `json:"type,omitempty"` + Tag string `json:"tag,omitempty"` + Options any `json:"-"` +} + +type NewDNSServerOptions _NewDNSServerOptions + +func (o *NewDNSServerOptions) MarshalJSONContext(ctx context.Context) ([]byte, error) { + return badjson.MarshallObjectsContext(ctx, (*_NewDNSServerOptions)(o), o.Options) +} + +func (o *NewDNSServerOptions) UnmarshalJSONContext(ctx context.Context, content []byte) error { + err := json.UnmarshalContext(ctx, content, (*_NewDNSServerOptions)(o)) + if err != nil { + return err + } + registry := service.FromContext[DNSTransportOptionsRegistry](ctx) + if registry == nil { + return E.New("missing outbound options registry in context") + } + var options any + switch o.Type { + case "", C.DNSTypeLegacy: + o.Type = C.DNSTypeLegacy + options = new(LegacyDNSServerOptions) + deprecated.Report(ctx, deprecated.OptionLegacyDNSTransport) + default: + var loaded bool + options, loaded = registry.CreateOptions(o.Type) + if !loaded { + return E.New("unknown transport type: ", o.Type) + } + } + err = badjson.UnmarshallExcludedContext(ctx, content, (*_Outbound)(o), options) + if err != nil { + return err + } + o.Options = options + if o.Type == C.DNSTypeLegacy { + err = o.Upgrade(ctx) + if err != nil { + return err + } + } + return nil +} + +func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { + if o.Type != C.DNSTypeLegacy { + return nil + } + defer func() { + encoder := json.NewEncoder(os.Stderr) + encoder.SetIndent("", " ") + encoder.Encode(o) + }() + options := o.Options.(*LegacyDNSServerOptions) + serverURL, _ := url.Parse(options.Address) + var serverType string + if serverURL.Scheme != "" { + serverType = serverURL.Scheme + } else { + switch options.Address { + case "local", "fakeip": + serverType = options.Address + default: + serverType = C.DNSTypeUDP + } + } + remoteOptions := RemoteDNSServerOptions{ + LocalDNSServerOptions: LocalDNSServerOptions{ + DialerOptions: DialerOptions{ + Detour: options.Detour, + }, + LegacyStrategy: options.Strategy, + LegacyDefaultDialer: options.Detour == "", + LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}), + }, + AddressResolver: options.AddressResolver, + AddressStrategy: options.AddressStrategy, + AddressFallbackDelay: options.AddressFallbackDelay, + } + switch serverType { + case C.DNSTypeLocal: + o.Type = C.DNSTypeLocal + o.Options = &remoteOptions.LocalDNSServerOptions + case C.DNSTypeUDP: + o.Type = C.DNSTypeUDP + o.Options = &remoteOptions + var serverAddr M.Socksaddr + if serverURL.Scheme == "" { + serverAddr = M.ParseSocksaddr(options.Address) + } else { + serverAddr = M.ParseSocksaddr(serverURL.Host) + } + if !serverAddr.IsValid() { + return E.New("invalid server address") + } + remoteOptions.Server = serverAddr.Addr.String() + if serverAddr.Port != 0 && serverAddr.Port != 53 { + remoteOptions.ServerPort = serverAddr.Port + } + remoteOptions.Server = serverAddr.AddrString() + remoteOptions.ServerPort = serverAddr.Port + case C.DNSTypeTCP: + o.Type = C.DNSTypeTCP + o.Options = &remoteOptions + serverAddr := M.ParseSocksaddr(serverURL.Host) + if !serverAddr.IsValid() { + return E.New("invalid server address") + } + remoteOptions.Server = serverAddr.Addr.String() + if serverAddr.Port != 0 && serverAddr.Port != 53 { + remoteOptions.ServerPort = serverAddr.Port + } + remoteOptions.Server = serverAddr.AddrString() + remoteOptions.ServerPort = serverAddr.Port + case C.DNSTypeTLS, C.DNSTypeQUIC: + o.Type = serverType + serverAddr := M.ParseSocksaddr(serverURL.Host) + if !serverAddr.IsValid() { + return E.New("invalid server address") + } + remoteOptions.Server = serverAddr.Addr.String() + if serverAddr.Port != 0 && serverAddr.Port != 853 { + remoteOptions.ServerPort = serverAddr.Port + } + o.Options = &RemoteTLSDNSServerOptions{ + RemoteDNSServerOptions: remoteOptions, + } + case C.DNSTypeHTTPS, C.DNSTypeHTTP3: + o.Type = serverType + httpsOptions := RemoteHTTPSDNSServerOptions{ + RemoteTLSDNSServerOptions: RemoteTLSDNSServerOptions{ + RemoteDNSServerOptions: remoteOptions, + }, + } + o.Options = &httpsOptions + serverAddr := M.ParseSocksaddr(serverURL.Host) + if !serverAddr.IsValid() { + return E.New("invalid server address") + } + httpsOptions.Server = serverAddr.Addr.String() + if serverAddr.Port != 0 && serverAddr.Port != 443 { + httpsOptions.ServerPort = serverAddr.Port + } + if serverURL.Path != "/dns-query" { + httpsOptions.Path = serverURL.Path + } + case "rcode": + var rcode int + switch serverURL.Host { + case "success": + rcode = dns.RcodeSuccess + case "format_error": + rcode = dns.RcodeFormatError + case "server_failure": + rcode = dns.RcodeServerFailure + case "name_error": + rcode = dns.RcodeNameError + case "not_implemented": + rcode = dns.RcodeNotImplemented + case "refused": + rcode = dns.RcodeRefused + default: + return E.New("unknown rcode: ", serverURL.Host) + } + o.Type = C.DNSTypePreDefined + o.Options = &PredefinedDNSServerOptions{ + Responses: []DNSResponseOptions{ + { + RCode: common.Ptr(DNSRCode(rcode)), + }, + }, + } + case C.DNSTypeDHCP: + o.Type = C.DNSTypeDHCP + dhcpOptions := DHCPDNSServerOptions{} + if serverURL.Host != "" && serverURL.Host != "auto" { + dhcpOptions.Interface = serverURL.Host + } + o.Options = &dhcpOptions + case C.DNSTypeFakeIP: + o.Type = C.DNSTypeFakeIP + fakeipOptions := FakeIPDNSServerOptions{} + if legacyOptions, loaded := ctx.Value((*LegacyDNSFakeIPOptions)(nil)).(*LegacyDNSFakeIPOptions); loaded { + fakeipOptions.Inet4Range = legacyOptions.Inet4Range + fakeipOptions.Inet6Range = legacyOptions.Inet6Range + } + o.Options = &fakeipOptions + default: + return E.New("unsupported DNS server scheme: ", serverType) + } + return nil +} + +type LegacyDNSServerOptions struct { + Address string `json:"address"` + AddressResolver string `json:"address_resolver,omitempty"` + AddressStrategy DomainStrategy `json:"address_strategy,omitempty"` + AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"` + Strategy DomainStrategy `json:"strategy,omitempty"` + Detour string `json:"detour,omitempty"` + ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` +} + +type HostsDNSServerOptions struct { + Path badoption.Listable[string] `json:"path,omitempty"` + Predefined badjson.TypedMap[string, badoption.Listable[netip.Addr]] `json:"predefined,omitempty"` +} + +type LocalDNSServerOptions struct { + DialerOptions + LegacyStrategy DomainStrategy `json:"-"` + LegacyDefaultDialer bool `json:"-"` + LegacyClientSubnet netip.Prefix `json:"-"` +} + +type RemoteDNSServerOptions struct { + LocalDNSServerOptions + ServerOptions + AddressResolver string `json:"address_resolver,omitempty"` + AddressStrategy DomainStrategy `json:"address_strategy,omitempty"` + AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"` +} + +type RemoteTLSDNSServerOptions struct { + RemoteDNSServerOptions + OutboundTLSOptionsContainer +} + +type RemoteHTTPSDNSServerOptions struct { + RemoteTLSDNSServerOptions + Path string `json:"path,omitempty"` + Method string `json:"method,omitempty"` + Headers badoption.HTTPHeader `json:"headers,omitempty"` +} + +type FakeIPDNSServerOptions struct { + Inet4Range *badoption.Prefix `json:"inet4_range,omitempty"` + Inet6Range *badoption.Prefix `json:"inet6_range,omitempty"` +} + +type DHCPDNSServerOptions struct { + LocalDNSServerOptions + Interface string `json:"interface,omitempty"` } diff --git a/option/dns_record.go b/option/dns_record.go new file mode 100644 index 00000000..c76a76c6 --- /dev/null +++ b/option/dns_record.go @@ -0,0 +1,161 @@ +package option + +import ( + "encoding/base64" + + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badoption" + M "github.com/sagernet/sing/common/metadata" + + "github.com/miekg/dns" +) + +type PredefinedDNSServerOptions struct { + Responses []DNSResponseOptions `json:"responses,omitempty"` +} + +type DNSResponseOptions struct { + Query badoption.Listable[string] `json:"query,omitempty"` + QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` + + RCode *DNSRCode `json:"rcode,omitempty"` + Answer badoption.Listable[DNSRecordOptions] `json:"answer,omitempty"` + Ns badoption.Listable[DNSRecordOptions] `json:"ns,omitempty"` + Extra badoption.Listable[DNSRecordOptions] `json:"extra,omitempty"` +} + +type DNSRCode int + +func (r DNSRCode) MarshalJSON() ([]byte, error) { + rCodeValue, loaded := dns.RcodeToString[int(r)] + if loaded { + return json.Marshal(rCodeValue) + } + return json.Marshal(int(r)) +} + +func (r *DNSRCode) UnmarshalJSON(bytes []byte) error { + var intValue int + err := json.Unmarshal(bytes, &intValue) + if err == nil { + *r = DNSRCode(intValue) + return nil + } + var stringValue string + err = json.Unmarshal(bytes, &stringValue) + if err != nil { + return err + } + rCodeValue, loaded := dns.StringToRcode[stringValue] + if !loaded { + return E.New("unknown rcode: " + stringValue) + } + *r = DNSRCode(rCodeValue) + return nil +} + +func (r *DNSRCode) Build() int { + if r == nil { + return dns.RcodeSuccess + } + return int(*r) +} + +func (o DNSResponseOptions) Build() ([]dns.Question, *dns.Msg, error) { + var questions []dns.Question + if len(o.Query) == 0 && len(o.QueryType) == 0 { + questions = []dns.Question{{Qclass: dns.ClassINET}} + } else if len(o.Query) == 0 { + for _, queryType := range o.QueryType { + questions = append(questions, dns.Question{ + Qtype: uint16(queryType), + Qclass: dns.ClassINET, + }) + } + } else if len(o.QueryType) == 0 { + for _, domain := range o.Query { + questions = append(questions, dns.Question{ + Name: dns.Fqdn(domain), + Qclass: dns.ClassINET, + }) + } + } else { + for _, queryType := range o.QueryType { + for _, domain := range o.Query { + questions = append(questions, dns.Question{ + Name: dns.Fqdn(domain), + Qtype: uint16(queryType), + Qclass: dns.ClassINET, + }) + } + } + } + return questions, &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Response: true, + Rcode: o.RCode.Build(), + Authoritative: true, + RecursionDesired: true, + RecursionAvailable: true, + }, + Answer: common.Map(o.Answer, DNSRecordOptions.build), + Ns: common.Map(o.Ns, DNSRecordOptions.build), + Extra: common.Map(o.Extra, DNSRecordOptions.build), + }, nil +} + +type DNSRecordOptions struct { + dns.RR + fromBase64 bool +} + +func (o DNSRecordOptions) MarshalJSON() ([]byte, error) { + if o.fromBase64 { + buffer := buf.Get(dns.Len(o.RR)) + defer buf.Put(buffer) + offset, err := dns.PackRR(o.RR, buffer, 0, nil, false) + if err != nil { + return nil, err + } + return json.Marshal(base64.StdEncoding.EncodeToString(buffer[:offset])) + } + return json.Marshal(o.RR.String()) +} + +func (o *DNSRecordOptions) UnmarshalJSON(data []byte) error { + var stringValue string + err := json.Unmarshal(data, &stringValue) + if err != nil { + return err + } + binary, err := base64.StdEncoding.DecodeString(stringValue) + if err == nil { + return o.unmarshalBase64(binary) + } + record, err := dns.NewRR(stringValue) + if err != nil { + return err + } + if a, isA := record.(*dns.A); isA { + a.A = M.AddrFromIP(a.A).Unmap().AsSlice() + } + o.RR = record + return nil +} + +func (o *DNSRecordOptions) unmarshalBase64(binary []byte) error { + record, _, err := dns.UnpackRR(binary, 0) + if err != nil { + return E.New("parse binary DNS record") + } + o.RR = record + o.fromBase64 = true + return nil +} + +func (o DNSRecordOptions) build() dns.RR { + return o.RR +} diff --git a/option/rule_action.go b/option/rule_action.go index 35f334d6..a715d260 100644 --- a/option/rule_action.go +++ b/option/rule_action.go @@ -7,7 +7,6 @@ import ( "time" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-dns" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json/badjson" @@ -168,12 +167,14 @@ func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error { type DNSRouteActionOptions struct { Server string `json:"server,omitempty"` + Strategy DomainStrategy `json:"strategy,omitempty"` DisableCache bool `json:"disable_cache,omitempty"` RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` } type _DNSRouteOptionsActionOptions struct { + Strategy DomainStrategy `json:"strategy,omitempty"` DisableCache bool `json:"disable_cache,omitempty"` RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` @@ -225,7 +226,7 @@ func (d DirectActionOptions) Descriptions() []string { if d.UDPFragment != nil { descriptions = append(descriptions, "udp_fragment="+fmt.Sprint(*d.UDPFragment)) } - if d.DomainStrategy != DomainStrategy(dns.DomainStrategyAsIS) { + if d.DomainStrategy != DomainStrategy(C.DomainStrategyAsIS) { descriptions = append(descriptions, "domain_strategy="+d.DomainStrategy.String()) } if d.FallbackDelay != 0 { diff --git a/option/rule_dns.go b/option/rule_dns.go index b437eb54..9d6fb138 100644 --- a/option/rule_dns.go +++ b/option/rule_dns.go @@ -83,6 +83,7 @@ type RawDefaultDNSRule struct { GeoIP badoption.Listable[string] `json:"geoip,omitempty"` IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"` IPIsPrivate bool `json:"ip_is_private,omitempty"` + IPAcceptAny bool `json:"ip_accept_any,omitempty"` SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"` SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"` SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"` diff --git a/option/types.go b/option/types.go index 66f58ef8..fe7d4b3d 100644 --- a/option/types.go +++ b/option/types.go @@ -4,7 +4,6 @@ import ( "strings" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-dns" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/json" @@ -45,19 +44,19 @@ func (v NetworkList) Build() []string { return strings.Split(string(v), "\n") } -type DomainStrategy dns.DomainStrategy +type DomainStrategy C.DomainStrategy func (s DomainStrategy) String() string { - switch dns.DomainStrategy(s) { - case dns.DomainStrategyAsIS: + switch C.DomainStrategy(s) { + case C.DomainStrategyAsIS: return "" - case dns.DomainStrategyPreferIPv4: + case C.DomainStrategyPreferIPv4: return "prefer_ipv4" - case dns.DomainStrategyPreferIPv6: + case C.DomainStrategyPreferIPv6: return "prefer_ipv6" - case dns.DomainStrategyUseIPv4: + case C.DomainStrategyIPv4Only: return "ipv4_only" - case dns.DomainStrategyUseIPv6: + case C.DomainStrategyIPv6Only: return "ipv6_only" default: panic(E.New("unknown domain strategy: ", s)) @@ -66,17 +65,17 @@ func (s DomainStrategy) String() string { func (s DomainStrategy) MarshalJSON() ([]byte, error) { var value string - switch dns.DomainStrategy(s) { - case dns.DomainStrategyAsIS: + switch C.DomainStrategy(s) { + case C.DomainStrategyAsIS: value = "" // value = "as_is" - case dns.DomainStrategyPreferIPv4: + case C.DomainStrategyPreferIPv4: value = "prefer_ipv4" - case dns.DomainStrategyPreferIPv6: + case C.DomainStrategyPreferIPv6: value = "prefer_ipv6" - case dns.DomainStrategyUseIPv4: + case C.DomainStrategyIPv4Only: value = "ipv4_only" - case dns.DomainStrategyUseIPv6: + case C.DomainStrategyIPv6Only: value = "ipv6_only" default: return nil, E.New("unknown domain strategy: ", s) @@ -92,15 +91,15 @@ func (s *DomainStrategy) UnmarshalJSON(bytes []byte) error { } switch value { case "", "as_is": - *s = DomainStrategy(dns.DomainStrategyAsIS) + *s = DomainStrategy(C.DomainStrategyAsIS) case "prefer_ipv4": - *s = DomainStrategy(dns.DomainStrategyPreferIPv4) + *s = DomainStrategy(C.DomainStrategyPreferIPv4) case "prefer_ipv6": - *s = DomainStrategy(dns.DomainStrategyPreferIPv6) + *s = DomainStrategy(C.DomainStrategyPreferIPv6) case "ipv4_only": - *s = DomainStrategy(dns.DomainStrategyUseIPv4) + *s = DomainStrategy(C.DomainStrategyIPv4Only) case "ipv6_only": - *s = DomainStrategy(dns.DomainStrategyUseIPv6) + *s = DomainStrategy(C.DomainStrategyIPv6Only) default: return E.New("unknown domain strategy: ", value) } diff --git a/protocol/direct/outbound.go b/protocol/direct/outbound.go index aba56336..d173ec53 100644 --- a/protocol/direct/outbound.go +++ b/protocol/direct/outbound.go @@ -12,7 +12,6 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" @@ -34,7 +33,7 @@ type Outbound struct { outbound.Adapter logger logger.ContextLogger dialer dialer.ParallelInterfaceDialer - domainStrategy dns.DomainStrategy + domainStrategy C.DomainStrategy fallbackDelay time.Duration overrideOption int overrideDestination M.Socksaddr @@ -50,7 +49,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL outbound := &Outbound{ Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions), logger: logger, - domainStrategy: dns.DomainStrategy(options.DomainStrategy), + domainStrategy: C.DomainStrategy(options.DomainStrategy), fallbackDelay: time.Duration(options.FallbackDelay), dialer: outboundDialer, // loopBack: newLoopBackDetector(router), @@ -151,26 +150,26 @@ func (h *Outbound) DialParallel(ctx context.Context, network string, destination case N.NetworkUDP: h.logger.InfoContext(ctx, "outbound packet connection to ", destination) } - var domainStrategy dns.DomainStrategy - if h.domainStrategy != dns.DomainStrategyAsIS { + var domainStrategy C.DomainStrategy + if h.domainStrategy != C.DomainStrategyAsIS { domainStrategy = h.domainStrategy } else { //nolint:staticcheck - domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) + domainStrategy = C.DomainStrategy(metadata.InboundOptions.DomainStrategy) } switch domainStrategy { - case dns.DomainStrategyUseIPv4: + case C.DomainStrategyIPv4Only: destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is4) if len(destinationAddresses) == 0 { return nil, E.New("no IPv4 address available for ", destination) } - case dns.DomainStrategyUseIPv6: + case C.DomainStrategyIPv6Only: destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is6) if len(destinationAddresses) == 0 { return nil, E.New("no IPv6 address available for ", destination) } } - return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, nil, nil, nil, h.fallbackDelay) + return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == C.DomainStrategyPreferIPv6, nil, nil, nil, h.fallbackDelay) } func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) { @@ -191,26 +190,26 @@ func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, dest case N.NetworkUDP: h.logger.InfoContext(ctx, "outbound packet connection to ", destination) } - var domainStrategy dns.DomainStrategy - if h.domainStrategy != dns.DomainStrategyAsIS { + var domainStrategy C.DomainStrategy + if h.domainStrategy != C.DomainStrategyAsIS { domainStrategy = h.domainStrategy } else { //nolint:staticcheck - domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) + domainStrategy = C.DomainStrategy(metadata.InboundOptions.DomainStrategy) } switch domainStrategy { - case dns.DomainStrategyUseIPv4: + case C.DomainStrategyIPv4Only: destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is4) if len(destinationAddresses) == 0 { return nil, E.New("no IPv4 address available for ", destination) } - case dns.DomainStrategyUseIPv6: + case C.DomainStrategyIPv6Only: destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is6) if len(destinationAddresses) == 0 { return nil, E.New("no IPv6 address available for ", destination) } } - return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, networkStrategy, networkType, fallbackNetworkType, fallbackDelay) + return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == C.DomainStrategyPreferIPv6, networkStrategy, networkType, fallbackNetworkType, fallbackDelay) } func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) { diff --git a/protocol/dns/handle.go b/protocol/dns/handle.go index bc58d9e2..c4ad79d9 100644 --- a/protocol/dns/handle.go +++ b/protocol/dns/handle.go @@ -7,7 +7,7 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-dns" + "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" @@ -19,7 +19,7 @@ import ( mDNS "github.com/miekg/dns" ) -func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net.Conn, metadata adapter.InboundContext) error { +func HandleStreamDNSRequest(ctx context.Context, router adapter.DNSRouter, conn net.Conn, metadata adapter.InboundContext) error { var queryLength uint16 err := binary.Read(conn, binary.BigEndian, &queryLength) if err != nil { @@ -41,7 +41,7 @@ func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net } metadataInQuery := metadata go func() error { - response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message) + response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{}) if err != nil { conn.Close() return err @@ -61,7 +61,7 @@ func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net return nil } -func NewDNSPacketConnection(ctx context.Context, router adapter.Router, conn N.PacketConn, cachedPackets []*N.PacketBuffer, metadata adapter.InboundContext) error { +func NewDNSPacketConnection(ctx context.Context, router adapter.DNSRouter, conn N.PacketConn, cachedPackets []*N.PacketBuffer, metadata adapter.InboundContext) error { metadata.Destination = M.Socksaddr{} var reader N.PacketReader = conn var counters []N.CountFunc @@ -123,7 +123,7 @@ func NewDNSPacketConnection(ctx context.Context, router adapter.Router, conn N.P } metadataInQuery := metadata go func() error { - response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message) + response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{}) if err != nil { cancel(err) return err @@ -148,7 +148,7 @@ func NewDNSPacketConnection(ctx context.Context, router adapter.Router, conn N.P return group.Run(fastClose) } -func newDNSPacketConnection(ctx context.Context, router adapter.Router, conn N.PacketConn, readWaiter N.PacketReadWaiter, readCounters []N.CountFunc, cached []*N.PacketBuffer, metadata adapter.InboundContext) error { +func newDNSPacketConnection(ctx context.Context, router adapter.DNSRouter, conn N.PacketConn, readWaiter N.PacketReadWaiter, readCounters []N.CountFunc, cached []*N.PacketBuffer, metadata adapter.InboundContext) error { fastClose, cancel := common.ContextWithCancelCause(ctx) timeout := canceler.New(fastClose, cancel, C.DNSTimeout) var group task.Group @@ -193,7 +193,7 @@ func newDNSPacketConnection(ctx context.Context, router adapter.Router, conn N.P } metadataInQuery := metadata go func() error { - response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message) + response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{}) if err != nil { cancel(err) return err diff --git a/protocol/dns/outbound.go b/protocol/dns/outbound.go index 5f06557b..277d7454 100644 --- a/protocol/dns/outbound.go +++ b/protocol/dns/outbound.go @@ -14,6 +14,7 @@ import ( "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" ) func RegisterOutbound(registry *outbound.Registry) { @@ -22,14 +23,14 @@ func RegisterOutbound(registry *outbound.Registry) { type Outbound struct { outbound.Adapter - router adapter.Router + router adapter.DNSRouter logger logger.ContextLogger } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.StubOptions) (adapter.Outbound, error) { return &Outbound{ Adapter: outbound.NewAdapter(C.TypeDNS, tag, []string{N.NetworkTCP, N.NetworkUDP}, nil), - router: router, + router: service.FromContext[adapter.DNSRouter](ctx), logger: logger, }, nil } diff --git a/protocol/socks/outbound.go b/protocol/socks/outbound.go index 0632f082..323149e2 100644 --- a/protocol/socks/outbound.go +++ b/protocol/socks/outbound.go @@ -17,6 +17,7 @@ import ( N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/uot" "github.com/sagernet/sing/protocol/socks" + "github.com/sagernet/sing/service" ) func RegisterOutbound(registry *outbound.Registry) { @@ -27,7 +28,7 @@ var _ adapter.Outbound = (*Outbound)(nil) type Outbound struct { outbound.Adapter - router adapter.Router + dnsRouter adapter.DNSRouter logger logger.ContextLogger client *socks.Client resolve bool @@ -50,11 +51,11 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL return nil, err } outbound := &Outbound{ - Adapter: outbound.NewAdapterWithDialerOptions(C.TypeSOCKS, tag, options.Network.Build(), options.DialerOptions), - router: router, - logger: logger, - client: socks.NewClient(outboundDialer, options.ServerOptions.Build(), version, options.Username, options.Password), - resolve: version == socks.Version4, + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeSOCKS, tag, options.Network.Build(), options.DialerOptions), + dnsRouter: service.FromContext[adapter.DNSRouter](ctx), + logger: logger, + client: socks.NewClient(outboundDialer, options.ServerOptions.Build(), version, options.Username, options.Password), + resolve: version == socks.Version4, } uotOptions := common.PtrValueOrDefault(options.UDPOverTCP) if uotOptions.Enabled { @@ -83,7 +84,7 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination return nil, E.Extend(N.ErrUnknownNetwork, network) } if h.resolve && destination.IsFqdn() { - destinationAddresses, err := h.router.LookupDefault(ctx, destination.Fqdn) + destinationAddresses, err := h.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err } @@ -101,7 +102,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n return h.uotClient.ListenPacket(ctx, destination) } if h.resolve && destination.IsFqdn() { - destinationAddresses, err := h.router.LookupDefault(ctx, destination.Fqdn) + destinationAddresses, err := h.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err } diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index 2f6825fd..1dead5c9 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -13,13 +13,13 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/wireguard" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" ) func RegisterEndpoint(registry *endpoint.Registry) { @@ -30,6 +30,7 @@ type Endpoint struct { endpoint.Adapter ctx context.Context router adapter.Router + dnsRouter adapter.DNSRouter logger logger.ContextLogger localAddresses []netip.Prefix endpoint *wireguard.Endpoint @@ -40,6 +41,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL Adapter: endpoint.NewAdapterWithDialerOptions(C.TypeWireGuard, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions), ctx: ctx, router: router, + dnsRouter: service.FromContext[adapter.DNSRouter](ctx), logger: logger, localAddresses: options.Address, } @@ -74,7 +76,9 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL PrivateKey: options.PrivateKey, ListenPort: options.ListenPort, ResolvePeer: func(domain string) (netip.Addr, error) { - endpointAddresses, lookupErr := router.Lookup(ctx, domain, dns.DomainStrategy(options.DomainStrategy)) + endpointAddresses, lookupErr := ep.dnsRouter.Lookup(ctx, domain, adapter.DNSQueryOptions{ + Strategy: C.DomainStrategy(options.DomainStrategy), + }) if lookupErr != nil { return netip.Addr{}, lookupErr } @@ -175,7 +179,7 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination w.logger.InfoContext(ctx, "outbound packet connection to ", destination) } if destination.IsFqdn() { - destinationAddresses, err := w.router.LookupDefault(ctx, destination.Fqdn) + destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err } @@ -189,7 +193,7 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { w.logger.InfoContext(ctx, "outbound packet connection to ", destination) if destination.IsFqdn() { - destinationAddresses, err := w.router.LookupDefault(ctx, destination.Fqdn) + destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err } diff --git a/protocol/wireguard/outbound.go b/protocol/wireguard/outbound.go index 808dade4..47e1dac5 100644 --- a/protocol/wireguard/outbound.go +++ b/protocol/wireguard/outbound.go @@ -13,12 +13,12 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/wireguard" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" ) func RegisterOutbound(registry *outbound.Registry) { @@ -28,7 +28,7 @@ func RegisterOutbound(registry *outbound.Registry) { type Outbound struct { outbound.Adapter ctx context.Context - router adapter.Router + dnsRouter adapter.DNSRouter logger logger.ContextLogger localAddresses []netip.Prefix endpoint *wireguard.Endpoint @@ -42,7 +42,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL outbound := &Outbound{ Adapter: outbound.NewAdapterWithDialerOptions(C.TypeWireGuard, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions), ctx: ctx, - router: router, + dnsRouter: service.FromContext[adapter.DNSRouter](ctx), logger: logger, localAddresses: options.LocalAddress, } @@ -89,7 +89,9 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL Address: options.LocalAddress, PrivateKey: options.PrivateKey, ResolvePeer: func(domain string) (netip.Addr, error) { - endpointAddresses, lookupErr := router.Lookup(ctx, domain, dns.DomainStrategy(options.DomainStrategy)) + endpointAddresses, lookupErr := outbound.dnsRouter.Lookup(ctx, domain, adapter.DNSQueryOptions{ + Strategy: C.DomainStrategy(options.DomainStrategy), + }) if lookupErr != nil { return netip.Addr{}, lookupErr } @@ -127,7 +129,7 @@ func (o *Outbound) DialContext(ctx context.Context, network string, destination o.logger.InfoContext(ctx, "outbound packet connection to ", destination) } if destination.IsFqdn() { - destinationAddresses, err := o.router.LookupDefault(ctx, destination.Fqdn) + destinationAddresses, err := o.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err } @@ -141,7 +143,7 @@ func (o *Outbound) DialContext(ctx context.Context, network string, destination func (o *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { o.logger.InfoContext(ctx, "outbound packet connection to ", destination) if destination.IsFqdn() { - destinationAddresses, err := o.router.LookupDefault(ctx, destination.Fqdn) + destinationAddresses, err := o.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { return nil, err } diff --git a/release/config/config.json b/release/config/config.json index c518d18b..bdc78d40 100644 --- a/release/config/config.json +++ b/release/config/config.json @@ -14,10 +14,13 @@ "type": "shadowsocks", "listen": "::", "listen_port": 8080, - "sniff": true, "network": "tcp", "method": "2022-blake3-aes-128-gcm", - "password": "8JCsPssfgS8tiRwiMlhARg==" + "password": "Gn1JUS14bLUHgv1cWDDp4A==", + "multiplex": { + "enabled": true, + "padding": true + } } ], "outbounds": [ @@ -32,7 +35,7 @@ "route": { "rules": [ { - "protocol": "dns", + "port": 53, "outbound": "dns-out" } ] diff --git a/route/dns.go b/route/dns.go index 2c6efefe..7d2b5778 100644 --- a/route/dns.go +++ b/route/dns.go @@ -8,11 +8,12 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" dnsOutbound "github.com/sagernet/sing-box/protocol/dns" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/udpnat2" @@ -24,7 +25,7 @@ func (r *Router) hijackDNSStream(ctx context.Context, conn net.Conn, metadata ad metadata.Destination = M.Socksaddr{} for { conn.SetReadDeadline(time.Now().Add(C.DNSTimeout)) - err := dnsOutbound.HandleStreamDNSRequest(ctx, r, conn, metadata) + err := dnsOutbound.HandleStreamDNSRequest(ctx, r.dns, conn, metadata) if err != nil { return err } @@ -38,37 +39,38 @@ func (r *Router) hijackDNSPacket(ctx context.Context, conn N.PacketConn, packetB buffer := packet.Buffer destination := packet.Destination N.PutPacketBuffer(packet) - go ExchangeDNSPacket(ctx, r, natConn, buffer, metadata, destination) + go ExchangeDNSPacket(ctx, r.dns, r.logger, natConn, buffer, metadata, destination) } natConn.SetHandler(&dnsHijacker{ - router: r, + router: r.dns, + logger: r.logger, conn: conn, ctx: ctx, metadata: metadata, }) return } - err := dnsOutbound.NewDNSPacketConnection(ctx, r, conn, packetBuffers, metadata) + err := dnsOutbound.NewDNSPacketConnection(ctx, r.dns, conn, packetBuffers, metadata) if err != nil && !E.IsClosedOrCanceled(err) { - r.dnsLogger.ErrorContext(ctx, E.Cause(err, "process packet connection")) + r.logger.ErrorContext(ctx, E.Cause(err, "process DNS packet connection")) } } -func ExchangeDNSPacket(ctx context.Context, router *Router, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) { +func ExchangeDNSPacket(ctx context.Context, router adapter.DNSRouter, logger logger.ContextLogger, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) { err := exchangeDNSPacket(ctx, router, conn, buffer, metadata, destination) if err != nil && !errors.Is(err, tun.ErrDrop) && !E.IsClosedOrCanceled(err) { - router.dnsLogger.ErrorContext(ctx, E.Cause(err, "process packet connection")) + logger.ErrorContext(ctx, E.Cause(err, "process DNS packet connection")) } } -func exchangeDNSPacket(ctx context.Context, router *Router, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) error { +func exchangeDNSPacket(ctx context.Context, router adapter.DNSRouter, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) error { var message mDNS.Msg err := message.Unpack(buffer.Bytes()) buffer.Release() if err != nil { return E.Cause(err, "unpack request") } - response, err := router.Exchange(adapter.WithContext(ctx, &metadata), &message) + response, err := router.Exchange(adapter.WithContext(ctx, &metadata), &message, adapter.DNSQueryOptions{}) if err != nil { return err } @@ -81,12 +83,13 @@ func exchangeDNSPacket(ctx context.Context, router *Router, conn N.PacketConn, b } type dnsHijacker struct { - router *Router + router adapter.DNSRouter + logger logger.ContextLogger conn N.PacketConn ctx context.Context metadata adapter.InboundContext } func (h *dnsHijacker) NewPacketEx(buffer *buf.Buffer, destination M.Socksaddr) { - go ExchangeDNSPacket(h.ctx, h.router, h.conn, buffer, h.metadata, destination) + go ExchangeDNSPacket(h.ctx, h.router, h.logger, h.conn, buffer, h.metadata, destination) } diff --git a/route/geo_resources.go b/route/geo_resources.go deleted file mode 100644 index 8a8a3ef5..00000000 --- a/route/geo_resources.go +++ /dev/null @@ -1,246 +0,0 @@ -package route - -import ( - "context" - "io" - "net" - "net/http" - "os" - "path/filepath" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/common/geoip" - "github.com/sagernet/sing-box/common/geosite" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/experimental/deprecated" - R "github.com/sagernet/sing-box/route/rule" - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" - "github.com/sagernet/sing/common/rw" - "github.com/sagernet/sing/service/filemanager" -) - -func (r *Router) GeoIPReader() *geoip.Reader { - return r.geoIPReader -} - -func (r *Router) LoadGeosite(code string) (adapter.Rule, error) { - rule, cached := r.geositeCache[code] - if cached { - return rule, nil - } - items, err := r.geositeReader.Read(code) - if err != nil { - return nil, err - } - rule, err = R.NewDefaultRule(r.ctx, nil, geosite.Compile(items)) - if err != nil { - return nil, err - } - r.geositeCache[code] = rule - return rule, nil -} - -func (r *Router) prepareGeoIPDatabase() error { - deprecated.Report(r.ctx, deprecated.OptionGEOIP) - var geoPath string - if r.geoIPOptions.Path != "" { - geoPath = r.geoIPOptions.Path - } else { - geoPath = "geoip.db" - if foundPath, loaded := C.FindPath(geoPath); loaded { - geoPath = foundPath - } - } - if !rw.IsFile(geoPath) { - geoPath = filemanager.BasePath(r.ctx, geoPath) - } - if stat, err := os.Stat(geoPath); err == nil { - if stat.IsDir() { - return E.New("geoip path is a directory: ", geoPath) - } - if stat.Size() == 0 { - os.Remove(geoPath) - } - } - if !rw.IsFile(geoPath) { - r.logger.Warn("geoip database not exists: ", geoPath) - var err error - for attempts := 0; attempts < 3; attempts++ { - err = r.downloadGeoIPDatabase(geoPath) - if err == nil { - break - } - r.logger.Error("download geoip database: ", err) - os.Remove(geoPath) - // time.Sleep(10 * time.Second) - } - if err != nil { - return err - } - } - geoReader, codes, err := geoip.Open(geoPath) - if err != nil { - return E.Cause(err, "open geoip database") - } - r.logger.Info("loaded geoip database: ", len(codes), " codes") - r.geoIPReader = geoReader - return nil -} - -func (r *Router) prepareGeositeDatabase() error { - deprecated.Report(r.ctx, deprecated.OptionGEOSITE) - var geoPath string - if r.geositeOptions.Path != "" { - geoPath = r.geositeOptions.Path - } else { - geoPath = "geosite.db" - if foundPath, loaded := C.FindPath(geoPath); loaded { - geoPath = foundPath - } - } - if !rw.IsFile(geoPath) { - geoPath = filemanager.BasePath(r.ctx, geoPath) - } - if stat, err := os.Stat(geoPath); err == nil { - if stat.IsDir() { - return E.New("geoip path is a directory: ", geoPath) - } - if stat.Size() == 0 { - os.Remove(geoPath) - } - } - if !rw.IsFile(geoPath) { - r.logger.Warn("geosite database not exists: ", geoPath) - var err error - for attempts := 0; attempts < 3; attempts++ { - err = r.downloadGeositeDatabase(geoPath) - if err == nil { - break - } - r.logger.Error("download geosite database: ", err) - os.Remove(geoPath) - } - if err != nil { - return err - } - } - geoReader, codes, err := geosite.Open(geoPath) - if err == nil { - r.logger.Info("loaded geosite database: ", len(codes), " codes") - r.geositeReader = geoReader - } else { - return E.Cause(err, "open geosite database") - } - return nil -} - -func (r *Router) downloadGeoIPDatabase(savePath string) error { - var downloadURL string - if r.geoIPOptions.DownloadURL != "" { - downloadURL = r.geoIPOptions.DownloadURL - } else { - downloadURL = "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db" - } - r.logger.Info("downloading geoip database") - var detour adapter.Outbound - if r.geoIPOptions.DownloadDetour != "" { - outbound, loaded := r.outbound.Outbound(r.geoIPOptions.DownloadDetour) - if !loaded { - return E.New("detour outbound not found: ", r.geoIPOptions.DownloadDetour) - } - detour = outbound - } else { - detour = r.outbound.Default() - } - - if parentDir := filepath.Dir(savePath); parentDir != "" { - filemanager.MkdirAll(r.ctx, parentDir, 0o755) - } - - httpClient := &http.Client{ - Transport: &http.Transport{ - ForceAttemptHTTP2: true, - TLSHandshakeTimeout: C.TCPTimeout, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) - }, - }, - } - defer httpClient.CloseIdleConnections() - request, err := http.NewRequest("GET", downloadURL, nil) - if err != nil { - return err - } - response, err := httpClient.Do(request.WithContext(r.ctx)) - if err != nil { - return err - } - defer response.Body.Close() - - saveFile, err := filemanager.Create(r.ctx, savePath) - if err != nil { - return E.Cause(err, "open output file: ", downloadURL) - } - _, err = io.Copy(saveFile, response.Body) - saveFile.Close() - if err != nil { - filemanager.Remove(r.ctx, savePath) - } - return err -} - -func (r *Router) downloadGeositeDatabase(savePath string) error { - var downloadURL string - if r.geositeOptions.DownloadURL != "" { - downloadURL = r.geositeOptions.DownloadURL - } else { - downloadURL = "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db" - } - r.logger.Info("downloading geosite database") - var detour adapter.Outbound - if r.geositeOptions.DownloadDetour != "" { - outbound, loaded := r.outbound.Outbound(r.geositeOptions.DownloadDetour) - if !loaded { - return E.New("detour outbound not found: ", r.geositeOptions.DownloadDetour) - } - detour = outbound - } else { - detour = r.outbound.Default() - } - - if parentDir := filepath.Dir(savePath); parentDir != "" { - filemanager.MkdirAll(r.ctx, parentDir, 0o755) - } - - httpClient := &http.Client{ - Transport: &http.Transport{ - ForceAttemptHTTP2: true, - TLSHandshakeTimeout: C.TCPTimeout, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) - }, - }, - } - defer httpClient.CloseIdleConnections() - request, err := http.NewRequest("GET", downloadURL, nil) - if err != nil { - return err - } - response, err := httpClient.Do(request.WithContext(r.ctx)) - if err != nil { - return err - } - defer response.Body.Close() - - saveFile, err := filemanager.Create(r.ctx, savePath) - if err != nil { - return E.Cause(err, "open output file: ", downloadURL) - } - _, err = io.Copy(saveFile, response.Body) - saveFile.Close() - if err != nil { - filemanager.Remove(r.ctx, savePath) - } - return err -} diff --git a/route/route.go b/route/route.go index f3e7a983..ec76318f 100644 --- a/route/route.go +++ b/route/route.go @@ -17,7 +17,6 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/route/rule" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing-mux" "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common" @@ -318,22 +317,23 @@ func (r *Router) matchRule( metadata.ProcessInfo = processInfo } } - if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) { - domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr) + if metadata.Destination.Addr.IsValid() && r.dnsTransport.FakeIP() != nil && r.dnsTransport.FakeIP().Store().Contains(metadata.Destination.Addr) { + domain, loaded := r.dnsTransport.FakeIP().Store().Lookup(metadata.Destination.Addr) if !loaded { - fatalErr = E.New("missing fakeip record, try to configure experimental.cache_file") + fatalErr = E.New("missing fakeip record, try enable `experimental.cache_file`") return } - metadata.OriginDestination = metadata.Destination - metadata.Destination = M.Socksaddr{ - Fqdn: domain, - Port: metadata.Destination.Port, + if domain != "" { + metadata.OriginDestination = metadata.Destination + metadata.Destination = M.Socksaddr{ + Fqdn: domain, + Port: metadata.Destination.Port, + } + metadata.FakeIP = true + r.logger.DebugContext(ctx, "found fakeip domain: ", domain) } - metadata.FakeIP = true - r.logger.DebugContext(ctx, "found fakeip domain: ", domain) - } - if r.dnsReverseMapping != nil && metadata.Domain == "" { - domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr) + } else if metadata.Domain == "" { + domain, loaded := r.dns.LookupReverseMapping(metadata.Destination.Addr) if loaded { metadata.Domain = domain r.logger.DebugContext(ctx, "found reserve mapped domain: ", metadata.Domain) @@ -362,9 +362,9 @@ func (r *Router) matchRule( packetBuffers = newPackerBuffers } } - if dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS { + if C.DomainStrategy(metadata.InboundOptions.DomainStrategy) != C.DomainStrategyAsIS { fatalErr = r.actionResolve(ctx, metadata, &rule.RuleActionResolve{ - Strategy: dns.DomainStrategy(metadata.InboundOptions.DomainStrategy), + Strategy: C.DomainStrategy(metadata.InboundOptions.DomainStrategy), }) if fatalErr != nil { return @@ -644,13 +644,23 @@ func (r *Router) actionSniff( func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionResolve) error { if metadata.Destination.IsFqdn() { - metadata.DNSServer = action.Server - addresses, err := r.Lookup(adapter.WithContext(ctx, metadata), metadata.Destination.Fqdn, action.Strategy) + var transport adapter.DNSTransport + if action.Server != "" { + var loaded bool + transport, loaded = r.dnsTransport.Transport(action.Server) + if !loaded { + return E.New("DNS server not found: ", action.Server) + } + } + addresses, err := r.dns.Lookup(adapter.WithContext(ctx, metadata), metadata.Destination.Fqdn, adapter.DNSQueryOptions{ + Transport: transport, + Strategy: action.Strategy, + }) if err != nil { return err } metadata.DestinationAddresses = addresses - r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") + r.logger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") if metadata.Destination.IsIPv4() { metadata.IPVersion = 4 } else if metadata.Destination.IsIPv6() { diff --git a/route/route_dns.go b/route/route_dns.go deleted file mode 100644 index 62eff1c2..00000000 --- a/route/route_dns.go +++ /dev/null @@ -1,348 +0,0 @@ -package route - -import ( - "context" - "errors" - "net/netip" - "strings" - "time" - - "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" - R "github.com/sagernet/sing-box/route/rule" - "github.com/sagernet/sing-dns" - "github.com/sagernet/sing-tun" - "github.com/sagernet/sing/common/cache" - E "github.com/sagernet/sing/common/exceptions" - F "github.com/sagernet/sing/common/format" - M "github.com/sagernet/sing/common/metadata" - - mDNS "github.com/miekg/dns" -) - -type DNSReverseMapping struct { - cache *cache.LruCache[netip.Addr, string] -} - -func NewDNSReverseMapping() *DNSReverseMapping { - return &DNSReverseMapping{ - cache: cache.New[netip.Addr, string](), - } -} - -func (m *DNSReverseMapping) Save(address netip.Addr, domain string, ttl int) { - m.cache.StoreWithExpire(address, domain, time.Now().Add(time.Duration(ttl)*time.Second)) -} - -func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) { - domain, loaded := m.cache.Load(address) - return domain, loaded -} - -func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, isAddressQuery bool) (dns.Transport, dns.QueryOptions, adapter.DNSRule, int) { - metadata := adapter.ContextFrom(ctx) - if metadata == nil { - panic("no context") - } - var options dns.QueryOptions - var currentRuleIndex int - if ruleIndex != -1 { - currentRuleIndex = ruleIndex + 1 - } - for ; currentRuleIndex < len(r.dnsRules); currentRuleIndex++ { - currentRule := r.dnsRules[currentRuleIndex] - if currentRule.WithAddressLimit() && !isAddressQuery { - continue - } - metadata.ResetRuleCache() - if currentRule.Match(metadata) { - ruleDescription := currentRule.String() - if ruleDescription != "" { - r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action()) - } else { - r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] => ", currentRule.Action()) - } - switch action := currentRule.Action().(type) { - case *R.RuleActionDNSRoute: - transport, loaded := r.transportMap[action.Server] - if !loaded { - r.dnsLogger.ErrorContext(ctx, "transport not found: ", action.Server) - continue - } - _, isFakeIP := transport.(adapter.FakeIPTransport) - if isFakeIP && !allowFakeIP { - continue - } - if isFakeIP || action.DisableCache { - options.DisableCache = true - } - if action.RewriteTTL != nil { - options.RewriteTTL = action.RewriteTTL - } - if action.ClientSubnet.IsValid() { - options.ClientSubnet = action.ClientSubnet - } - if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded { - options.Strategy = domainStrategy - } else { - options.Strategy = r.defaultDomainStrategy - } - r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] => ", currentRule.Action()) - return transport, options, currentRule, currentRuleIndex - case *R.RuleActionDNSRouteOptions: - if action.DisableCache { - options.DisableCache = true - } - if action.RewriteTTL != nil { - options.RewriteTTL = action.RewriteTTL - } - if action.ClientSubnet.IsValid() { - options.ClientSubnet = action.ClientSubnet - } - r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] => ", currentRule.Action()) - case *R.RuleActionReject: - r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] => ", currentRule.Action()) - return nil, options, currentRule, currentRuleIndex - } - } - } - if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded { - options.Strategy = domainStrategy - } else { - options.Strategy = r.defaultDomainStrategy - } - return r.defaultTransport, options, nil, -1 -} - -func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { - if len(message.Question) != 1 { - r.dnsLogger.WarnContext(ctx, "bad question size: ", len(message.Question)) - responseMessage := mDNS.Msg{ - MsgHdr: mDNS.MsgHdr{ - Id: message.Id, - Response: true, - Rcode: mDNS.RcodeFormatError, - }, - Question: message.Question, - } - return &responseMessage, nil - } - var ( - response *mDNS.Msg - cached bool - transport dns.Transport - err error - ) - response, cached = r.dnsClient.ExchangeCache(ctx, message) - if !cached { - var metadata *adapter.InboundContext - ctx, metadata = adapter.ExtendContext(ctx) - metadata.Destination = M.Socksaddr{} - metadata.QueryType = message.Question[0].Qtype - switch metadata.QueryType { - case mDNS.TypeA: - metadata.IPVersion = 4 - case mDNS.TypeAAAA: - metadata.IPVersion = 6 - } - metadata.Domain = fqdnToDomain(message.Question[0].Name) - var ( - options dns.QueryOptions - rule adapter.DNSRule - ruleIndex int - ) - ruleIndex = -1 - for { - dnsCtx := adapter.OverrideContext(ctx) - var addressLimit bool - transport, options, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message)) - if rule != nil { - switch action := rule.Action().(type) { - case *R.RuleActionReject: - switch action.Method { - case C.RuleActionRejectMethodDefault: - return dns.FixedResponse(message.Id, message.Question[0], nil, 0), nil - case C.RuleActionRejectMethodDrop: - return nil, tun.ErrDrop - } - } - } - r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()), " via ", transport.Name()) - if rule != nil && rule.WithAddressLimit() { - addressLimit = true - response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, options, func(responseAddrs []netip.Addr) bool { - metadata.DestinationAddresses = responseAddrs - return rule.MatchAddressLimit(metadata) - }) - } else { - addressLimit = false - response, err = r.dnsClient.Exchange(dnsCtx, transport, message, options) - } - var rejected bool - if err != nil { - if errors.Is(err, dns.ErrResponseRejectedCached) { - rejected = true - r.dnsLogger.DebugContext(ctx, E.Cause(err, "response rejected for ", formatQuestion(message.Question[0].String())), " (cached)") - } else if errors.Is(err, dns.ErrResponseRejected) { - rejected = true - r.dnsLogger.DebugContext(ctx, E.Cause(err, "response rejected for ", formatQuestion(message.Question[0].String()))) - } else if len(message.Question) > 0 { - r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String()))) - } else { - r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ")) - } - } - if addressLimit && rejected { - continue - } - break - } - } - if err != nil { - return nil, err - } - if r.dnsReverseMapping != nil && response != nil && len(response.Answer) > 0 { - if _, isFakeIP := transport.(adapter.FakeIPTransport); !isFakeIP { - for _, answer := range response.Answer { - switch record := answer.(type) { - case *mDNS.A: - r.dnsReverseMapping.Save(M.AddrFromIP(record.A), fqdnToDomain(record.Hdr.Name), int(record.Hdr.Ttl)) - case *mDNS.AAAA: - r.dnsReverseMapping.Save(M.AddrFromIP(record.AAAA), fqdnToDomain(record.Hdr.Name), int(record.Hdr.Ttl)) - } - } - } - } - return response, nil -} - -func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { - var ( - responseAddrs []netip.Addr - cached bool - err error - ) - printResult := func() { - if err == nil && len(responseAddrs) == 0 { - err = E.New("empty result") - } - if err != nil { - if errors.Is(err, dns.ErrResponseRejectedCached) { - r.dnsLogger.DebugContext(ctx, "response rejected for ", domain, " (cached)") - } else if errors.Is(err, dns.ErrResponseRejected) { - r.dnsLogger.DebugContext(ctx, "response rejected for ", domain) - } else { - r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain)) - } - } - } - responseAddrs, cached = r.dnsClient.LookupCache(ctx, domain, strategy) - if cached { - if len(responseAddrs) == 0 { - return nil, dns.RCodeNameError - } - return responseAddrs, nil - } - r.dnsLogger.DebugContext(ctx, "lookup domain ", domain) - ctx, metadata := adapter.ExtendContext(ctx) - metadata.Destination = M.Socksaddr{} - metadata.Domain = domain - if metadata.DNSServer != "" { - transport, loaded := r.transportMap[metadata.DNSServer] - if !loaded { - return nil, E.New("transport not found: ", metadata.DNSServer) - } - if strategy == dns.DomainStrategyAsIS { - if transportDomainStrategy, loaded := r.transportDomainStrategy[transport]; loaded { - strategy = transportDomainStrategy - } else { - strategy = r.defaultDomainStrategy - } - } - responseAddrs, err = r.dnsClient.Lookup(ctx, transport, domain, dns.QueryOptions{Strategy: strategy}) - } else { - var ( - transport dns.Transport - options dns.QueryOptions - rule adapter.DNSRule - ruleIndex int - ) - ruleIndex = -1 - for { - dnsCtx := adapter.OverrideContext(ctx) - var addressLimit bool - transport, options, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true) - if strategy != dns.DomainStrategyAsIS { - options.Strategy = strategy - } - if rule != nil { - switch action := rule.Action().(type) { - case *R.RuleActionReject: - switch action.Method { - case C.RuleActionRejectMethodDefault: - return nil, nil - case C.RuleActionRejectMethodDrop: - return nil, tun.ErrDrop - } - } - } - if rule != nil && rule.WithAddressLimit() { - addressLimit = true - responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, options, func(responseAddrs []netip.Addr) bool { - metadata.DestinationAddresses = responseAddrs - return rule.MatchAddressLimit(metadata) - }) - } else { - addressLimit = false - responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, options) - } - if !addressLimit || err == nil { - break - } - printResult() - } - } - printResult() - if len(responseAddrs) > 0 { - r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(responseAddrs), " ")) - } - return responseAddrs, err -} - -func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) { - return r.Lookup(ctx, domain, dns.DomainStrategyAsIS) -} - -func (r *Router) ClearDNSCache() { - r.dnsClient.ClearCache() - if r.platformInterface != nil { - r.platformInterface.ClearDNSCache() - } -} - -func isAddressQuery(message *mDNS.Msg) bool { - for _, question := range message.Question { - if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA || question.Qtype == mDNS.TypeHTTPS { - return true - } - } - return false -} - -func fqdnToDomain(fqdn string) string { - if mDNS.IsFqdn(fqdn) { - return fqdn[:len(fqdn)-1] - } - return fqdn -} - -func formatQuestion(string string) string { - if strings.HasPrefix(string, ";") { - string = string[1:] - } - string = strings.ReplaceAll(string, "\t", " ") - for strings.Contains(string, " ") { - string = strings.ReplaceAll(string, " ", " ") - } - return string -} diff --git a/route/router.go b/route/router.go index b74af8b9..0872421f 100644 --- a/route/router.go +++ b/route/router.go @@ -2,17 +2,10 @@ package route import ( "context" - "net/netip" - "net/url" "os" "runtime" - "strings" - "time" "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/common/dialer" - "github.com/sagernet/sing-box/common/geoip" - "github.com/sagernet/sing-box/common/geosite" "github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/taskmonitor" C "github.com/sagernet/sing-box/constant" @@ -20,13 +13,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" R "github.com/sagernet/sing-box/route/rule" - "github.com/sagernet/sing-box/transport/fakeip" - "github.com/sagernet/sing-dns" - "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" - F "github.com/sagernet/sing/common/format" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/task" "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/pause" @@ -35,334 +22,71 @@ import ( var _ adapter.Router = (*Router)(nil) type Router struct { - ctx context.Context - logger log.ContextLogger - dnsLogger log.ContextLogger - inbound adapter.InboundManager - outbound adapter.OutboundManager - connection adapter.ConnectionManager - network adapter.NetworkManager - rules []adapter.Rule - needGeoIPDatabase bool - needGeositeDatabase bool - geoIPOptions option.GeoIPOptions - geositeOptions option.GeositeOptions - geoIPReader *geoip.Reader - geositeReader *geosite.Reader - geositeCache map[string]adapter.Rule - needFindProcess bool - dnsClient *dns.Client - defaultDomainStrategy dns.DomainStrategy - dnsRules []adapter.DNSRule - ruleSets []adapter.RuleSet - ruleSetMap map[string]adapter.RuleSet - defaultTransport dns.Transport - transports []dns.Transport - transportMap map[string]dns.Transport - transportDomainStrategy map[dns.Transport]dns.DomainStrategy - dnsReverseMapping *DNSReverseMapping - fakeIPStore adapter.FakeIPStore - processSearcher process.Searcher - pauseManager pause.Manager - trackers []adapter.ConnectionTracker - platformInterface platform.Interface - needWIFIState bool - started bool + ctx context.Context + logger log.ContextLogger + inbound adapter.InboundManager + outbound adapter.OutboundManager + dns adapter.DNSRouter + dnsTransport adapter.DNSTransportManager + connection adapter.ConnectionManager + network adapter.NetworkManager + rules []adapter.Rule + needFindProcess bool + ruleSets []adapter.RuleSet + ruleSetMap map[string]adapter.RuleSet + processSearcher process.Searcher + pauseManager pause.Manager + trackers []adapter.ConnectionTracker + platformInterface platform.Interface + needWIFIState bool + started bool } -func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions) (*Router, error) { - router := &Router{ - ctx: ctx, - logger: logFactory.NewLogger("router"), - dnsLogger: logFactory.NewLogger("dns"), - inbound: service.FromContext[adapter.InboundManager](ctx), - outbound: service.FromContext[adapter.OutboundManager](ctx), - connection: service.FromContext[adapter.ConnectionManager](ctx), - network: service.FromContext[adapter.NetworkManager](ctx), - rules: make([]adapter.Rule, 0, len(options.Rules)), - dnsRules: make([]adapter.DNSRule, 0, len(dnsOptions.Rules)), - ruleSetMap: make(map[string]adapter.RuleSet), - needGeoIPDatabase: hasRule(options.Rules, isGeoIPRule) || hasDNSRule(dnsOptions.Rules, isGeoIPDNSRule), - needGeositeDatabase: hasRule(options.Rules, isGeositeRule) || hasDNSRule(dnsOptions.Rules, isGeositeDNSRule), - geoIPOptions: common.PtrValueOrDefault(options.GeoIP), - geositeOptions: common.PtrValueOrDefault(options.Geosite), - geositeCache: make(map[string]adapter.Rule), - needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess, - defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy), - pauseManager: service.FromContext[pause.Manager](ctx), - platformInterface: service.FromContext[platform.Interface](ctx), - needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule), - } - service.MustRegister[adapter.Router](ctx, router) - router.dnsClient = dns.NewClient(dns.ClientOptions{ - DisableCache: dnsOptions.DNSClientOptions.DisableCache, - DisableExpire: dnsOptions.DNSClientOptions.DisableExpire, - IndependentCache: dnsOptions.DNSClientOptions.IndependentCache, - CacheCapacity: dnsOptions.DNSClientOptions.CacheCapacity, - RDRC: func() dns.RDRCStore { - cacheFile := service.FromContext[adapter.CacheFile](ctx) - if cacheFile == nil { - return nil - } - if !cacheFile.StoreRDRC() { - return nil - } - return cacheFile - }, - Logger: router.dnsLogger, - }) - for i, ruleOptions := range options.Rules { - routeRule, err := R.NewRule(ctx, router.logger, ruleOptions, true) - if err != nil { - return nil, E.Cause(err, "parse rule[", i, "]") - } - router.rules = append(router.rules, routeRule) - } - for i, dnsRuleOptions := range dnsOptions.Rules { - dnsRule, err := R.NewDNSRule(ctx, router.logger, dnsRuleOptions, true) - if err != nil { - return nil, E.Cause(err, "parse dns rule[", i, "]") - } - router.dnsRules = append(router.dnsRules, dnsRule) - } - for i, ruleSetOptions := range options.RuleSet { - if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists { - return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag) - } - ruleSet, err := R.NewRuleSet(ctx, router.logger, ruleSetOptions) - if err != nil { - return nil, E.Cause(err, "parse rule-set[", i, "]") - } - router.ruleSets = append(router.ruleSets, ruleSet) - router.ruleSetMap[ruleSetOptions.Tag] = ruleSet +func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions) *Router { + return &Router{ + ctx: ctx, + logger: logFactory.NewLogger("router"), + inbound: service.FromContext[adapter.InboundManager](ctx), + outbound: service.FromContext[adapter.OutboundManager](ctx), + dns: service.FromContext[adapter.DNSRouter](ctx), + dnsTransport: service.FromContext[adapter.DNSTransportManager](ctx), + connection: service.FromContext[adapter.ConnectionManager](ctx), + network: service.FromContext[adapter.NetworkManager](ctx), + rules: make([]adapter.Rule, 0, len(options.Rules)), + ruleSetMap: make(map[string]adapter.RuleSet), + needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess, + pauseManager: service.FromContext[pause.Manager](ctx), + platformInterface: service.FromContext[platform.Interface](ctx), + needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule), } +} - transports := make([]dns.Transport, len(dnsOptions.Servers)) - dummyTransportMap := make(map[string]dns.Transport) - transportMap := make(map[string]dns.Transport) - transportTags := make([]string, len(dnsOptions.Servers)) - transportTagMap := make(map[string]bool) - transportDomainStrategy := make(map[dns.Transport]dns.DomainStrategy) - for i, server := range dnsOptions.Servers { - var tag string - if server.Tag != "" { - tag = server.Tag - } else { - tag = F.ToString(i) +func (r *Router) Initialize(rules []option.Rule, ruleSets []option.RuleSet) error { + for i, options := range rules { + rule, err := R.NewRule(r.ctx, r.logger, options, false) + if err != nil { + return E.Cause(err, "parse rule[", i, "]") } - if transportTagMap[tag] { - return nil, E.New("duplicate dns server tag: ", tag) - } - transportTags[i] = tag - transportTagMap[tag] = true + r.rules = append(r.rules, rule) } - outboundManager := service.FromContext[adapter.OutboundManager](ctx) - for { - lastLen := len(dummyTransportMap) - for i, server := range dnsOptions.Servers { - tag := transportTags[i] - if _, exists := dummyTransportMap[tag]; exists { - continue - } - var detour N.Dialer - if server.Detour == "" { - detour = dialer.NewDefaultOutbound(outboundManager) - } else { - detour = dialer.NewDetour(outboundManager, server.Detour) - } - var serverProtocol string - switch server.Address { - case "local": - serverProtocol = "local" - default: - serverURL, _ := url.Parse(server.Address) - var serverAddress string - if serverURL != nil { - if serverURL.Scheme == "" { - serverProtocol = "udp" - } else { - serverProtocol = serverURL.Scheme - } - serverAddress = serverURL.Hostname() - } - if serverAddress == "" { - serverAddress = server.Address - } - notIpAddress := !M.ParseSocksaddr(serverAddress).Addr.IsValid() - if server.AddressResolver != "" { - if !transportTagMap[server.AddressResolver] { - return nil, E.New("parse dns server[", tag, "]: address resolver not found: ", server.AddressResolver) - } - if upstream, exists := dummyTransportMap[server.AddressResolver]; exists { - detour = dns.NewDialerWrapper(detour, router.dnsClient, upstream, dns.DomainStrategy(server.AddressStrategy), time.Duration(server.AddressFallbackDelay)) - } else { - continue - } - } else if notIpAddress && strings.Contains(server.Address, ".") { - return nil, E.New("parse dns server[", tag, "]: missing address_resolver") - } - } - var clientSubnet netip.Prefix - if server.ClientSubnet != nil { - clientSubnet = netip.Prefix(common.PtrValueOrDefault(server.ClientSubnet)) - } else if dnsOptions.ClientSubnet != nil { - clientSubnet = netip.Prefix(common.PtrValueOrDefault(dnsOptions.ClientSubnet)) - } - if serverProtocol == "" { - serverProtocol = "transport" - } - transport, err := dns.CreateTransport(dns.TransportOptions{ - Context: ctx, - Logger: logFactory.NewLogger(F.ToString("dns/", serverProtocol, "[", tag, "]")), - Name: tag, - Dialer: detour, - Address: server.Address, - ClientSubnet: clientSubnet, - }) - if err != nil { - return nil, E.Cause(err, "parse dns server[", tag, "]") - } - transports[i] = transport - dummyTransportMap[tag] = transport - if server.Tag != "" { - transportMap[server.Tag] = transport - } - strategy := dns.DomainStrategy(server.Strategy) - if strategy != dns.DomainStrategyAsIS { - transportDomainStrategy[transport] = strategy - } + for i, options := range ruleSets { + if _, exists := r.ruleSetMap[options.Tag]; exists { + return E.New("duplicate rule-set tag: ", options.Tag) } - if len(transports) == len(dummyTransportMap) { - break + ruleSet, err := R.NewRuleSet(r.ctx, r.logger, options) + if err != nil { + return E.Cause(err, "parse rule-set[", i, "]") } - if lastLen != len(dummyTransportMap) { - continue - } - unresolvedTags := common.MapIndexed(common.FilterIndexed(dnsOptions.Servers, func(index int, server option.DNSServerOptions) bool { - _, exists := dummyTransportMap[transportTags[index]] - return !exists - }), func(index int, server option.DNSServerOptions) string { - return transportTags[index] - }) - if len(unresolvedTags) == 0 { - panic(F.ToString("unexpected unresolved dns servers: ", len(transports), " ", len(dummyTransportMap), " ", len(transportMap))) - } - return nil, E.New("found circular reference in dns servers: ", strings.Join(unresolvedTags, " ")) + r.ruleSets = append(r.ruleSets, ruleSet) + r.ruleSetMap[options.Tag] = ruleSet } - var defaultTransport dns.Transport - if dnsOptions.Final != "" { - defaultTransport = dummyTransportMap[dnsOptions.Final] - if defaultTransport == nil { - return nil, E.New("default dns server not found: ", dnsOptions.Final) - } - } - if defaultTransport == nil { - if len(transports) == 0 { - transports = append(transports, common.Must1(dns.CreateTransport(dns.TransportOptions{ - Context: ctx, - Name: "local", - Address: "local", - Dialer: common.Must1(dialer.NewDefault(ctx, option.DialerOptions{})), - }))) - } - defaultTransport = transports[0] - } - if _, isFakeIP := defaultTransport.(adapter.FakeIPTransport); isFakeIP { - return nil, E.New("default DNS server cannot be fakeip") - } - router.defaultTransport = defaultTransport - router.transports = transports - router.transportMap = transportMap - router.transportDomainStrategy = transportDomainStrategy - - if dnsOptions.ReverseMapping { - router.dnsReverseMapping = NewDNSReverseMapping() - } - - if fakeIPOptions := dnsOptions.FakeIP; fakeIPOptions != nil && dnsOptions.FakeIP.Enabled { - var inet4Range netip.Prefix - var inet6Range netip.Prefix - if fakeIPOptions.Inet4Range != nil { - inet4Range = *fakeIPOptions.Inet4Range - } - if fakeIPOptions.Inet6Range != nil { - inet6Range = *fakeIPOptions.Inet6Range - } - router.fakeIPStore = fakeip.NewStore(ctx, router.logger, inet4Range, inet6Range) - } - return router, nil + return nil } func (r *Router) Start(stage adapter.StartStage) error { monitor := taskmonitor.New(r.logger, C.StartTimeout) switch stage { - case adapter.StartStateInitialize: - if r.fakeIPStore != nil { - monitor.Start("initialize fakeip store") - err := r.fakeIPStore.Start() - monitor.Finish() - if err != nil { - return err - } - } case adapter.StartStateStart: - if r.needGeoIPDatabase { - monitor.Start("initialize geoip database") - err := r.prepareGeoIPDatabase() - monitor.Finish() - if err != nil { - return err - } - } - if r.needGeositeDatabase { - monitor.Start("initialize geosite database") - err := r.prepareGeositeDatabase() - monitor.Finish() - if err != nil { - return err - } - } - if r.needGeositeDatabase { - for _, rule := range r.rules { - err := rule.UpdateGeosite() - if err != nil { - r.logger.Error("failed to initialize geosite: ", err) - } - } - for _, rule := range r.dnsRules { - err := rule.UpdateGeosite() - if err != nil { - r.logger.Error("failed to initialize geosite: ", err) - } - } - err := common.Close(r.geositeReader) - if err != nil { - return err - } - r.geositeCache = nil - r.geositeReader = nil - } - - monitor.Start("initialize DNS client") - r.dnsClient.Start() - monitor.Finish() - - for i, rule := range r.dnsRules { - monitor.Start("initialize DNS rule[", i, "]") - err := rule.Start() - monitor.Finish() - if err != nil { - return E.Cause(err, "initialize DNS rule[", i, "]") - } - } - for i, transport := range r.transports { - monitor.Start("initialize DNS transport[", i, "]") - err := transport.Start() - monitor.Finish() - if err != nil { - return E.Cause(err, "initialize DNS server[", i, "]") - } - } var cacheContext *adapter.HTTPStartContext if len(r.ruleSets) > 0 { monitor.Start("initialize rule-set") @@ -438,7 +162,7 @@ func (r *Router) Start(stage adapter.StartStage) error { r.started = true return nil case adapter.StartStateStarted: - for _, ruleSet := range r.ruleSetMap { + for _, ruleSet := range r.ruleSets { ruleSet.Cleanup() } runtime.GC() @@ -456,34 +180,6 @@ func (r *Router) Close() error { }) monitor.Finish() } - for i, rule := range r.dnsRules { - monitor.Start("close dns rule[", i, "]") - err = E.Append(err, rule.Close(), func(err error) error { - return E.Cause(err, "close dns rule[", i, "]") - }) - monitor.Finish() - } - for i, transport := range r.transports { - monitor.Start("close dns transport[", i, "]") - err = E.Append(err, transport.Close(), func(err error) error { - return E.Cause(err, "close dns transport[", i, "]") - }) - monitor.Finish() - } - if r.geoIPReader != nil { - monitor.Start("close geoip reader") - err = E.Append(err, r.geoIPReader.Close(), func(err error) error { - return E.Cause(err, "close geoip reader") - }) - monitor.Finish() - } - if r.fakeIPStore != nil { - monitor.Start("close fakeip store") - err = E.Append(err, r.fakeIPStore.Close(), func(err error) error { - return E.Cause(err, "close fakeip store") - }) - monitor.Finish() - } for i, ruleSet := range r.ruleSets { monitor.Start("close rule-set[", i, "]") err = E.Append(err, ruleSet.Close(), func(err error) error { @@ -494,10 +190,6 @@ func (r *Router) Close() error { return err } -func (r *Router) FakeIPStore() adapter.FakeIPStore { - return r.fakeIPStore -} - func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) { ruleSet, loaded := r.ruleSetMap[tag] return ruleSet, loaded @@ -517,7 +209,5 @@ func (r *Router) AppendTracker(tracker adapter.ConnectionTracker) { func (r *Router) ResetNetwork() { r.network.ResetNetwork() - for _, transport := range r.transports { - transport.Reset() - } + r.dns.ResetNetwork() } diff --git a/route/rule/rule_abstract.go b/route/rule/rule_abstract.go index 6a569341..5be215e0 100644 --- a/route/rule/rule_abstract.go +++ b/route/rule/rule_abstract.go @@ -51,18 +51,6 @@ func (r *abstractDefaultRule) Close() error { return nil } -func (r *abstractDefaultRule) UpdateGeosite() error { - for _, item := range r.allItems { - if geositeItem, isSite := item.(*GeositeItem); isSite { - err := geositeItem.Update() - if err != nil { - return err - } - } - } - return nil -} - func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool { if len(r.allItems) == 0 { return true @@ -173,19 +161,6 @@ func (r *abstractLogicalRule) Type() string { return C.RuleTypeLogical } -func (r *abstractLogicalRule) UpdateGeosite() error { - for _, rule := range common.FilterIsInstance(r.rules, func(it adapter.HeadlessRule) (adapter.Rule, bool) { - rule, loaded := it.(adapter.Rule) - return rule, loaded - }) { - err := rule.UpdateGeosite() - if err != nil { - return err - } - } - return nil -} - func (r *abstractLogicalRule) Start() error { for _, rule := range common.FilterIsInstance(r.rules, func(it adapter.HeadlessRule) (interface { Start() error diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index d4c6625d..2c4f977f 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -13,7 +13,6 @@ import ( "github.com/sagernet/sing-box/common/sniff" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" @@ -85,7 +84,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti return sniffAction, sniffAction.build() case C.RuleActionTypeResolve: return &RuleActionResolve{ - Strategy: dns.DomainStrategy(action.ResolveOptions.Strategy), + Strategy: C.DomainStrategy(action.ResolveOptions.Strategy), Server: action.ResolveOptions.Server, }, nil default: @@ -101,6 +100,7 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction) return &RuleActionDNSRoute{ Server: action.RouteOptions.Server, RuleActionDNSRouteOptions: RuleActionDNSRouteOptions{ + Strategy: C.DomainStrategy(action.RouteOptions.Strategy), DisableCache: action.RouteOptions.DisableCache, RewriteTTL: action.RouteOptions.RewriteTTL, ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptions.ClientSubnet)), @@ -108,6 +108,7 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction) } case C.RuleActionTypeRouteOptions: return &RuleActionDNSRouteOptions{ + Strategy: C.DomainStrategy(action.RouteOptionsOptions.Strategy), DisableCache: action.RouteOptionsOptions.DisableCache, RewriteTTL: action.RouteOptionsOptions.RewriteTTL, ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptionsOptions.ClientSubnet)), @@ -214,6 +215,7 @@ func (r *RuleActionDNSRoute) String() string { } type RuleActionDNSRouteOptions struct { + Strategy C.DomainStrategy DisableCache bool RewriteTTL *uint32 ClientSubnet netip.Prefix @@ -365,7 +367,7 @@ func (r *RuleActionSniff) String() string { } type RuleActionResolve struct { - Strategy dns.DomainStrategy + Strategy C.DomainStrategy Server string } @@ -374,11 +376,11 @@ func (r *RuleActionResolve) Type() string { } func (r *RuleActionResolve) String() string { - if r.Strategy == dns.DomainStrategyAsIS && r.Server == "" { + if r.Strategy == C.DomainStrategyAsIS && r.Server == "" { return F.ToString("resolve") - } else if r.Strategy != dns.DomainStrategyAsIS && r.Server == "" { + } else if r.Strategy != C.DomainStrategyAsIS && r.Server == "" { return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ")") - } else if r.Strategy == dns.DomainStrategyAsIS && r.Server != "" { + } else if r.Strategy == C.DomainStrategyAsIS && r.Server != "" { return F.ToString("resolve(", r.Server, ")") } else { return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ",", r.Server, ")") diff --git a/route/rule/rule_default.go b/route/rule/rule_default.go index 56cf32dc..08e21e5f 100644 --- a/route/rule/rule_default.go +++ b/route/rule/rule_default.go @@ -123,19 +123,13 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio rule.allItems = append(rule.allItems, item) } if len(options.Geosite) > 0 { - item := NewGeositeItem(router, logger, options.Geosite) - rule.destinationAddressItems = append(rule.destinationAddressItems, item) - rule.allItems = append(rule.allItems, item) + return nil, E.New("geosite database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0") } if len(options.SourceGeoIP) > 0 { - item := NewGeoIPItem(router, logger, true, options.SourceGeoIP) - rule.sourceAddressItems = append(rule.sourceAddressItems, item) - rule.allItems = append(rule.allItems, item) + return nil, E.New("geoip database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0") } if len(options.GeoIP) > 0 { - item := NewGeoIPItem(router, logger, false, options.GeoIP) - rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item) - rule.allItems = append(rule.allItems, item) + return nil, E.New("geoip database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0") } if len(options.SourceIPCIDR) > 0 { item, err := NewIPCIDRItem(true, options.SourceIPCIDR) diff --git a/route/rule/rule_dns.go b/route/rule/rule_dns.go index d6eb7104..ccd48733 100644 --- a/route/rule/rule_dns.go +++ b/route/rule/rule_dns.go @@ -114,19 +114,13 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op rule.allItems = append(rule.allItems, item) } if len(options.Geosite) > 0 { - item := NewGeositeItem(router, logger, options.Geosite) - rule.destinationAddressItems = append(rule.destinationAddressItems, item) - rule.allItems = append(rule.allItems, item) + return nil, E.New("geosite database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0") } if len(options.SourceGeoIP) > 0 { - item := NewGeoIPItem(router, logger, true, options.SourceGeoIP) - rule.sourceAddressItems = append(rule.sourceAddressItems, item) - rule.allItems = append(rule.allItems, item) + return nil, E.New("geoip database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0") } if len(options.GeoIP) > 0 { - item := NewGeoIPItem(router, logger, false, options.GeoIP) - rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item) - rule.allItems = append(rule.allItems, item) + return nil, E.New("geoip database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0") } if len(options.SourceIPCIDR) > 0 { item, err := NewIPCIDRItem(true, options.SourceIPCIDR) @@ -154,6 +148,11 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item) rule.allItems = append(rule.allItems, item) } + if options.IPAcceptAny { + item := NewIPAcceptAnyItem() + rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item) + rule.allItems = append(rule.allItems, item) + } if len(options.SourcePort) > 0 { item := NewPortItem(true, options.SourcePort) rule.sourcePortItems = append(rule.sourcePortItems, item) diff --git a/route/rule/rule_item_geoip.go b/route/rule/rule_item_geoip.go deleted file mode 100644 index 3c967fec..00000000 --- a/route/rule/rule_item_geoip.go +++ /dev/null @@ -1,98 +0,0 @@ -package rule - -import ( - "net/netip" - "strings" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - N "github.com/sagernet/sing/common/network" -) - -var _ RuleItem = (*GeoIPItem)(nil) - -type GeoIPItem struct { - router adapter.Router - logger log.ContextLogger - isSource bool - codes []string - codeMap map[string]bool -} - -func NewGeoIPItem(router adapter.Router, logger log.ContextLogger, isSource bool, codes []string) *GeoIPItem { - codeMap := make(map[string]bool) - for _, code := range codes { - codeMap[code] = true - } - return &GeoIPItem{ - router: router, - logger: logger, - codes: codes, - isSource: isSource, - codeMap: codeMap, - } -} - -func (r *GeoIPItem) Match(metadata *adapter.InboundContext) bool { - var geoipCode string - if r.isSource && metadata.SourceGeoIPCode != "" { - geoipCode = metadata.SourceGeoIPCode - } else if !r.isSource && metadata.GeoIPCode != "" { - geoipCode = metadata.GeoIPCode - } - if geoipCode != "" { - return r.codeMap[geoipCode] - } - var destination netip.Addr - if r.isSource { - destination = metadata.Source.Addr - } else { - destination = metadata.Destination.Addr - } - if destination.IsValid() { - return r.match(metadata, destination) - } - for _, destinationAddress := range metadata.DestinationAddresses { - if r.match(metadata, destinationAddress) { - return true - } - } - return false -} - -func (r *GeoIPItem) match(metadata *adapter.InboundContext, destination netip.Addr) bool { - var geoipCode string - geoReader := r.router.GeoIPReader() - if !N.IsPublicAddr(destination) { - geoipCode = "private" - } else if geoReader != nil { - geoipCode = geoReader.Lookup(destination) - } - if geoipCode == "" { - return false - } - if r.isSource { - metadata.SourceGeoIPCode = geoipCode - } else { - metadata.GeoIPCode = geoipCode - } - return r.codeMap[geoipCode] -} - -func (r *GeoIPItem) String() string { - var description string - if r.isSource { - description = "source_geoip=" - } else { - description = "geoip=" - } - cLen := len(r.codes) - if cLen == 1 { - description += r.codes[0] - } else if cLen > 3 { - description += "[" + strings.Join(r.codes[:3], " ") + "...]" - } else { - description += "[" + strings.Join(r.codes, " ") + "]" - } - return description -} diff --git a/route/rule/rule_item_geosite.go b/route/rule/rule_item_geosite.go deleted file mode 100644 index 9e5e03c8..00000000 --- a/route/rule/rule_item_geosite.go +++ /dev/null @@ -1,61 +0,0 @@ -package rule - -import ( - "strings" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - E "github.com/sagernet/sing/common/exceptions" -) - -var _ RuleItem = (*GeositeItem)(nil) - -type GeositeItem struct { - router adapter.Router - logger log.ContextLogger - codes []string - matchers []adapter.Rule -} - -func NewGeositeItem(router adapter.Router, logger log.ContextLogger, codes []string) *GeositeItem { - return &GeositeItem{ - router: router, - logger: logger, - codes: codes, - } -} - -func (r *GeositeItem) Update() error { - matchers := make([]adapter.Rule, 0, len(r.codes)) - for _, code := range r.codes { - matcher, err := r.router.LoadGeosite(code) - if err != nil { - return E.Cause(err, "read geosite") - } - matchers = append(matchers, matcher) - } - r.matchers = matchers - return nil -} - -func (r *GeositeItem) Match(metadata *adapter.InboundContext) bool { - for _, matcher := range r.matchers { - if matcher.Match(metadata) { - return true - } - } - return false -} - -func (r *GeositeItem) String() string { - description := "geosite=" - cLen := len(r.codes) - if cLen == 1 { - description += r.codes[0] - } else if cLen > 3 { - description += "[" + strings.Join(r.codes[:3], " ") + "...]" - } else { - description += "[" + strings.Join(r.codes, " ") + "]" - } - return description -} diff --git a/route/rule/rule_item_ip_accept_any.go b/route/rule/rule_item_ip_accept_any.go new file mode 100644 index 00000000..1ca71257 --- /dev/null +++ b/route/rule/rule_item_ip_accept_any.go @@ -0,0 +1,21 @@ +package rule + +import ( + "github.com/sagernet/sing-box/adapter" +) + +var _ RuleItem = (*IPAcceptAnyItem)(nil) + +type IPAcceptAnyItem struct{} + +func NewIPAcceptAnyItem() *IPAcceptAnyItem { + return &IPAcceptAnyItem{} +} + +func (r *IPAcceptAnyItem) Match(metadata *adapter.InboundContext) bool { + return len(metadata.DestinationAddresses) > 0 +} + +func (r *IPAcceptAnyItem) String() string { + return "ip_accept_any=true" +} diff --git a/route/rule_conds.go b/route/rule_conds.go index 76447176..55c4a058 100644 --- a/route/rule_conds.go +++ b/route/rule_conds.go @@ -3,7 +3,6 @@ package route import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing/common" ) func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool { @@ -38,22 +37,6 @@ func hasDNSRule(rules []option.DNSRule, cond func(rule option.DefaultDNSRule) bo return false } -func isGeoIPRule(rule option.DefaultRule) bool { - return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) || len(rule.GeoIP) > 0 && common.Any(rule.GeoIP, notPrivateNode) -} - -func isGeoIPDNSRule(rule option.DefaultDNSRule) bool { - return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) || len(rule.GeoIP) > 0 && common.Any(rule.GeoIP, notPrivateNode) -} - -func isGeositeRule(rule option.DefaultRule) bool { - return len(rule.Geosite) > 0 -} - -func isGeositeDNSRule(rule option.DefaultDNSRule) bool { - return len(rule.Geosite) > 0 -} - func isProcessRule(rule option.DefaultRule) bool { return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.ProcessPathRegex) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0 } @@ -62,10 +45,6 @@ func isProcessDNSRule(rule option.DefaultDNSRule) bool { return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.ProcessPathRegex) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0 } -func notPrivateNode(code string) bool { - return code != "private" -} - func isWIFIRule(rule option.DefaultRule) bool { return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0 } diff --git a/test/domain_inbound_test.go b/test/domain_inbound_test.go index f39cd187..605740d4 100644 --- a/test/domain_inbound_test.go +++ b/test/domain_inbound_test.go @@ -6,7 +6,6 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/json/badoption" @@ -34,7 +33,7 @@ func TestTUICDomainUDP(t *testing.T) { Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), ListenPort: serverPort, InboundOptions: option.InboundOptions{ - DomainStrategy: option.DomainStrategy(dns.DomainStrategyUseIPv6), + DomainStrategy: option.DomainStrategy(C.DomainStrategyIPv6Only), }, }, Users: []option.TUICUser{{ diff --git a/transport/fakeip/server.go b/transport/fakeip/server.go deleted file mode 100644 index d1bbb2aa..00000000 --- a/transport/fakeip/server.go +++ /dev/null @@ -1,95 +0,0 @@ -package fakeip - -import ( - "context" - "net/netip" - "os" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-dns" - E "github.com/sagernet/sing/common/exceptions" - "github.com/sagernet/sing/common/logger" - "github.com/sagernet/sing/service" - - mDNS "github.com/miekg/dns" -) - -var ( - _ dns.Transport = (*Transport)(nil) - _ adapter.FakeIPTransport = (*Transport)(nil) -) - -func init() { - dns.RegisterTransport([]string{"fakeip"}, func(options dns.TransportOptions) (dns.Transport, error) { - return NewTransport(options) - }) -} - -type Transport struct { - name string - router adapter.Router - store adapter.FakeIPStore - logger logger.ContextLogger -} - -func NewTransport(options dns.TransportOptions) (*Transport, error) { - router := service.FromContext[adapter.Router](options.Context) - if router == nil { - return nil, E.New("missing router in context") - } - return &Transport{ - name: options.Name, - router: router, - logger: options.Logger, - }, nil -} - -func (s *Transport) Name() string { - return s.name -} - -func (s *Transport) Start() error { - s.store = s.router.FakeIPStore() - if s.store == nil { - return E.New("fakeip not enabled") - } - return nil -} - -func (s *Transport) Reset() { -} - -func (s *Transport) Close() error { - return nil -} - -func (s *Transport) Raw() bool { - return false -} - -func (s *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { - return nil, os.ErrInvalid -} - -func (s *Transport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { - var addresses []netip.Addr - if strategy != dns.DomainStrategyUseIPv6 { - inet4Address, err := s.store.Create(domain, false) - if err != nil { - return nil, err - } - addresses = append(addresses, inet4Address) - } - if strategy != dns.DomainStrategyUseIPv4 { - inet6Address, err := s.store.Create(domain, true) - if err != nil { - return nil, err - } - addresses = append(addresses, inet6Address) - } - return addresses, nil -} - -func (s *Transport) Store() adapter.FakeIPStore { - return s.store -} diff --git a/transport/v2rayhttp/server.go b/transport/v2rayhttp/server.go index dd2bc9a2..4b830ae2 100644 --- a/transport/v2rayhttp/server.go +++ b/transport/v2rayhttp/server.go @@ -164,7 +164,7 @@ func (s *Server) Serve(listener net.Listener) error { if len(s.tlsConfig.NextProtos()) == 0 { s.tlsConfig.SetNextProtos([]string{http2.NextProtoTLS, "http/1.1"}) } else if !common.Contains(s.tlsConfig.NextProtos(), http2.NextProtoTLS) { - s.tlsConfig.SetNextProtos(append([]string{"h2"}, s.tlsConfig.NextProtos()...)) + s.tlsConfig.SetNextProtos(append([]string{http2.NextProtoTLS}, s.tlsConfig.NextProtos()...)) } listener = aTLS.NewListener(listener, s.tlsConfig) } From 9e4dfcf644603c31596a19b222ee8c5183c3730a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 12 Jan 2025 12:45:27 +0800 Subject: [PATCH 18/94] refactor: Outbound domain resolver --- adapter/network.go | 14 +-- box.go | 6 +- common/dialer/default.go | 7 -- common/dialer/detour.go | 16 ++-- common/dialer/dialer.go | 130 ++++++++++++++++++--------- common/dialer/resolve.go | 113 ++++++++++++++++++----- common/tls/reality_server.go | 2 +- dns/transport_adapter.go | 12 ++- dns/transport_dialer.go | 50 ++++++----- experimental/deprecated/constants.go | 26 +++++- option/dns.go | 50 ++++++----- option/outbound.go | 40 ++++++++- option/route.go | 1 + protocol/direct/outbound.go | 12 ++- protocol/http/outbound.go | 2 +- protocol/hysteria/outbound.go | 2 +- protocol/hysteria2/outbound.go | 2 +- protocol/shadowsocks/outbound.go | 2 +- protocol/shadowtls/inbound.go | 4 +- protocol/shadowtls/outbound.go | 2 +- protocol/socks/outbound.go | 2 +- protocol/ssh/outbound.go | 2 +- protocol/tor/outbound.go | 2 +- protocol/trojan/outbound.go | 2 +- protocol/tuic/outbound.go | 2 +- protocol/vless/outbound.go | 2 +- protocol/vmess/outbound.go | 2 +- protocol/wireguard/endpoint.go | 13 ++- protocol/wireguard/outbound.go | 13 ++- route/network.go | 13 ++- route/rule/rule_action.go | 2 +- route/rule/rule_dns.go | 2 +- route/rule/rule_item_outbound.go | 9 +- 33 files changed, 392 insertions(+), 167 deletions(-) diff --git a/adapter/network.go b/adapter/network.go index 3adfaaeb..50670831 100644 --- a/adapter/network.go +++ b/adapter/network.go @@ -29,12 +29,14 @@ type NetworkManager interface { } type NetworkOptions struct { - NetworkStrategy *C.NetworkStrategy - NetworkType []C.InterfaceType - FallbackNetworkType []C.InterfaceType - FallbackDelay time.Duration - BindInterface string - RoutingMark uint32 + BindInterface string + RoutingMark uint32 + DomainResolver string + DomainResolveOptions DNSQueryOptions + NetworkStrategy *C.NetworkStrategy + NetworkType []C.InterfaceType + FallbackNetworkType []C.InterfaceType + FallbackDelay time.Duration } type InterfaceUpdateListener interface { diff --git a/box.go b/box.go index e810b771..3050f37c 100644 --- a/box.go +++ b/box.go @@ -187,7 +187,7 @@ func New(options Options) (*Box, error) { transportOptions.Options, ) if err != nil { - return nil, E.Cause(err, "initialize inbound[", i, "]") + return nil, E.Cause(err, "initialize DNS server[", i, "]") } } err = dnsRouter.Initialize(dnsOptions.Rules) @@ -217,7 +217,7 @@ func New(options Options) (*Box, error) { endpointOptions.Options, ) if err != nil { - return nil, E.Cause(err, "initialize inbound[", i, "]") + return nil, E.Cause(err, "initialize endpoint[", i, "]") } } for i, inboundOptions := range options.Inbounds { @@ -316,7 +316,7 @@ func New(options Options) (*Box, error) { } } if ntpOptions.Enabled { - ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions) + ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions, ntpOptions.ServerIsDomain()) if err != nil { return nil, E.Cause(err, "create NTP service") } diff --git a/common/dialer/default.go b/common/dialer/default.go index bd8d9018..0bc956ec 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -35,7 +35,6 @@ type DefaultDialer struct { udpListener net.ListenConfig udpAddr4 string udpAddr6 string - isWireGuardListener bool networkManager adapter.NetworkManager networkStrategy *C.NetworkStrategy defaultNetworkStrategy bool @@ -183,11 +182,6 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial } setMultiPathTCP(&dialer4) } - if options.IsWireGuardListener { - for _, controlFn := range WgControlFns { - listener.Control = control.Append(listener.Control, controlFn) - } - } tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen) if err != nil { return nil, err @@ -204,7 +198,6 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial udpListener: listener, udpAddr4: udpAddr4, udpAddr6: udpAddr6, - isWireGuardListener: options.IsWireGuardListener, networkManager: networkManager, networkStrategy: networkStrategy, defaultNetworkStrategy: defaultNetworkStrategy, diff --git a/common/dialer/detour.go b/common/dialer/detour.go index c1d40faa..e4a46049 100644 --- a/common/dialer/detour.go +++ b/common/dialer/detour.go @@ -29,16 +29,18 @@ func (d *DetourDialer) Start() error { } func (d *DetourDialer) Dialer() (N.Dialer, error) { - d.initOnce.Do(func() { - var loaded bool - d.dialer, loaded = d.outboundManager.Outbound(d.detour) - if !loaded { - d.initErr = E.New("outbound detour not found: ", d.detour) - } - }) + d.initOnce.Do(d.init) return d.dialer, d.initErr } +func (d *DetourDialer) init() { + var loaded bool + d.dialer, loaded = d.outboundManager.Outbound(d.detour) + if !loaded { + d.initErr = E.New("outbound detour not found: ", d.detour) + } +} + func (d *DetourDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { dialer, err := d.Dialer() if err != nil { diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index f63e3864..812e5575 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -8,6 +8,7 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/experimental/deprecated" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" @@ -15,60 +16,105 @@ import ( "github.com/sagernet/sing/service" ) -func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) { - if options.IsWireGuardListener { - return NewDefault(ctx, options) - } +type Options struct { + Context context.Context + Options option.DialerOptions + RemoteIsDomain bool + DirectResolver bool + ResolverOnDetour bool +} + +// TODO: merge with NewWithOptions +func New(ctx context.Context, options option.DialerOptions, remoteIsDomain bool) (N.Dialer, error) { + return NewWithOptions(Options{ + Context: ctx, + Options: options, + RemoteIsDomain: remoteIsDomain, + }) +} + +func NewWithOptions(options Options) (N.Dialer, error) { + dialOptions := options.Options var ( dialer N.Dialer err error ) - if options.Detour == "" { - dialer, err = NewDefault(ctx, options) - if err != nil { - return nil, err - } - } else { - outboundManager := service.FromContext[adapter.OutboundManager](ctx) + if dialOptions.Detour != "" { + outboundManager := service.FromContext[adapter.OutboundManager](options.Context) if outboundManager == nil { return nil, E.New("missing outbound manager") } - dialer = NewDetour(outboundManager, options.Detour) - } - if options.Detour == "" { - router := service.FromContext[adapter.DNSRouter](ctx) - if router != nil { - dialer = NewResolveDialer( - router, - dialer, - options.Detour == "" && !options.TCPFastOpen, - C.DomainStrategy(options.DomainStrategy), - time.Duration(options.FallbackDelay)) + dialer = NewDetour(outboundManager, dialOptions.Detour) + } else { + dialer, err = NewDefault(options.Context, dialOptions) + if err != nil { + return nil, err } } + if options.RemoteIsDomain && (dialOptions.Detour == "" || options.ResolverOnDetour) { + networkManager := service.FromContext[adapter.NetworkManager](options.Context) + dnsTransport := service.FromContext[adapter.DNSTransportManager](options.Context) + var defaultOptions adapter.NetworkOptions + if networkManager != nil { + defaultOptions = networkManager.DefaultOptions() + } + var ( + server string + dnsQueryOptions adapter.DNSQueryOptions + resolveFallbackDelay time.Duration + ) + if dialOptions.DomainResolver != nil && dialOptions.DomainResolver.Server != "" { + var transport adapter.DNSTransport + if !options.DirectResolver { + var loaded bool + transport, loaded = dnsTransport.Transport(dialOptions.DomainResolver.Server) + if !loaded { + return nil, E.New("domain resolver not found: " + dialOptions.DomainResolver.Server) + } + } + var strategy C.DomainStrategy + if dialOptions.DomainResolver.Strategy != option.DomainStrategy(C.DomainStrategyAsIS) { + strategy = C.DomainStrategy(dialOptions.DomainResolver.Strategy) + } else if + //nolint:staticcheck + dialOptions.DomainStrategy != option.DomainStrategy(C.DomainStrategyAsIS) { + //nolint:staticcheck + strategy = C.DomainStrategy(dialOptions.DomainStrategy) + } + server = dialOptions.DomainResolver.Server + dnsQueryOptions = adapter.DNSQueryOptions{ + Transport: transport, + Strategy: strategy, + DisableCache: dialOptions.DomainResolver.DisableCache, + RewriteTTL: dialOptions.DomainResolver.RewriteTTL, + ClientSubnet: dialOptions.DomainResolver.ClientSubnet.Build(netip.Prefix{}), + } + resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay) + } else if options.DirectResolver { + return nil, E.New("missing domain resolver for domain server address") + } else if defaultOptions.DomainResolver != "" { + dnsQueryOptions = defaultOptions.DomainResolveOptions + transport, loaded := dnsTransport.Transport(defaultOptions.DomainResolver) + if !loaded { + return nil, E.New("default domain resolver not found: " + defaultOptions.DomainResolver) + } + dnsQueryOptions.Transport = transport + resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay) + } else { + deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) + } + dialer = NewResolveDialer( + options.Context, + dialer, + dialOptions.Detour == "" && !dialOptions.TCPFastOpen, + server, + dnsQueryOptions, + resolveFallbackDelay, + ) + } return dialer, nil } -func NewDirect(ctx context.Context, options option.DialerOptions) (ParallelInterfaceDialer, error) { - if options.Detour != "" { - return nil, E.New("`detour` is not supported in direct context") - } - if options.IsWireGuardListener { - return NewDefault(ctx, options) - } - dialer, err := NewDefault(ctx, options) - if err != nil { - return nil, err - } - return NewResolveParallelInterfaceDialer( - service.FromContext[adapter.DNSRouter](ctx), - dialer, - true, - C.DomainStrategy(options.DomainStrategy), - time.Duration(options.FallbackDelay), - ), nil -} - type ParallelInterfaceDialer interface { N.Dialer DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) diff --git a/common/dialer/resolve.go b/common/dialer/resolve.go index 3d667a6c..66b74e3c 100644 --- a/common/dialer/resolve.go +++ b/common/dialer/resolve.go @@ -3,14 +3,17 @@ package dialer import ( "context" "net" + "sync" "time" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing/common/bufio" + E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" ) var ( @@ -18,21 +21,37 @@ var ( _ ParallelInterfaceDialer = (*resolveParallelNetworkDialer)(nil) ) +type ResolveDialer interface { + N.Dialer + QueryOptions() adapter.DNSQueryOptions +} + +type ParallelInterfaceResolveDialer interface { + ParallelInterfaceDialer + QueryOptions() adapter.DNSQueryOptions +} + type resolveDialer struct { + transport adapter.DNSTransportManager + router adapter.DNSRouter dialer N.Dialer parallel bool - router adapter.DNSRouter - strategy C.DomainStrategy + server string + initOnce sync.Once + initErr error + queryOptions adapter.DNSQueryOptions fallbackDelay time.Duration } -func NewResolveDialer(router adapter.DNSRouter, dialer N.Dialer, parallel bool, strategy C.DomainStrategy, fallbackDelay time.Duration) N.Dialer { +func NewResolveDialer(ctx context.Context, dialer N.Dialer, parallel bool, server string, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ResolveDialer { return &resolveDialer{ - dialer, - parallel, - router, - strategy, - fallbackDelay, + transport: service.FromContext[adapter.DNSTransportManager](ctx), + router: service.FromContext[adapter.DNSRouter](ctx), + dialer: dialer, + parallel: parallel, + server: server, + queryOptions: queryOptions, + fallbackDelay: fallbackDelay, } } @@ -41,41 +60,68 @@ type resolveParallelNetworkDialer struct { dialer ParallelInterfaceDialer } -func NewResolveParallelInterfaceDialer(router adapter.DNSRouter, dialer ParallelInterfaceDialer, parallel bool, strategy C.DomainStrategy, fallbackDelay time.Duration) ParallelInterfaceDialer { +func NewResolveParallelInterfaceDialer(ctx context.Context, dialer ParallelInterfaceDialer, parallel bool, server string, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ParallelInterfaceResolveDialer { return &resolveParallelNetworkDialer{ resolveDialer{ - dialer, - parallel, - router, - strategy, - fallbackDelay, + transport: service.FromContext[adapter.DNSTransportManager](ctx), + router: service.FromContext[adapter.DNSRouter](ctx), + dialer: dialer, + parallel: parallel, + server: server, + queryOptions: queryOptions, + fallbackDelay: fallbackDelay, }, dialer, } } +func (d *resolveDialer) initialize() error { + d.initOnce.Do(d.initServer) + return d.initErr +} + +func (d *resolveDialer) initServer() { + if d.server == "" { + return + } + transport, loaded := d.transport.Transport(d.server) + if !loaded { + d.initErr = E.New("domain resolver not found: " + d.server) + return + } + d.queryOptions.Transport = transport +} + func (d *resolveDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + err := d.initialize() + if err != nil { + return nil, err + } if !destination.IsFqdn() { return d.dialer.DialContext(ctx, network, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) + addresses, err := d.router.Lookup(ctx, destination.Fqdn, d.queryOptions) if err != nil { return nil, err } if d.parallel { - return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, d.fallbackDelay) + return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.queryOptions.Strategy == C.DomainStrategyPreferIPv6, d.fallbackDelay) } else { return N.DialSerial(ctx, d.dialer, network, destination, addresses) } } func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + err := d.initialize() + if err != nil { + return nil, err + } if !destination.IsFqdn() { return d.dialer.ListenPacket(ctx, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) + addresses, err := d.router.Lookup(ctx, destination.Fqdn, d.queryOptions) if err != nil { return nil, err } @@ -86,14 +132,24 @@ func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil } +func (d *resolveDialer) QueryOptions() adapter.DNSQueryOptions { + return d.queryOptions +} + +func (d *resolveDialer) Upstream() any { + return d.dialer +} + func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) { + err := d.initialize() + if err != nil { + return nil, err + } if !destination.IsFqdn() { return d.dialer.DialContext(ctx, network, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{ - Strategy: d.strategy, - }) + addresses, err := d.router.Lookup(ctx, destination.Fqdn, d.queryOptions) if err != nil { return nil, err } @@ -101,21 +157,28 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context fallbackDelay = d.fallbackDelay } if d.parallel { - return DialParallelNetwork(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) + return DialParallelNetwork(ctx, d.dialer, network, destination, addresses, d.queryOptions.Strategy == C.DomainStrategyPreferIPv6, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) } else { return DialSerialNetwork(ctx, d.dialer, network, destination, addresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) } } func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) { + err := d.initialize() + if err != nil { + return nil, err + } if !destination.IsFqdn() { return d.dialer.ListenPacket(ctx, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) + addresses, err := d.router.Lookup(ctx, destination.Fqdn, d.queryOptions) if err != nil { return nil, err } + if fallbackDelay == 0 { + fallbackDelay = d.fallbackDelay + } conn, destinationAddress, err := ListenSerialNetworkPacket(ctx, d.dialer, destination, addresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) if err != nil { return nil, err @@ -123,6 +186,10 @@ func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.C return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil } -func (d *resolveDialer) Upstream() any { +func (d *resolveParallelNetworkDialer) QueryOptions() adapter.DNSQueryOptions { + return d.queryOptions +} + +func (d *resolveParallelNetworkDialer) Upstream() any { return d.dialer } diff --git a/common/tls/reality_server.go b/common/tls/reality_server.go index 48a17e63..84b0979d 100644 --- a/common/tls/reality_server.go +++ b/common/tls/reality_server.go @@ -105,7 +105,7 @@ func NewRealityServer(ctx context.Context, logger log.Logger, options option.Inb } } - handshakeDialer, err := dialer.New(ctx, options.Reality.Handshake.DialerOptions) + handshakeDialer, err := dialer.New(ctx, options.Reality.Handshake.DialerOptions, options.Reality.Handshake.ServerIsDomain()) if err != nil { return nil, err } diff --git a/dns/transport_adapter.go b/dns/transport_adapter.go index 02c84621..47345709 100644 --- a/dns/transport_adapter.go +++ b/dns/transport_adapter.go @@ -27,9 +27,14 @@ func NewTransportAdapter(transportType string, transportTag string, dependencies } func NewTransportAdapterWithLocalOptions(transportType string, transportTag string, localOptions option.LocalDNSServerOptions) TransportAdapter { + var dependencies []string + if localOptions.DomainResolver != nil && localOptions.DomainResolver.Server != "" { + dependencies = append(dependencies, localOptions.DomainResolver.Server) + } return TransportAdapter{ transportType: transportType, transportTag: transportTag, + dependencies: dependencies, strategy: C.DomainStrategy(localOptions.LegacyStrategy), clientSubnet: localOptions.LegacyClientSubnet, } @@ -37,8 +42,11 @@ func NewTransportAdapterWithLocalOptions(transportType string, transportTag stri func NewTransportAdapterWithRemoteOptions(transportType string, transportTag string, remoteOptions option.RemoteDNSServerOptions) TransportAdapter { var dependencies []string - if remoteOptions.AddressResolver != "" { - dependencies = []string{remoteOptions.AddressResolver} + if remoteOptions.DomainResolver != nil && remoteOptions.DomainResolver.Server != "" { + dependencies = append(dependencies, remoteOptions.DomainResolver.Server) + } + if remoteOptions.LegacyAddressResolver != "" { + dependencies = append(dependencies, remoteOptions.LegacyAddressResolver) } return TransportAdapter{ transportType: transportType, diff --git a/dns/transport_dialer.go b/dns/transport_dialer.go index 14e1188d..0b15c7ea 100644 --- a/dns/transport_dialer.go +++ b/dns/transport_dialer.go @@ -19,29 +19,39 @@ func NewLocalDialer(ctx context.Context, options option.LocalDNSServerOptions) ( if options.LegacyDefaultDialer { return dialer.NewDefaultOutbound(ctx), nil } else { - return dialer.New(ctx, options.DialerOptions) + return dialer.NewWithOptions(dialer.Options{ + Context: ctx, + Options: options.DialerOptions, + DirectResolver: true, + }) } } func NewRemoteDialer(ctx context.Context, options option.RemoteDNSServerOptions) (N.Dialer, error) { - transportDialer, err := NewLocalDialer(ctx, options.LocalDNSServerOptions) - if err != nil { - return nil, err - } - if options.AddressResolver != "" { - transport := service.FromContext[adapter.DNSTransportManager](ctx) - resolverTransport, loaded := transport.Transport(options.AddressResolver) - if !loaded { - return nil, E.New("address resolver not found: ", options.AddressResolver) + if options.LegacyDefaultDialer { + transportDialer := dialer.NewDefaultOutbound(ctx) + if options.LegacyAddressResolver != "" { + transport := service.FromContext[adapter.DNSTransportManager](ctx) + resolverTransport, loaded := transport.Transport(options.LegacyAddressResolver) + if !loaded { + return nil, E.New("address resolver not found: ", options.LegacyAddressResolver) + } + transportDialer = newTransportDialer(transportDialer, service.FromContext[adapter.DNSRouter](ctx), resolverTransport, C.DomainStrategy(options.LegacyAddressStrategy), time.Duration(options.LegacyAddressFallbackDelay)) + } else if options.ServerIsDomain() { + return nil, E.New("missing address resolver for server: ", options.Server) } - transportDialer = NewTransportDialer(transportDialer, service.FromContext[adapter.DNSRouter](ctx), resolverTransport, C.DomainStrategy(options.AddressStrategy), time.Duration(options.AddressFallbackDelay)) - } else if M.IsDomainName(options.Server) { - return nil, E.New("missing address resolver for server: ", options.Server) + return transportDialer, nil + } else { + return dialer.NewWithOptions(dialer.Options{ + Context: ctx, + Options: options.DialerOptions, + RemoteIsDomain: options.ServerIsDomain(), + DirectResolver: true, + }) } - return transportDialer, nil } -type TransportDialer struct { +type legacyTransportDialer struct { dialer N.Dialer dnsRouter adapter.DNSRouter transport adapter.DNSTransport @@ -49,8 +59,8 @@ type TransportDialer struct { fallbackDelay time.Duration } -func NewTransportDialer(dialer N.Dialer, dnsRouter adapter.DNSRouter, transport adapter.DNSTransport, strategy C.DomainStrategy, fallbackDelay time.Duration) *TransportDialer { - return &TransportDialer{ +func newTransportDialer(dialer N.Dialer, dnsRouter adapter.DNSRouter, transport adapter.DNSTransport, strategy C.DomainStrategy, fallbackDelay time.Duration) *legacyTransportDialer { + return &legacyTransportDialer{ dialer, dnsRouter, transport, @@ -59,7 +69,7 @@ func NewTransportDialer(dialer N.Dialer, dnsRouter adapter.DNSRouter, transport } } -func (d *TransportDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (d *legacyTransportDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if destination.IsIP() { return d.dialer.DialContext(ctx, network, destination) } @@ -73,7 +83,7 @@ func (d *TransportDialer) DialContext(ctx context.Context, network string, desti return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, d.fallbackDelay) } -func (d *TransportDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (d *legacyTransportDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { if destination.IsIP() { return d.dialer.ListenPacket(ctx, destination) } @@ -88,6 +98,6 @@ func (d *TransportDialer) ListenPacket(ctx context.Context, destination M.Socksa return conn, err } -func (d *TransportDialer) Upstream() any { +func (d *legacyTransportDialer) Upstream() any { return d.dialer } diff --git a/experimental/deprecated/constants.go b/experimental/deprecated/constants.go index bf648365..d5c3ca48 100644 --- a/experimental/deprecated/constants.go +++ b/experimental/deprecated/constants.go @@ -148,10 +148,11 @@ var OptionTUNGSO = Note{ var OptionLegacyDNSTransport = Note{ Name: "legacy-dns-transport", - Description: "legacy DNS transport", + Description: "legacy DNS servers", DeprecatedVersion: "1.12.0", ScheduledVersion: "1.14.0", - EnvName: "LEGACY_DNS_TRANSPORT", + EnvName: "LEGACY_DNS_SERVERS", + MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-to-new-dns-server-formats", } var OptionLegacyDNSFakeIPOptions = Note{ @@ -159,6 +160,23 @@ var OptionLegacyDNSFakeIPOptions = Note{ Description: "legacy DNS fakeip options", DeprecatedVersion: "1.12.0", ScheduledVersion: "1.14.0", + MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-to-new-dns-server-formats", +} + +var OptionOutboundDNSRuleItem = Note{ + Name: "outbound-dns-rule-item", + Description: "outbound DNS rule item", + DeprecatedVersion: "1.12.0", + ScheduledVersion: "1.14.0", + MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-outbound-dns-rule-items-to-domain-resolver", +} + +var OptionMissingDomainResolver = Note{ + Name: "missing-domain-resolver", + Description: "missing `route.default_domain_resolver` or `domain_resolver` in dial fields", + DeprecatedVersion: "1.12.0", + ScheduledVersion: "1.14.0", + MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-outbound-dns-rule-items-to-domain-resolver", } var Options = []Note{ @@ -172,4 +190,8 @@ var Options = []Note{ OptionWireGuardOutbound, OptionWireGuardGSO, OptionTUNGSO, + OptionLegacyDNSTransport, + OptionLegacyDNSFakeIPOptions, + OptionOutboundDNSRuleItem, + OptionMissingDomainResolver, } diff --git a/option/dns.go b/option/dns.go index 8c9b8bda..f4418468 100644 --- a/option/dns.go +++ b/option/dns.go @@ -4,7 +4,6 @@ import ( "context" "net/netip" "net/url" - "os" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/experimental/deprecated" @@ -121,11 +120,6 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { if o.Type != C.DNSTypeLegacy { return nil } - defer func() { - encoder := json.NewEncoder(os.Stderr) - encoder.SetIndent("", " ") - encoder.Encode(o) - }() options := o.Options.(*LegacyDNSServerOptions) serverURL, _ := url.Parse(options.Address) var serverType string @@ -139,18 +133,34 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { serverType = C.DNSTypeUDP } } - remoteOptions := RemoteDNSServerOptions{ - LocalDNSServerOptions: LocalDNSServerOptions{ - DialerOptions: DialerOptions{ - Detour: options.Detour, + var remoteOptions RemoteDNSServerOptions + if options.Detour == "" { + remoteOptions = RemoteDNSServerOptions{ + LocalDNSServerOptions: LocalDNSServerOptions{ + LegacyStrategy: options.Strategy, + LegacyDefaultDialer: options.Detour == "", + LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}), }, - LegacyStrategy: options.Strategy, - LegacyDefaultDialer: options.Detour == "", - LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}), - }, - AddressResolver: options.AddressResolver, - AddressStrategy: options.AddressStrategy, - AddressFallbackDelay: options.AddressFallbackDelay, + LegacyAddressResolver: options.AddressResolver, + LegacyAddressStrategy: options.AddressStrategy, + LegacyAddressFallbackDelay: options.AddressFallbackDelay, + } + } else { + remoteOptions = RemoteDNSServerOptions{ + LocalDNSServerOptions: LocalDNSServerOptions{ + DialerOptions: DialerOptions{ + Detour: options.Detour, + DomainResolver: &DomainResolveOptions{ + Server: options.AddressResolver, + Strategy: options.AddressStrategy, + }, + FallbackDelay: options.AddressFallbackDelay, + }, + LegacyStrategy: options.Strategy, + LegacyDefaultDialer: options.Detour == "", + LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}), + }, + } } switch serverType { case C.DNSTypeLocal: @@ -291,9 +301,9 @@ type LocalDNSServerOptions struct { type RemoteDNSServerOptions struct { LocalDNSServerOptions ServerOptions - AddressResolver string `json:"address_resolver,omitempty"` - AddressStrategy DomainStrategy `json:"address_strategy,omitempty"` - AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"` + LegacyAddressResolver string `json:"-"` + LegacyAddressStrategy DomainStrategy `json:"-"` + LegacyAddressFallbackDelay badoption.Duration `json:"-"` } type RemoteTLSDNSServerOptions struct { diff --git a/option/outbound.go b/option/outbound.go index 5cadd3e2..99e3361a 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -77,12 +77,46 @@ type DialerOptions struct { TCPMultiPath bool `json:"tcp_multi_path,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragmentDefault bool `json:"-"` - DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` + DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"` NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"` NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"` FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"` IsWireGuardListener bool `json:"-"` + + // Deprecated: migrated to domain resolver + DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` +} + +type _DomainResolveOptions struct { + Server string `json:"server"` + Strategy DomainStrategy `json:"strategy,omitempty"` + DisableCache bool `json:"disable_cache,omitempty"` + RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` + ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` +} + +type DomainResolveOptions _DomainResolveOptions + +func (o DomainResolveOptions) MarshalJSON() ([]byte, error) { + if o.Strategy == DomainStrategy(C.DomainStrategyAsIS) && + !o.DisableCache && + o.RewriteTTL == nil && + o.ClientSubnet == nil { + return json.Marshal(o.Server) + } else { + return json.Marshal((_DomainResolveOptions)(o)) + } +} + +func (o *DomainResolveOptions) UnmarshalJSON(bytes []byte) error { + var stringValue string + err := json.Unmarshal(bytes, &stringValue) + if err == nil { + o.Server = stringValue + return nil + } + return json.Unmarshal(bytes, (*_DomainResolveOptions)(o)) } func (o *DialerOptions) TakeDialerOptions() DialerOptions { @@ -107,6 +141,10 @@ func (o ServerOptions) Build() M.Socksaddr { return M.ParseSocksaddrHostPort(o.Server, o.ServerPort) } +func (o ServerOptions) ServerIsDomain() bool { + return M.IsDomainName(o.Server) +} + func (o *ServerOptions) TakeServerOptions() ServerOptions { return *o } diff --git a/option/route.go b/option/route.go index 1eb2294b..f4b65391 100644 --- a/option/route.go +++ b/option/route.go @@ -13,6 +13,7 @@ type RouteOptions struct { OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"` DefaultInterface string `json:"default_interface,omitempty"` DefaultMark FwMark `json:"default_mark,omitempty"` + DefaultDomainResolver *DomainResolveOptions `json:"default_domain_resolver,omitempty"` DefaultNetworkStrategy *NetworkStrategy `json:"default_network_strategy,omitempty"` DefaultNetworkType badoption.Listable[InterfaceType] `json:"default_network_type,omitempty"` DefaultFallbackNetworkType badoption.Listable[InterfaceType] `json:"default_fallback_network_type,omitempty"` diff --git a/protocol/direct/outbound.go b/protocol/direct/outbound.go index d173ec53..9b454f58 100644 --- a/protocol/direct/outbound.go +++ b/protocol/direct/outbound.go @@ -42,16 +42,20 @@ type Outbound struct { func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (adapter.Outbound, error) { options.UDPFragmentDefault = true - outboundDialer, err := dialer.NewDirect(ctx, options.DialerOptions) + if options.Detour != "" { + return nil, E.New("`detour` is not supported in direct context") + } + outboundDialer, err := dialer.New(ctx, options.DialerOptions, false) if err != nil { return nil, err } outbound := &Outbound{ - Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions), - logger: logger, + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions), + logger: logger, + //nolint:staticcheck domainStrategy: C.DomainStrategy(options.DomainStrategy), fallbackDelay: time.Duration(options.FallbackDelay), - dialer: outboundDialer, + dialer: outboundDialer.(dialer.ParallelInterfaceDialer), // loopBack: newLoopBackDetector(router), } //nolint:staticcheck diff --git a/protocol/http/outbound.go b/protocol/http/outbound.go index c58f3071..0570dde5 100644 --- a/protocol/http/outbound.go +++ b/protocol/http/outbound.go @@ -30,7 +30,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/hysteria/outbound.go b/protocol/hysteria/outbound.go index e1d8716c..7746df13 100644 --- a/protocol/hysteria/outbound.go +++ b/protocol/hysteria/outbound.go @@ -47,7 +47,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/hysteria2/outbound.go b/protocol/hysteria2/outbound.go index 74e87b37..c805f07e 100644 --- a/protocol/hysteria2/outbound.go +++ b/protocol/hysteria2/outbound.go @@ -60,7 +60,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL return nil, E.New("unknown obfs type: ", options.Obfs.Type) } } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/shadowsocks/outbound.go b/protocol/shadowsocks/outbound.go index 7e7277ef..875c9e69 100644 --- a/protocol/shadowsocks/outbound.go +++ b/protocol/shadowsocks/outbound.go @@ -44,7 +44,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/shadowtls/inbound.go b/protocol/shadowtls/inbound.go index 5ae5656f..1db191d8 100644 --- a/protocol/shadowtls/inbound.go +++ b/protocol/shadowtls/inbound.go @@ -47,7 +47,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo if options.Version > 1 { handshakeForServerName = make(map[string]shadowtls.HandshakeConfig) for serverName, serverOptions := range options.HandshakeForServerName { - handshakeDialer, err := dialer.New(ctx, serverOptions.DialerOptions) + handshakeDialer, err := dialer.New(ctx, serverOptions.DialerOptions, serverOptions.ServerIsDomain()) if err != nil { return nil, err } @@ -57,7 +57,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo } } } - handshakeDialer, err := dialer.New(ctx, options.Handshake.DialerOptions) + handshakeDialer, err := dialer.New(ctx, options.Handshake.DialerOptions, options.Handshake.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/shadowtls/outbound.go b/protocol/shadowtls/outbound.go index 2b480729..0731b033 100644 --- a/protocol/shadowtls/outbound.go +++ b/protocol/shadowtls/outbound.go @@ -68,7 +68,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL tlsHandshakeFunc = shadowtls.DefaultTLSHandshakeFunc(options.Password, stdTLSConfig) } } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/socks/outbound.go b/protocol/socks/outbound.go index 323149e2..851412ff 100644 --- a/protocol/socks/outbound.go +++ b/protocol/socks/outbound.go @@ -46,7 +46,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/ssh/outbound.go b/protocol/ssh/outbound.go index b329d4b3..aeb43d34 100644 --- a/protocol/ssh/outbound.go +++ b/protocol/ssh/outbound.go @@ -50,7 +50,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SSHOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/tor/outbound.go b/protocol/tor/outbound.go index 58824b53..9a0e2d65 100644 --- a/protocol/tor/outbound.go +++ b/protocol/tor/outbound.go @@ -75,7 +75,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL } startConf.TorrcFile = torrcFile } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, false) if err != nil { return nil, err } diff --git a/protocol/trojan/outbound.go b/protocol/trojan/outbound.go index 82889bc1..37a6933c 100644 --- a/protocol/trojan/outbound.go +++ b/protocol/trojan/outbound.go @@ -38,7 +38,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/tuic/outbound.go b/protocol/tuic/outbound.go index 49b01f96..a31d4850 100644 --- a/protocol/tuic/outbound.go +++ b/protocol/tuic/outbound.go @@ -60,7 +60,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL case "quic": tuicUDPStream = true } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/vless/outbound.go b/protocol/vless/outbound.go index 1d832a65..e0208be9 100644 --- a/protocol/vless/outbound.go +++ b/protocol/vless/outbound.go @@ -41,7 +41,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/vmess/outbound.go b/protocol/vmess/outbound.go index d41b30d9..be05990e 100644 --- a/protocol/vmess/outbound.go +++ b/protocol/vmess/outbound.go @@ -41,7 +41,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index 1dead5c9..29acbf12 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -48,7 +48,14 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL if options.Detour == "" { options.IsWireGuardListener = true } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.NewWithOptions(dialer.Options{ + Context: ctx, + Options: options.DialerOptions, + RemoteIsDomain: common.Any(options.Peers, func(it option.WireGuardPeer) bool { + return !M.ParseAddr(it.Address).IsValid() + }), + ResolverOnDetour: true, + }) if err != nil { return nil, err } @@ -76,9 +83,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL PrivateKey: options.PrivateKey, ListenPort: options.ListenPort, ResolvePeer: func(domain string) (netip.Addr, error) { - endpointAddresses, lookupErr := ep.dnsRouter.Lookup(ctx, domain, adapter.DNSQueryOptions{ - Strategy: C.DomainStrategy(options.DomainStrategy), - }) + endpointAddresses, lookupErr := ep.dnsRouter.Lookup(ctx, domain, outboundDialer.(dialer.ResolveDialer).QueryOptions()) if lookupErr != nil { return netip.Addr{}, lookupErr } diff --git a/protocol/wireguard/outbound.go b/protocol/wireguard/outbound.go index 47e1dac5..1e7a32c7 100644 --- a/protocol/wireguard/outbound.go +++ b/protocol/wireguard/outbound.go @@ -51,7 +51,14 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL } else if options.GSO { return nil, E.New("gso is conflict with detour") } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.NewWithOptions(dialer.Options{ + Context: ctx, + Options: options.DialerOptions, + RemoteIsDomain: options.ServerIsDomain() || common.Any(options.Peers, func(it option.LegacyWireGuardPeer) bool { + return it.ServerIsDomain() + }), + ResolverOnDetour: true, + }) if err != nil { return nil, err } @@ -89,9 +96,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL Address: options.LocalAddress, PrivateKey: options.PrivateKey, ResolvePeer: func(domain string) (netip.Addr, error) { - endpointAddresses, lookupErr := outbound.dnsRouter.Lookup(ctx, domain, adapter.DNSQueryOptions{ - Strategy: C.DomainStrategy(options.DomainStrategy), - }) + endpointAddresses, lookupErr := outbound.dnsRouter.Lookup(ctx, domain, outboundDialer.(dialer.ResolveDialer).QueryOptions()) if lookupErr != nil { return netip.Addr{}, lookupErr } diff --git a/route/network.go b/route/network.go index ab1be76c..a494ce09 100644 --- a/route/network.go +++ b/route/network.go @@ -4,6 +4,7 @@ import ( "context" "errors" "net" + "net/netip" "os" "runtime" "strings" @@ -55,13 +56,21 @@ type NetworkManager struct { } func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOptions option.RouteOptions) (*NetworkManager, error) { + defaultDomainResolver := common.PtrValueOrDefault(routeOptions.DefaultDomainResolver) nm := &NetworkManager{ logger: logger, interfaceFinder: control.NewDefaultInterfaceFinder(), autoDetectInterface: routeOptions.AutoDetectInterface, defaultOptions: adapter.NetworkOptions{ - BindInterface: routeOptions.DefaultInterface, - RoutingMark: uint32(routeOptions.DefaultMark), + BindInterface: routeOptions.DefaultInterface, + RoutingMark: uint32(routeOptions.DefaultMark), + DomainResolver: defaultDomainResolver.Server, + DomainResolveOptions: adapter.DNSQueryOptions{ + Strategy: C.DomainStrategy(defaultDomainResolver.Strategy), + DisableCache: defaultDomainResolver.DisableCache, + RewriteTTL: defaultDomainResolver.RewriteTTL, + ClientSubnet: defaultDomainResolver.ClientSubnet.Build(netip.Prefix{}), + }, NetworkStrategy: (*C.NetworkStrategy)(routeOptions.DefaultNetworkStrategy), NetworkType: common.Map(routeOptions.DefaultNetworkType, option.InterfaceType.Build), FallbackNetworkType: common.Map(routeOptions.DefaultFallbackNetworkType, option.InterfaceType.Build), diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index 2c4f977f..5476678a 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -49,7 +49,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout), }, nil case C.RuleActionTypeDirect: - directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions)) + directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions), false) if err != nil { return nil, err } diff --git a/route/rule/rule_dns.go b/route/rule/rule_dns.go index ccd48733..30442abf 100644 --- a/route/rule/rule_dns.go +++ b/route/rule/rule_dns.go @@ -213,7 +213,7 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op rule.allItems = append(rule.allItems, item) } if len(options.Outbound) > 0 { - item := NewOutboundRule(options.Outbound) + item := NewOutboundRule(ctx, options.Outbound) rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) } diff --git a/route/rule/rule_item_outbound.go b/route/rule/rule_item_outbound.go index 3f37dee7..a13d0597 100644 --- a/route/rule/rule_item_outbound.go +++ b/route/rule/rule_item_outbound.go @@ -1,9 +1,11 @@ package rule import ( + "context" "strings" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/experimental/deprecated" F "github.com/sagernet/sing/common/format" ) @@ -15,7 +17,8 @@ type OutboundItem struct { matchAny bool } -func NewOutboundRule(outbounds []string) *OutboundItem { +func NewOutboundRule(ctx context.Context, outbounds []string) *OutboundItem { + deprecated.Report(ctx, deprecated.OptionOutboundDNSRuleItem) rule := &OutboundItem{outbounds: outbounds, outboundMap: make(map[string]bool)} for _, outbound := range outbounds { if outbound == "any" { @@ -28,8 +31,8 @@ func NewOutboundRule(outbounds []string) *OutboundItem { } func (r *OutboundItem) Match(metadata *adapter.InboundContext) bool { - if r.matchAny && metadata.Outbound != "" { - return true + if r.matchAny { + return metadata.Outbound != "" } return r.outboundMap[metadata.Outbound] } From 493bc247d79fe7a2d15523ad7ec327179066a923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 26 Jan 2025 09:01:00 +0800 Subject: [PATCH 19/94] Add TLS fragment support --- adapter/inbound.go | 2 + common/process/searcher.go | 1 + common/process/searcher_windows.go | 191 ++--------------------------- common/tlsfragment/conn.go | 107 ++++++++++++++++ common/tlsfragment/index.go | 131 ++++++++++++++++++++ common/tlsfragment/wait_darwin.go | 93 ++++++++++++++ common/tlsfragment/wait_linux.go | 40 ++++++ common/tlsfragment/wait_stub.go | 14 +++ common/tlsfragment/wait_windows.go | 28 +++++ constant/timeout.go | 1 + go.mod | 2 +- go.sum | 4 +- option/rule_action.go | 3 + route/conn.go | 16 +++ route/route.go | 4 + route/rule/rule_action.go | 12 ++ transport/simple-obfs/http.go | 4 + transport/simple-obfs/tls.go | 4 + 18 files changed, 476 insertions(+), 181 deletions(-) create mode 100644 common/tlsfragment/conn.go create mode 100644 common/tlsfragment/index.go create mode 100644 common/tlsfragment/wait_darwin.go create mode 100644 common/tlsfragment/wait_linux.go create mode 100644 common/tlsfragment/wait_stub.go create mode 100644 common/tlsfragment/wait_windows.go diff --git a/adapter/inbound.go b/adapter/inbound.go index 11085099..1218c049 100644 --- a/adapter/inbound.go +++ b/adapter/inbound.go @@ -72,6 +72,8 @@ type InboundContext struct { UDPDisableDomainUnmapping bool UDPConnect bool UDPTimeout time.Duration + TLSFragment bool + TLSFragmentFallbackDelay time.Duration NetworkStrategy *C.NetworkStrategy NetworkType []C.InterfaceType diff --git a/common/process/searcher.go b/common/process/searcher.go index cee81068..d525b3c1 100644 --- a/common/process/searcher.go +++ b/common/process/searcher.go @@ -23,6 +23,7 @@ type Config struct { } type Info struct { + ProcessID uint32 ProcessPath string PackageName string User string diff --git a/common/process/searcher_windows.go b/common/process/searcher_windows.go index 6ea7c709..b7d89dda 100644 --- a/common/process/searcher_windows.go +++ b/common/process/searcher_windows.go @@ -2,14 +2,11 @@ package process import ( "context" - "fmt" "net/netip" - "os" "syscall" - "unsafe" E "github.com/sagernet/sing/common/exceptions" - N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/winiphlpapi" "golang.org/x/sys/windows" ) @@ -26,201 +23,39 @@ func NewSearcher(_ Config) (Searcher, error) { return &windowsSearcher{}, nil } -var ( - modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") - procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable") - procGetExtendedUdpTable = modiphlpapi.NewProc("GetExtendedUdpTable") - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW") -) - func initWin32API() error { - err := modiphlpapi.Load() - if err != nil { - return E.Cause(err, "load iphlpapi.dll") - } - - err = procGetExtendedTcpTable.Find() - if err != nil { - return E.Cause(err, "load iphlpapi::GetExtendedTcpTable") - } - - err = procGetExtendedUdpTable.Find() - if err != nil { - return E.Cause(err, "load iphlpapi::GetExtendedUdpTable") - } - - err = modkernel32.Load() - if err != nil { - return E.Cause(err, "load kernel32.dll") - } - - err = procQueryFullProcessImageNameW.Find() - if err != nil { - return E.Cause(err, "load kernel32::QueryFullProcessImageNameW") - } - - return nil + return winiphlpapi.LoadExtendedTable() } func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) { - processName, err := findProcessName(network, source.Addr(), int(source.Port())) + pid, err := winiphlpapi.FindPid(network, source) if err != nil { return nil, err } - return &Info{ProcessPath: processName, UserId: -1}, nil -} - -func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) { - family := windows.AF_INET - if ip.Is6() { - family = windows.AF_INET6 - } - - const ( - tcpTablePidConn = 4 - udpTablePid = 1 - ) - - var class int - var fn uintptr - switch network { - case N.NetworkTCP: - fn = procGetExtendedTcpTable.Addr() - class = tcpTablePidConn - case N.NetworkUDP: - fn = procGetExtendedUdpTable.Addr() - class = udpTablePid - default: - return "", os.ErrInvalid - } - - buf, err := getTransportTable(fn, family, class) + path, err := getProcessPath(pid) if err != nil { - return "", err + return &Info{ProcessID: pid, UserId: -1}, err } - - s := newSearcher(family == windows.AF_INET, network == N.NetworkTCP) - - pid, err := s.Search(buf, ip, uint16(srcPort)) - if err != nil { - return "", err - } - return getExecPathFromPID(pid) + return &Info{ProcessID: pid, ProcessPath: path, UserId: -1}, nil } -type searcher struct { - itemSize int - port int - ip int - ipSize int - pid int - tcpState int -} - -func (s *searcher) Search(b []byte, ip netip.Addr, port uint16) (uint32, error) { - n := int(readNativeUint32(b[:4])) - itemSize := s.itemSize - for i := 0; i < n; i++ { - row := b[4+itemSize*i : 4+itemSize*(i+1)] - - // according to MSDN, only the lower 16 bits of dwLocalPort are used and the port number is in network endian. - // this field can be illustrated as follows depends on different machine endianess: - // little endian: [ MSB LSB 0 0 ] interpret as native uint32 is ((LSB<<8)|MSB) - // big endian: [ 0 0 MSB LSB ] interpret as native uint32 is ((MSB<<8)|LSB) - // so we need an syscall.Ntohs on the lower 16 bits after read the port as native uint32 - srcPort := syscall.Ntohs(uint16(readNativeUint32(row[s.port : s.port+4]))) - if srcPort != port { - continue - } - - srcIP, _ := netip.AddrFromSlice(row[s.ip : s.ip+s.ipSize]) - // windows binds an unbound udp socket to 0.0.0.0/[::] while first sendto - if ip != srcIP && (!srcIP.IsUnspecified()) { - continue - } - - pid := readNativeUint32(row[s.pid : s.pid+4]) - return pid, nil - } - return 0, ErrNotFound -} - -func newSearcher(isV4, isTCP bool) *searcher { - var itemSize, port, ip, ipSize, pid int - tcpState := -1 - switch { - case isV4 && isTCP: - // struct MIB_TCPROW_OWNER_PID - itemSize, port, ip, ipSize, pid, tcpState = 24, 8, 4, 4, 20, 0 - case isV4 && !isTCP: - // struct MIB_UDPROW_OWNER_PID - itemSize, port, ip, ipSize, pid = 12, 4, 0, 4, 8 - case !isV4 && isTCP: - // struct MIB_TCP6ROW_OWNER_PID - itemSize, port, ip, ipSize, pid, tcpState = 56, 20, 0, 16, 52, 48 - case !isV4 && !isTCP: - // struct MIB_UDP6ROW_OWNER_PID - itemSize, port, ip, ipSize, pid = 28, 20, 0, 16, 24 - } - - return &searcher{ - itemSize: itemSize, - port: port, - ip: ip, - ipSize: ipSize, - pid: pid, - tcpState: tcpState, - } -} - -func getTransportTable(fn uintptr, family int, class int) ([]byte, error) { - for size, buf := uint32(8), make([]byte, 8); ; { - ptr := unsafe.Pointer(&buf[0]) - err, _, _ := syscall.SyscallN(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) - - switch err { - case 0: - return buf, nil - case uintptr(syscall.ERROR_INSUFFICIENT_BUFFER): - buf = make([]byte, size) - default: - return nil, fmt.Errorf("syscall error: %d", err) - } - } -} - -func readNativeUint32(b []byte) uint32 { - return *(*uint32)(unsafe.Pointer(&b[0])) -} - -func getExecPathFromPID(pid uint32) (string, error) { - // kernel process starts with a colon in order to distinguish with normal processes +func getProcessPath(pid uint32) (string, error) { switch pid { case 0: - // reserved pid for system idle process return ":System Idle Process", nil case 4: - // reserved pid for windows kernel image return ":System", nil } - h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid) + handle, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid) if err != nil { return "", err } - defer windows.CloseHandle(h) - + defer windows.CloseHandle(handle) + size := uint32(syscall.MAX_LONG_PATH) buf := make([]uint16, syscall.MAX_LONG_PATH) - size := uint32(len(buf)) - r1, _, err := syscall.SyscallN( - procQueryFullProcessImageNameW.Addr(), - uintptr(h), - uintptr(0), - uintptr(unsafe.Pointer(&buf[0])), - uintptr(unsafe.Pointer(&size)), - ) - if r1 == 0 { + err = windows.QueryFullProcessImageName(handle, 0, &buf[0], &size) + if err != nil { return "", err } - return syscall.UTF16ToString(buf[:size]), nil + return windows.UTF16ToString(buf[:size]), nil } diff --git a/common/tlsfragment/conn.go b/common/tlsfragment/conn.go new file mode 100644 index 00000000..6f2a3dad --- /dev/null +++ b/common/tlsfragment/conn.go @@ -0,0 +1,107 @@ +package tf + +import ( + "context" + "math/rand" + "net" + "strings" + "time" + + N "github.com/sagernet/sing/common/network" + + "golang.org/x/net/publicsuffix" +) + +type Conn struct { + net.Conn + tcpConn *net.TCPConn + ctx context.Context + firstPacketWritten bool + fallbackDelay time.Duration +} + +func NewConn(conn net.Conn, ctx context.Context, fallbackDelay time.Duration) (*Conn, error) { + tcpConn, _ := N.UnwrapReader(conn).(*net.TCPConn) + return &Conn{ + Conn: conn, + tcpConn: tcpConn, + ctx: ctx, + fallbackDelay: fallbackDelay, + }, nil +} + +func (c *Conn) Write(b []byte) (n int, err error) { + if !c.firstPacketWritten { + defer func() { + c.firstPacketWritten = true + }() + serverName := indexTLSServerName(b) + if serverName != nil { + if c.tcpConn != nil { + err = c.tcpConn.SetNoDelay(true) + if err != nil { + return + } + } + splits := strings.Split(serverName.ServerName, ".") + currentIndex := serverName.Index + if publicSuffix := publicsuffix.List.PublicSuffix(serverName.ServerName); publicSuffix != "" { + splits = splits[:len(splits)-strings.Count(serverName.ServerName, ".")] + } + if len(splits) > 1 && splits[0] == "..." { + currentIndex += len(splits[0]) + 1 + splits = splits[1:] + } + var splitIndexes []int + for i, split := range splits { + splitAt := rand.Intn(len(split)) + splitIndexes = append(splitIndexes, currentIndex+splitAt) + currentIndex += len(split) + if i != len(splits)-1 { + currentIndex++ + } + } + for i := 0; i <= len(splitIndexes); i++ { + var payload []byte + if i == 0 { + payload = b[:splitIndexes[i]] + } else if i == len(splitIndexes) { + payload = b[splitIndexes[i-1]:] + } else { + payload = b[splitIndexes[i-1]:splitIndexes[i]] + } + if c.tcpConn != nil && i != len(splitIndexes) { + err = writeAndWaitAck(c.ctx, c.tcpConn, payload, c.fallbackDelay) + if err != nil { + return + } + } else { + _, err = c.Conn.Write(payload) + if err != nil { + return + } + } + } + if c.tcpConn != nil { + err = c.tcpConn.SetNoDelay(false) + if err != nil { + return + } + } + return len(b), nil + } + } + return c.Conn.Write(b) +} + +func (c *Conn) ReaderReplaceable() bool { + return true +} + +func (c *Conn) WriterReplaceable() bool { + return c.firstPacketWritten +} + +func (c *Conn) Upstream() any { + return c.Conn +} diff --git a/common/tlsfragment/index.go b/common/tlsfragment/index.go new file mode 100644 index 00000000..59031cec --- /dev/null +++ b/common/tlsfragment/index.go @@ -0,0 +1,131 @@ +package tf + +import ( + "encoding/binary" +) + +const ( + recordLayerHeaderLen int = 5 + handshakeHeaderLen int = 6 + randomDataLen int = 32 + sessionIDHeaderLen int = 1 + cipherSuiteHeaderLen int = 2 + compressMethodHeaderLen int = 1 + extensionsHeaderLen int = 2 + extensionHeaderLen int = 4 + sniExtensionHeaderLen int = 5 + contentType uint8 = 22 + handshakeType uint8 = 1 + sniExtensionType uint16 = 0 + sniNameDNSHostnameType uint8 = 0 + tlsVersionBitmask uint16 = 0xFFFC + tls13 uint16 = 0x0304 +) + +type myServerName struct { + Index int + Length int + ServerName string +} + +func indexTLSServerName(payload []byte) *myServerName { + if len(payload) < recordLayerHeaderLen || payload[0] != contentType { + return nil + } + segmentLen := binary.BigEndian.Uint16(payload[3:5]) + if len(payload) < recordLayerHeaderLen+int(segmentLen) { + return nil + } + serverName := indexTLSServerNameFromHandshake(payload[recordLayerHeaderLen : recordLayerHeaderLen+int(segmentLen)]) + if serverName == nil { + return nil + } + serverName.Length += recordLayerHeaderLen + return serverName +} + +func indexTLSServerNameFromHandshake(hs []byte) *myServerName { + if len(hs) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen { + return nil + } + if hs[0] != handshakeType { + return nil + } + handshakeLen := uint32(hs[1])<<16 | uint32(hs[2])<<8 | uint32(hs[3]) + if len(hs[4:]) != int(handshakeLen) { + return nil + } + tlsVersion := uint16(hs[4])<<8 | uint16(hs[5]) + if tlsVersion&tlsVersionBitmask != 0x0300 && tlsVersion != tls13 { + return nil + } + sessionIDLen := hs[38] + if len(hs) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen+int(sessionIDLen) { + return nil + } + cs := hs[handshakeHeaderLen+randomDataLen+sessionIDHeaderLen+int(sessionIDLen):] + if len(cs) < cipherSuiteHeaderLen { + return nil + } + csLen := uint16(cs[0])<<8 | uint16(cs[1]) + if len(cs) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen { + return nil + } + compressMethodLen := uint16(cs[cipherSuiteHeaderLen+int(csLen)]) + if len(cs) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen+int(compressMethodLen) { + return nil + } + currentIndex := cipherSuiteHeaderLen + int(csLen) + compressMethodHeaderLen + int(compressMethodLen) + serverName := indexTLSServerNameFromExtensions(cs[currentIndex:]) + if serverName == nil { + return nil + } + serverName.Index += currentIndex + return serverName +} + +func indexTLSServerNameFromExtensions(exs []byte) *myServerName { + if len(exs) == 0 { + return nil + } + if len(exs) < extensionsHeaderLen { + return nil + } + exsLen := uint16(exs[0])<<8 | uint16(exs[1]) + exs = exs[extensionsHeaderLen:] + if len(exs) < int(exsLen) { + return nil + } + for currentIndex := extensionsHeaderLen; len(exs) > 0; { + if len(exs) < extensionHeaderLen { + return nil + } + exType := uint16(exs[0])<<8 | uint16(exs[1]) + exLen := uint16(exs[2])<<8 | uint16(exs[3]) + if len(exs) < extensionHeaderLen+int(exLen) { + return nil + } + sex := exs[extensionHeaderLen : extensionHeaderLen+int(exLen)] + + switch exType { + case sniExtensionType: + if len(sex) < sniExtensionHeaderLen { + return nil + } + sniType := sex[2] + if sniType != sniNameDNSHostnameType { + return nil + } + sniLen := uint16(sex[3])<<8 | uint16(sex[4]) + sex = sex[sniExtensionHeaderLen:] + return &myServerName{ + Index: currentIndex + extensionHeaderLen + sniExtensionHeaderLen, + Length: int(sniLen), + ServerName: string(sex), + } + } + exs = exs[4+exLen:] + currentIndex += 4 + int(exLen) + } + return nil +} diff --git a/common/tlsfragment/wait_darwin.go b/common/tlsfragment/wait_darwin.go new file mode 100644 index 00000000..90c65ba2 --- /dev/null +++ b/common/tlsfragment/wait_darwin.go @@ -0,0 +1,93 @@ +package tf + +import ( + "context" + "net" + "time" + + "github.com/sagernet/sing/common/control" + + "golang.org/x/sys/unix" +) + +/* +const tcpMaxNotifyAck = 10 + +type tcpNotifyAckID uint32 + + type tcpNotifyAckComplete struct { + NotifyPending uint32 + NotifyCompleteCount uint32 + NotifyCompleteID [tcpMaxNotifyAck]tcpNotifyAckID + } + +var sizeOfTCPNotifyAckComplete = int(unsafe.Sizeof(tcpNotifyAckComplete{})) + + func getsockoptTCPNotifyAckComplete(fd, level, opt int) (*tcpNotifyAckComplete, error) { + var value tcpNotifyAckComplete + vallen := uint32(sizeOfTCPNotifyAckComplete) + err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen) + return &value, err + } + +//go:linkname getsockopt golang.org/x/sys/unix.getsockopt +func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *uint32) error + + func waitAck(ctx context.Context, conn *net.TCPConn, _ time.Duration) error { + const TCP_NOTIFY_ACKNOWLEDGEMENT = 0x212 + return control.Conn(conn, func(fd uintptr) error { + err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, TCP_NOTIFY_ACKNOWLEDGEMENT, 1) + if err != nil { + if errors.Is(err, unix.EINVAL) { + return waitAckFallback(ctx, conn, 0) + } + return err + } + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + var ackComplete *tcpNotifyAckComplete + ackComplete, err = getsockoptTCPNotifyAckComplete(int(fd), unix.IPPROTO_TCP, TCP_NOTIFY_ACKNOWLEDGEMENT) + if err != nil { + return err + } + if ackComplete.NotifyPending == 0 { + return nil + } + time.Sleep(10 * time.Millisecond) + } + }) + } +*/ + +func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error { + _, err := conn.Write(payload) + if err != nil { + return err + } + return control.Conn(conn, func(fd uintptr) error { + start := time.Now() + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + unacked, err := unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_NWRITE) + if err != nil { + return err + } + if unacked == 0 { + if time.Since(start) <= 20*time.Millisecond { + // under transparent proxy + time.Sleep(fallbackDelay) + } + return nil + } + time.Sleep(10 * time.Millisecond) + } + }) +} diff --git a/common/tlsfragment/wait_linux.go b/common/tlsfragment/wait_linux.go new file mode 100644 index 00000000..517d6ea5 --- /dev/null +++ b/common/tlsfragment/wait_linux.go @@ -0,0 +1,40 @@ +package tf + +import ( + "context" + "net" + "time" + + "github.com/sagernet/sing/common/control" + + "golang.org/x/sys/unix" +) + +func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error { + _, err := conn.Write(payload) + if err != nil { + return err + } + return control.Conn(conn, func(fd uintptr) error { + start := time.Now() + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + tcpInfo, err := unix.GetsockoptTCPInfo(int(fd), unix.IPPROTO_TCP, unix.TCP_INFO) + if err != nil { + return err + } + if tcpInfo.Unacked == 0 { + if time.Since(start) <= 20*time.Millisecond { + // under transparent proxy + time.Sleep(fallbackDelay) + } + return nil + } + time.Sleep(10 * time.Millisecond) + } + }) +} diff --git a/common/tlsfragment/wait_stub.go b/common/tlsfragment/wait_stub.go new file mode 100644 index 00000000..7e451a04 --- /dev/null +++ b/common/tlsfragment/wait_stub.go @@ -0,0 +1,14 @@ +//go:build !(linux || darwin || windows) + +package tf + +import ( + "context" + "net" + "time" +) + +func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error { + time.Sleep(fallbackDelay) + return nil +} diff --git a/common/tlsfragment/wait_windows.go b/common/tlsfragment/wait_windows.go new file mode 100644 index 00000000..118a204d --- /dev/null +++ b/common/tlsfragment/wait_windows.go @@ -0,0 +1,28 @@ +package tf + +import ( + "context" + "errors" + "net" + "time" + + "github.com/sagernet/sing/common/winiphlpapi" + + "golang.org/x/sys/windows" +) + +func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error { + start := time.Now() + err := winiphlpapi.WriteAndWaitAck(ctx, conn, payload) + if err != nil { + if errors.Is(err, windows.ERROR_ACCESS_DENIED) { + time.Sleep(fallbackDelay) + return nil + } + return err + } + if time.Since(start) <= 20*time.Millisecond { + time.Sleep(fallbackDelay) + } + return nil +} diff --git a/constant/timeout.go b/constant/timeout.go index 3b5a452b..eb0fd34c 100644 --- a/constant/timeout.go +++ b/constant/timeout.go @@ -16,6 +16,7 @@ const ( StopTimeout = 5 * time.Second FatalStopTimeout = 10 * time.Second FakeIPMetadataSaveInterval = 10 * time.Second + TLSFragmentFallbackDelay = 500 * time.Millisecond ) var PortProtocols = map[uint16]string{ diff --git a/go.mod b/go.mod index 0572fe40..ac86825a 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff github.com/sagernet/quic-go v0.49.0-beta.1 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.6.7 + github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a github.com/sagernet/sing-mux v0.3.1 github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 github.com/sagernet/sing-shadowsocks v0.2.7 diff --git a/go.sum b/go.sum index 9725f6be..4f2a8eeb 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing v0.6.7 h1:NIWBLZ9AUWDXAQBKGleKwsitbQrI9M0nqoheXhUKnrI= -github.com/sagernet/sing v0.6.7/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a h1:oE67hmp5rzLlE6clE7FpK4Hg6yLXsa1Zu3A01vcazb0= +github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI= github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78= github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 h1:iwpCX6H3nZEOGUGwx0q5azcgYOA9f6v9YssihXoRKHk= diff --git a/option/rule_action.go b/option/rule_action.go index a715d260..f07d7298 100644 --- a/option/rule_action.go +++ b/option/rule_action.go @@ -150,6 +150,9 @@ type RawRouteOptionsActionOptions struct { UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"` UDPConnect bool `json:"udp_connect,omitempty"` UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"` + + TLSFragment bool `json:"tls_fragment,omitempty"` + TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"` } type RouteOptionsActionOptions RawRouteOptionsActionOptions diff --git a/route/conn.go b/route/conn.go index 319e463c..582562eb 100644 --- a/route/conn.go +++ b/route/conn.go @@ -13,6 +13,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/sing-box/common/tlsfragment" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" @@ -78,6 +79,21 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co m.logger.ErrorContext(ctx, err) return } + if metadata.TLSFragment { + fallbackDelay := metadata.TLSFragmentFallbackDelay + if fallbackDelay == 0 { + fallbackDelay = C.TLSFragmentFallbackDelay + } + var newConn *tf.Conn + newConn, err = tf.NewConn(remoteConn, ctx, fallbackDelay) + if err != nil { + conn.Close() + remoteConn.Close() + m.logger.ErrorContext(ctx, err) + return + } + remoteConn = newConn + } m.access.Lock() element := m.connections.PushBack(conn) m.access.Unlock() diff --git a/route/route.go b/route/route.go index ec76318f..ae78fcec 100644 --- a/route/route.go +++ b/route/route.go @@ -448,6 +448,10 @@ match: if routeOptions.UDPTimeout > 0 { metadata.UDPTimeout = routeOptions.UDPTimeout } + if routeOptions.TLSFragment { + metadata.TLSFragment = true + metadata.TLSFragmentFallbackDelay = routeOptions.TLSFragmentFallbackDelay + } } switch action := currentRule.Action().(type) { case *rule.RuleActionSniff: diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index 5476678a..1cb3089d 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -36,6 +36,8 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti FallbackDelay: time.Duration(action.RouteOptions.FallbackDelay), UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping, UDPConnect: action.RouteOptions.UDPConnect, + TLSFragment: action.RouteOptions.TLSFragment, + TLSFragmentFallbackDelay: time.Duration(action.RouteOptions.TLSFragmentFallbackDelay), }, }, nil case C.RuleActionTypeRouteOptions: @@ -47,6 +49,8 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti UDPDisableDomainUnmapping: action.RouteOptionsOptions.UDPDisableDomainUnmapping, UDPConnect: action.RouteOptionsOptions.UDPConnect, UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout), + TLSFragment: action.RouteOptionsOptions.TLSFragment, + TLSFragmentFallbackDelay: time.Duration(action.RouteOptionsOptions.TLSFragmentFallbackDelay), }, nil case C.RuleActionTypeDirect: directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions), false) @@ -142,6 +146,9 @@ func (r *RuleActionRoute) String() string { if r.UDPConnect { descriptions = append(descriptions, "udp-connect") } + if r.TLSFragment { + descriptions = append(descriptions, "tls-fragment") + } return F.ToString("route(", strings.Join(descriptions, ","), ")") } @@ -155,6 +162,8 @@ type RuleActionRouteOptions struct { UDPDisableDomainUnmapping bool UDPConnect bool UDPTimeout time.Duration + TLSFragment bool + TLSFragmentFallbackDelay time.Duration } func (r *RuleActionRouteOptions) Type() string { @@ -187,6 +196,9 @@ func (r *RuleActionRouteOptions) String() string { if r.UDPConnect { descriptions = append(descriptions, "udp-connect") } + if r.UDPTimeout > 0 { + descriptions = append(descriptions, "udp-timeout") + } return F.ToString("route-options(", strings.Join(descriptions, ","), ")") } diff --git a/transport/simple-obfs/http.go b/transport/simple-obfs/http.go index f77a63a8..df38768e 100644 --- a/transport/simple-obfs/http.go +++ b/transport/simple-obfs/http.go @@ -82,6 +82,10 @@ func (ho *HTTPObfs) Write(b []byte) (int, error) { return ho.Conn.Write(b) } +func (ho *HTTPObfs) Upstream() any { + return ho.Conn +} + // NewHTTPObfs return a HTTPObfs func NewHTTPObfs(conn net.Conn, host string, port string) net.Conn { return &HTTPObfs{ diff --git a/transport/simple-obfs/tls.go b/transport/simple-obfs/tls.go index 51756fdb..96564815 100644 --- a/transport/simple-obfs/tls.go +++ b/transport/simple-obfs/tls.go @@ -113,6 +113,10 @@ func (to *TLSObfs) write(b []byte) (int, error) { return len(b), err } +func (to *TLSObfs) Upstream() any { + return to.Conn +} + // NewTLSObfs return a SimpleObfs func NewTLSObfs(conn net.Conn, server string) net.Conn { return &TLSObfs{ From ea8aeb04f02843d55ac93da13eb7056e2c212181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 8 Jan 2025 10:34:45 +0800 Subject: [PATCH 20/94] Add certificate store --- Makefile | 3 + adapter/certificate.go | 21 + adapter/experimental.go | 16 +- adapter/router.go | 10 +- box.go | 16 +- cmd/internal/update_certificates/main.go | 68 + common/certificate/mozilla.go | 4359 +++++++++++++++++++++ common/certificate/store.go | 185 + common/tls/ech_client.go | 1 + common/tls/ech_server.go | 2 +- common/tls/reality_client.go | 14 +- common/tls/std_client.go | 2 + common/tls/std_server.go | 2 +- common/tls/utls_client.go | 2 + common/urltest/urltest.go | 20 +- constant/certificate.go | 7 + experimental/clashapi/api_meta_group.go | 2 +- experimental/clashapi/proxies.go | 8 +- experimental/clashapi/server.go | 6 +- experimental/clashapi/server_resources.go | 6 + experimental/libbox/command_urltest.go | 2 +- experimental/libbox/config.go | 4 + experimental/libbox/platform.go | 1 + experimental/libbox/platform/interface.go | 1 + experimental/libbox/service.go | 6 +- option/certificate.go | 36 + option/options.go | 1 + protocol/group/urltest.go | 9 +- route/router.go | 2 +- route/rule/rule_set_remote.go | 6 + 30 files changed, 4786 insertions(+), 32 deletions(-) create mode 100644 adapter/certificate.go create mode 100644 cmd/internal/update_certificates/main.go create mode 100644 common/certificate/mozilla.go create mode 100644 common/certificate/store.go create mode 100644 constant/certificate.go create mode 100644 option/certificate.go diff --git a/Makefile b/Makefile index ea633673..8ae443d4 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,9 @@ proto_install: go install -v google.golang.org/protobuf/cmd/protoc-gen-go@latest go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest +update_certificates: + go run ./cmd/internal/update_certificates + release: go run ./cmd/internal/build goreleaser release --clean --skip publish mkdir dist/release diff --git a/adapter/certificate.go b/adapter/certificate.go new file mode 100644 index 00000000..0998e130 --- /dev/null +++ b/adapter/certificate.go @@ -0,0 +1,21 @@ +package adapter + +import ( + "context" + "crypto/x509" + + "github.com/sagernet/sing/service" +) + +type CertificateStore interface { + LifecycleService + Pool() *x509.CertPool +} + +func RootPoolFromContext(ctx context.Context) *x509.CertPool { + store := service.FromContext[CertificateStore](ctx) + if store == nil { + return nil + } + return store.Pool() +} diff --git a/adapter/experimental.go b/adapter/experimental.go index 99d7c9a5..de01d7be 100644 --- a/adapter/experimental.go +++ b/adapter/experimental.go @@ -6,7 +6,6 @@ import ( "encoding/binary" "time" - "github.com/sagernet/sing-box/common/urltest" "github.com/sagernet/sing/common/varbin" ) @@ -15,7 +14,20 @@ type ClashServer interface { ConnectionTracker Mode() string ModeList() []string - HistoryStorage() *urltest.HistoryStorage + HistoryStorage() URLTestHistoryStorage +} + +type URLTestHistory struct { + Time time.Time `json:"time"` + Delay uint16 `json:"delay"` +} + +type URLTestHistoryStorage interface { + SetHook(hook chan<- struct{}) + LoadURLTestHistory(tag string) *URLTestHistory + DeleteURLTestHistory(tag string) + StoreURLTestHistory(tag string, history *URLTestHistory) + Close() error } type V2RayServer interface { diff --git a/adapter/router.go b/adapter/router.go index 45e4f723..0b7c8f4f 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -2,6 +2,7 @@ package adapter import ( "context" + "crypto/tls" "net" "net/http" "sync" @@ -9,6 +10,7 @@ import ( C "github.com/sagernet/sing-box/constant" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/common/x/list" "go4.org/netipx" @@ -66,12 +68,14 @@ type RuleSetMetadata struct { ContainsIPCIDRRule bool } type HTTPStartContext struct { + ctx context.Context access sync.Mutex httpClientCache map[string]*http.Client } -func NewHTTPStartContext() *HTTPStartContext { +func NewHTTPStartContext(ctx context.Context) *HTTPStartContext { return &HTTPStartContext{ + ctx: ctx, httpClientCache: make(map[string]*http.Client), } } @@ -89,6 +93,10 @@ func (c *HTTPStartContext) HTTPClient(detour string, dialer N.Dialer) *http.Clie DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) }, + TLSClientConfig: &tls.Config{ + Time: ntp.TimeFuncFromContext(c.ctx), + RootCAs: RootPoolFromContext(c.ctx), + }, }, } c.httpClientCache[detour] = httpClient diff --git a/box.go b/box.go index 3050f37c..db900a7a 100644 --- a/box.go +++ b/box.go @@ -12,6 +12,7 @@ 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/common/certificate" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/taskmonitor" "github.com/sagernet/sing-box/common/tls" @@ -141,6 +142,20 @@ func New(options Options) (*Box, error) { return nil, E.Cause(err, "create log factory") } + var services []adapter.LifecycleService + certificateOptions := common.PtrValueOrDefault(options.Certificate) + if C.IsAndroid || certificateOptions.Store != "" && certificateOptions.Store != C.CertificateStoreSystem || + len(certificateOptions.Certificate) > 0 || + len(certificateOptions.CertificatePath) > 0 || + len(certificateOptions.CertificateDirectoryPath) > 0 { + certificateStore, err := certificate.NewStore(ctx, logFactory.NewLogger("certificate"), certificateOptions) + if err != nil { + return nil, err + } + service.MustRegister[adapter.CertificateStore](ctx, certificateStore) + services = append(services, certificateStore) + } + routeOptions := common.PtrValueOrDefault(options.Route) dnsOptions := common.PtrValueOrDefault(options.DNS) endpointManager := endpoint.NewManager(logFactory.NewLogger("endpoint"), endpointRegistry) @@ -287,7 +302,6 @@ func New(options Options) (*Box, error) { return nil, E.Cause(err, "initialize platform interface") } } - var services []adapter.LifecycleService if needCacheFile { cacheFile := cachefile.New(ctx, common.PtrValueOrDefault(experimentalOptions.CacheFile)) service.MustRegister[adapter.CacheFile](ctx, cacheFile) diff --git a/cmd/internal/update_certificates/main.go b/cmd/internal/update_certificates/main.go new file mode 100644 index 00000000..cf597c86 --- /dev/null +++ b/cmd/internal/update_certificates/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "encoding/csv" + "io" + "net/http" + "os" + "strings" + + "github.com/sagernet/sing-box/log" + + "golang.org/x/exp/slices" +) + +func main() { + err := updateMozillaIncludedRootCAs() + if err != nil { + log.Error(err) + } +} + +func updateMozillaIncludedRootCAs() error { + response, err := http.Get("https://ccadb.my.salesforce-sites.com/mozilla/IncludedCACertificateReportPEMCSV") + if err != nil { + return err + } + defer response.Body.Close() + reader := csv.NewReader(response.Body) + header, err := reader.Read() + if err != nil { + return err + } + geoIndex := slices.Index(header, "Geographic Focus") + nameIndex := slices.Index(header, "Common Name or Certificate Name") + certIndex := slices.Index(header, "PEM Info") + + generated := strings.Builder{} + generated.WriteString(`// Code generated by 'make update_certificates'. DO NOT EDIT. + +package certificate + +import "crypto/x509" + +var mozillaIncluded *x509.CertPool + +func init() { + mozillaIncluded = x509.NewCertPool() +`) + for { + record, err := reader.Read() + if err == io.EOF { + break + } else if err != nil { + return err + } + if record[geoIndex] == "China" { + continue + } + generated.WriteString("\n // ") + generated.WriteString(record[nameIndex]) + generated.WriteString("\n") + generated.WriteString(" mozillaIncluded.AppendCertsFromPEM([]byte(`") + generated.WriteString(record[certIndex]) + generated.WriteString("`))\n") + } + generated.WriteString("}\n") + return os.WriteFile("common/certificate/mozilla.go", []byte(generated.String()), 0o644) +} diff --git a/common/certificate/mozilla.go b/common/certificate/mozilla.go new file mode 100644 index 00000000..3bf1d175 --- /dev/null +++ b/common/certificate/mozilla.go @@ -0,0 +1,4359 @@ +// Code generated by 'make update_certificates'. DO NOT EDIT. + +package certificate + +import "crypto/x509" + +var mozillaIncluded *x509.CertPool + +func init() { + mozillaIncluded = x509.NewCertPool() + + // Actalis Authentication Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE-----'`)) + + // TunTrust Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg +Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv +b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG +EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u +IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ +n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd +2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF +VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ +GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF +li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU +r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 +eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb +MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg +jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB +7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW +5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE +ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z +xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu +QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 +FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH +22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP +xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn +dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 +Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b +nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ +CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH +u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj +d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE-----'`)) + + // Amazon Root CA 1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE-----'`)) + + // Amazon Root CA 2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE-----'`)) + + // Amazon Root CA 3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE-----'`)) + + // Amazon Root CA 4 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE-----'`)) + + // Starfield Services Root Certificate Authority - G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE-----'`)) + + // Certum CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E +jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo +ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI +ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu +Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg +AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 +HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA +uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa +TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg +xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q +CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs +6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE-----'`)) + + // Certum EC-384 CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE-----'`)) + + // Certum Trusted Network CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE-----'`)) + + // Certum Trusted Network CA 2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE-----'`)) + + // Certum Trusted Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 +MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu +MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV +BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw +MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg +U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ +n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q +p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq +NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF +8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 +HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa +mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi +7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF +ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P +qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ +v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 +Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD +ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 +WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo +zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR +5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ +GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf +5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq +0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D +P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM +qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP +0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf +E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE-----'`)) + + // Autoridad de Certificacion Firmaprofesional CIF A62634068 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 +MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc +tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd +IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC +AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw +ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m +iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF +Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ +hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P +Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE +EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV +1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t +CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR +5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw +f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 +ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK +GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE-----'`)) + + // FIRMAPROFESIONAL CA ROOT-A WEB + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQsw +CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE +YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB +IFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2WhcNNDcwMzMxMDkwMTM2WjBuMQsw +CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE +YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB +IFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zf +e9MEkVz6iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6C +cyvHZpsKjECcfIr28jlgst7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FDY1w8ndYn81LsF7Kpryz3dvgwHQYDVR0O +BBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO +PQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgLcFBTApFw +hVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dG +XSaQpYXFuXqUPoeovQA= +-----END CERTIFICATE-----'`)) + + // ANF Secure Server Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV +BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk +YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV +BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN +MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF +UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD +VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj +cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q +yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH +2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX +H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL +zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR +p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz +W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ +SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn +LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 +n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B +u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L +9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej +rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK +pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 +vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq +OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 +2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI ++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 +MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo +tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE-----'`)) + + // Buypass Class 2 Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE-----'`)) + + // Buypass Class 3 Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE-----'`)) + + // Certainly Root E1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu +bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ +BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s +eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK ++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 +QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 +hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm +ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG +BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE-----'`)) + + // Certainly Root R1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw +PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy +dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 +YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 +1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT +vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed +aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 +1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 +r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 +cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ +wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ +6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA +2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH +Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR +eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u +d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr +PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi +1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd +rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di +taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 +lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj +yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn +Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy +yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n +wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 +OV+KmalBWQewLK8= +-----END CERTIFICATE-----'`)) + + // Certigna + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE-----'`)) + + // Certigna Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE-----'`)) + + // certSIGN ROOT CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE-----'`)) + + // certSIGN ROOT CA G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE-----'`)) + + // ePKI Root Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE-----'`)) + + // HiPKI Root CA - G1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa +Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 +YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw +qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv +Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 +lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz +Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ +KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK +FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj +HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr +y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ +/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM +a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 +fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc +SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza +ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc +XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg +iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho +L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF +Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr +kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ +vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU +YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE-----'`)) + + // CommScope Public Trust ECC Root-01 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMw +TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t +bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNa +Fw00NjA0MjgxNzM1NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv +cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDEw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16ocNfQj3Rid8NeeqrltqLxeP0C +flfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOcw5tjnSCDPgYLpkJE +hRGnSjot6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq +hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg +2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uS +Um9poIyNStDuiw7LR47QjRE= +-----END CERTIFICATE-----'`)) + + // CommScope Public Trust ECC Root-02 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMw +TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t +bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRa +Fw00NjA0MjgxNzQ0NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv +cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDIw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l63FRD/cHB8o5mXxO1Q/MMDAL +j2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/ubCK1sK9IRQq9qEmU +v4RDsNuESgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq +hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/n +ich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AV +mkzw5l4lIhVtwodZ0LKOag== +-----END CERTIFICATE-----'`)) + + // CommScope Public Trust RSA Root-01 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQEL +BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi +Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1 +NTRaFw00NjA0MjgxNjQ1NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t +U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt +MDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwSGWjDR1C45FtnYSk +YZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2bKOx7dAvnQmtVzslh +suitQDy6uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBWBB0HW0al +DrJLpA6lfO741GIDuZNqihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3Oj +WiE260f6GBfZumbCk6SP/F2krfxQapWsvCQz0b2If4b19bJzKo98rwjyGpg/qYFl +P8GMicWWMJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/cZip8UlF1y5mO6D1cv547 +KI2DAg+pn3LiLCuz3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTifBSeolz7p +UcZsBSjBAg/pGG3svZwG1KdJ9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/ +kQO9lLvkuI6cMmPNn7togbGEW682v3fuHX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JO +Hg9O5j9ZpSPcPYeoKFgo0fEbNttPxP/hjFtyjMcmAyejOQoBqsCyMWCDIqFPEgkB +Ea801M/XrmLTBQe0MXXgDW1XT2mH+VepuhX2yFJtocucH+X8eKg1mp9BFM6ltM6U +CBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm45P3luG0wDQYJ +KoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6 +NWPxzIHIxgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQ +nmhUQo8mUuJM3y+Xpi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+ +QgvfKNmwrZggvkN80V4aCRckjXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2v +trV0KnahP/t1MJ+UXjulYPPLXAziDslg+MkfFoom3ecnf+slpoq9uC02EJqxWE2a +aE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0DLwAHb/WNyVntHKLr4W96ioD +j8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/xBpMu95yo9GA+o/E4 +Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054A5U+1C0w +lREQKC6/oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHn +YfkUyq+Dj7+vsQpZXdxc1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVoc +icCMb3SgazNNtQEo/a2tiRc7ppqEvOuM6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw +-----END CERTIFICATE-----'`)) + + // CommScope Public Trust RSA Root-02 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQEL +BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi +Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2 +NDNaFw00NjA0MjgxNzE2NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t +U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt +MDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh+g77aAASyE3VrCLE +NQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mnG2I81lDnNJUDMrG0 +kyI9p+Kx7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0SFHRtI1C +rWDaSWqVcN3SAOLMV2MCe5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxz +hkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2WWy09X6GDRl224yW4fKcZgBzqZUPckXk2 +LHR88mcGyYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rpM9kzXzehxfCrPfp4sOcs +n/Y+n2Dg70jpkEUeBVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIfhs1w/tku +FT0du7jyU1fbzMZ0KZwYszZ1OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5 +kQMreyBUzQ0ZGshBMjTRsJnhkB4BQDa1t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3 +wNemKfrb3vOTlycEVS8KbzfFPROvCgCpLIscgSjX74Yxqa7ybrjKaixUR9gqiC6v +wQcQeKwRoi9C8DfF8rhW3Q5iLc4tVn5V8qdE9isy9COoR+jUKgF4z2rDN6ieZdIs +5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7GxcJXvYXowDQYJ +KoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqB +KCh6krm2qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3 ++VGXu6TwYofF1gbTl4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbyme +APnCKfWxkxlSaRosTKCL4BWaMS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3Nyq +pgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv41xdgSGn2rtO/+YHqP65DSdsu3BaVXoT +6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u5cSoHw2OHG1QAk8mGEPej1WF +sQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FUmrh1CoFSl+NmYWvt +PjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2d +lklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670 +v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17O +rg3bhzjlP1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7 +-----END CERTIFICATE-----'`)) + + // SecureSign Root CA12 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u +LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgw +NTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD +eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS +b290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3emhF +KxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mt +p7JIKwccJ/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zd +J1M3s6oYwlkm7Fsf0uZlfO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gur +FzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBFEaCeVESE99g2zvVQR9wsMJvuwPWW0v4J +hscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1UefNzFJM3IFTQy2VYzxV4+K +h9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsF +AAOCAQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6Ld +mmQOmFxv3Y67ilQiLUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJ +mBClnW8Zt7vPemVV2zfrPIpyMpcemik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA +8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPSvWKErI4cqc1avTc7bgoitPQV +55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhgaaaI5gdka9at/ +yOPiZwud9AzqVN/Ssq+xIvEg37xEHA== +-----END CERTIFICATE-----'`)) + + // SecureSign Root CA14 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEM +BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u +LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgw +NzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD +eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS +b290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh1oq/ +FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOg +vlIfX8xnbacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy +6pJxaeQp8E+BgQQ8sqVb1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo +/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9J +kdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOEkJTRX45zGRBdAuVwpcAQ +0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSxjVIHvXib +y8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac +18izju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs +0Wq2XSqypWa9a4X0dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIAB +SMbHdPTGrMNASRZhdCyvjG817XsYAFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVL +ApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeqYR3r6/wtbyPk +86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E +rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ib +ed87hwriZLoAymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopT +zfFP7ELyk+OZpDc8h7hi2/DsHzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHS +DCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPGFrojutzdfhrGe0K22VoF3Jpf1d+4 +2kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6qnsb58Nn4DSEC5MUo +FlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/OfVy +K4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6 +dB7h7sxaOgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtl +Lor6CZpO2oYofaphNdgOpygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB +365jJ6UeTo3cKXhZ+PmhIIynJkBugnLNeLLIjzwec+fBH7/PzqUqm9tEZDKgu39c +JRNItX+S +-----END CERTIFICATE-----'`)) + + // SecureSign Root CA15 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMw +UTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBM +dGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMy +NTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpDeWJl +cnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBSb290 +IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5GdCx4 +wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSR +ZHX+AezB2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT +9DAKBggqhkjOPQQDAwNoADBlAjEA2S6Jfl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp +4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJSwdLZrWeqrqgHkHZAXQ6 +bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= +-----END CERTIFICATE-----'`)) + + // D-TRUST BR Root CA 1 2020 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 +NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS +zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 +QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ +VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW +wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV +dWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE-----'`)) + + // D-TRUST EV Root CA 1 2020 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 +NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC +/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD +wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 +OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA +y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb +gfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE-----'`)) + + // D-TRUST Root CA 3 2013 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEDjCCAvagAwIBAgIDD92sMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxHzAdBgNVBAMMFkQtVFJVU1QgUm9vdCBD +QSAzIDIwMTMwHhcNMTMwOTIwMDgyNTUxWhcNMjgwOTIwMDgyNTUxWjBFMQswCQYD +VQQGEwJERTEVMBMGA1UECgwMRC1UcnVzdCBHbWJIMR8wHQYDVQQDDBZELVRSVVNU +IFJvb3QgQ0EgMyAyMDEzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +xHtCkoIf7O1UmI4SwMoJ35NuOpNcG+QQd55OaYhs9uFp8vabomGxvQcgdJhl8Ywm +CM2oNcqANtFjbehEeoLDbF7eu+g20sRoNoyfMr2EIuDcwu4QRjltr5M5rofmw7wJ +ySxrZ1vZm3Z1TAvgu8XXvD558l++0ZBX+a72Zl8xv9Ntj6e6SvMjZbu376Ml1wrq +WLbviPr6ebJSWNXwrIyhUXQplapRO5AyA58ccnSQ3j3tYdLl4/1kR+W5t0qp9x+u +loYErC/jpIF3t1oW/9gPP/a3eMykr/pbPBJbqFKJcu+I89VEgYaVI5973bzZNO98 +lDyqwEHC451QGsDkGSL8swIDAQABo4IBBTCCAQEwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUP5DIfccVb/Mkj6nDL0uiDyGyL+cwDgYDVR0PAQH/BAQDAgEGMIG+ +BgNVHR8EgbYwgbMwdKByoHCGbmxkYXA6Ly9kaXJlY3RvcnkuZC10cnVzdC5uZXQv +Q049RC1UUlVTVCUyMFJvb3QlMjBDQSUyMDMlMjAyMDEzLE89RC1UcnVzdCUyMEdt +YkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MDugOaA3hjVodHRwOi8v +Y3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2FfM18yMDEzLmNybDAN +BgkqhkiG9w0BAQsFAAOCAQEADlkOWOR0SCNEzzQhtZwUGq2aS7eziG1cqRdw8Cqf +jXv5e4X6xznoEAiwNStfzwLS05zICx7uBVSuN5MECX1sj8J0vPgclL4xAUAt8yQg +t4RVLFzI9XRKEBmLo8ftNdYJSNMOwLo5qLBGArDbxohZwr78e7Erz35ih1WWzAFv +m2chlTWL+BD8cRu3SzdppjvW7IvuwbDzJcmPkn2h6sPKRL8mpXSSnON065102ctN +h9j8tGlsi6BDB2B4l+nZk3zCRrybN1Kj7Yo8E6l7U0tJmhEFLAtuVqwfLoJs4Gln +tQ5tLdnkwBXxP/oYcuEVbSdbLTAoK59ImmQrme/ydUlfXA== +-----END CERTIFICATE-----'`)) + + // D-TRUST Root Class 3 CA 2 2009 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE-----'`)) + + // D-TRUST Root Class 3 CA 2 EV 2009 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE-----'`)) + + // D-Trust SBR Root CA 1 2022 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICXjCCAeOgAwIBAgIQUs/kjG2gSvc/gpcMgAmMlTAKBggqhkjOPQQDAzBJMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSMwIQYDVQQDExpELVRy +dXN0IFNCUiBSb290IENBIDEgMjAyMjAeFw0yMjA3MDYxMTMwMDBaFw0zNzA3MDYx +MTI5NTlaMEkxCzAJBgNVBAYTAkRFMRUwEwYDVQQKEwxELVRydXN0IEdtYkgxIzAh +BgNVBAMTGkQtVHJ1c3QgU0JSIFJvb3QgQ0EgMSAyMDIyMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEWZM59oxJZijXYQzIq38Moy3foqR8kito1S5+HkDLtGhJfxKhq39X +nxkuYy5b/mZxDDMPud5rxIjDse/sOUDjlqvb5XuuH9z5r0aaakYGL8c3ZIsXYv6W +w6LuhOCwlzm8o4GPMIGMMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPEpox4B +Eh09dVZNx1B8xRmqDxi3MA4GA1UdDwEB/wQEAwIBBjBKBgNVHR8EQzBBMD+gPaA7 +hjlodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Nicl9yb290X2Nh +XzFfMjAyMi5jcmwwCgYIKoZIzj0EAwMDaQAwZgIxAJf53q5Lj5i1HkB/Mn1NVEPa +ic3CqpI80YIec8/6TJIg+2MnxfVzPQk996dhhozzagIxAOcvfLj1JYw7OR82q431 +hqIu4Xpk2mc5Av7+Mz/Zc7ZYWzr8sqTZYHh3zHmnpq5VvQ== +-----END CERTIFICATE-----'`)) + + // D-Trust SBR Root CA 2 2022 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFrDCCA5SgAwIBAgIQVNWjlR49lbpyG5rQMSFKujANBgkqhkiG9w0BAQ0FADBJ +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSMwIQYDVQQDExpE +LVRydXN0IFNCUiBSb290IENBIDIgMjAyMjAeFw0yMjA3MDcwNzMwMDBaFw0zNzA3 +MDcwNzI5NTlaMEkxCzAJBgNVBAYTAkRFMRUwEwYDVQQKEwxELVRydXN0IEdtYkgx +IzAhBgNVBAMTGkQtVHJ1c3QgU0JSIFJvb3QgQ0EgMiAyMDIyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAryy8jjaM62SvUWrWbjxekTrqmsPKbPuqJ55k +IqlA37koRVrsU2EWKJjCiqR1eFCE3fogSJIHZUE1ZlESdGGdBwaFOTFXeyg/1Zyl +7FrpHEsnn84nBvM39VLYETMWQTof9WN4ZWOGyb/IAQQfbu7i7KwM7oKS4vYaDT85 ++Z1lk634uQXBPfg3gVbDoP4F7OCUFjojFgTapgqThXJtYTuhjUXW43++Fb02hAj2 +C4NrJqqiveCw56rgrmfE04KlDKmk8DN5DVA/8O+QPSS5f9IgbOqX87+c3EfeCWG9 +lHmVWgJ2NWDERyIN93ZjA9PG+4PGXaut7WklKwNbTSUAQeOMhxdSqOAFK0NNFBPK +5z9DIrw3pHXx9r867zIeru5YhpByugSsQEjvXMR4p6mPJ1rLeuxY8sIIWJBtTQOF +eXEVBQ5OPvnfDwX3XxRIViENM5KxrIzlGP6/D+7gBKq9IfJYtlyJCosYCSIaszXG +ZsL1MxWZgOAI+ZYvE4zu2reIxOk3tddq1zqETatwjNNOFFWgohD8ZNpn6PHLM93J +moqPli9Ygdn4mgBDzJD7VXb7huM3ASgMb/TpWU0Vd1FCSsw0uIBDUIHvV6UT26eU +eQ9Lyn4Xfa+jIWTocVVWjwawR+xZD11wWywWQvCGnnXea01ImITiVxi2nIKZZTqL +gHhXDEkCAwEAAaOBjzCBjDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRds4CU +G+WGv2i6FDSk9u5t8t3f5zAOBgNVHQ8BAf8EBAMCAQYwSgYDVR0fBEMwQTA/oD2g +O4Y5aHR0cDovL2NybC5kLXRydXN0Lm5ldC9jcmwvZC10cnVzdF9zYnJfcm9vdF9j +YV8yXzIwMjIuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA0VC5YGFbNSr2X0/V9K9yv +D1HhTbwhS5P0AEQTBxALJRg+SFmW96Hhk5B4Zho9I+siqwGmjgxRM+ZtjDHurKQB +cDlI3sdmLGsNy3Ofh5LpPkcfuO8v7rdWjEiJ8DinFTmy7sA/F6RzAgicvAaKpMK3 +YWH5w9vE0Hp8Yd6xWJH13WVMLwv46z217Yq+dxy6WQISZnHlmCfODj2vUaJF+YL7 +WqWUcPeLhMNMZSWbe+IfMHCzQI467r3052jFnckpR3EOk8i1SE71ZrsHiHFpa3tI +jm/wEcS0yXAUmCC97afqAdpupZsS/j5EMLPw63VSwPTD+ncmpHeCLW/zKB5OlfAw +94n4LKJQW/K+Mn5sVNtyySpa4By2C9hSmlmh47ABJ8WgFlBm3OuubfSbWz2EbVuH +56mJu2644JtTicD/LkAaiUQuGENnOOR8cl/ZoyklQUE9HHcbZKjDVe5jcWZig/R/ +JpmgVDuhEm1wYs7T+bi9IvzUmtS74jgWL7d9OcKwqQPpnM9+GI123F8Ru+tC7FAJ +PlzskDHYGnK6P2kH7pg0wjSk1toT1qmE8gCGwFS6HhGw4rnEB7SR56rmMVZvsUTE +KmK8ybBlnDT8DBpT3yEXu8JtoQrm8bCqRAlQSTh6XXHiMS4ZsN+VQgR9hIjOCiNn +azidFt4G/ihwOKVarvyD7Q== +-----END CERTIFICATE-----'`)) + + // T-TeleSec GlobalRoot Class 2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE-----'`)) + + // T-TeleSec GlobalRoot Class 3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE-----'`)) + + // Telekom Security SMIME ECC Root 2021 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICRzCCAc2gAwIBAgIQFSrdFMkY0aRWQIamJa8HXzAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH +bWJIMS0wKwYDVQQDDCRUZWxla29tIFNlY3VyaXR5IFNNSU1FIEVDQyBSb290IDIw +MjEwHhcNMjEwMzE4MTEwODMwWhcNNDYwMzE3MjM1OTU5WjBlMQswCQYDVQQGEwJE +RTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMS0wKwYD +VQQDDCRUZWxla29tIFNlY3VyaXR5IFNNSU1FIEVDQyBSb290IDIwMjEwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAASwGY+ia7XHzQ8wmTcMw2Bb8fEnIFU9wJKLq1ehb3OD +IcJDEwxeiarHBTV5k2KQ1l0TH9F6oLyeEKdmfEYKsFdsv+ZUOTghbBJccczTWl9t +t6eG37Pf7sLniUGWNfYvSrWjQjBAMB0GA1UdDgQWBBQrywEMY8NTEqWoV6/QnIP7 +vZA6SzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQD +AwNoADBlAjEA1rxIkodHA8dwOyW2H65GZ3N0ACdL5KUEogPfXiitbl4DyN1onLa/ +lBBIlS8P/xiLAjABQDOel5dNBfJ0VAzNOf1qawnBJD9hjjiht+jXRBURYv8OYTdH +S0B/Sl+yZ1pzdcI= +-----END CERTIFICATE-----'`)) + + // Telekom Security SMIME RSA Root 2023 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgIQDH5i9XlzO51Djotj7ZGVuDANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 +eSBHbWJIMS0wKwYDVQQDDCRUZWxla29tIFNlY3VyaXR5IFNNSU1FIFJTQSBSb290 +IDIwMjMwHhcNMjMwMzI4MTIwOTIyWhcNNDgwMzI3MjM1OTU5WjBlMQswCQYDVQQG +EwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMS0w +KwYDVQQDDCRUZWxla29tIFNlY3VyaXR5IFNNSU1FIFJTQSBSb290IDIwMjMwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDvxQ6LvjLSZ0f/Ckxnsyq/yMPF +keu1xx6R4WaoiItVIIAfUV53l54ZClzHazchfAM2AfSIJdmoLkGq/Ngm4JZAYnmu +V54DOBocsncUPumhctDk4DfRF0btUFx6WMX4K/d1L8+BnlostzqsoFmYBFEM/0nF +UP0e00eFSzNPoje1rwSaJzKdVtU/VWHji2+uUf6X/mkH+mJbJuYUeRWlEziuXze+ +lErWDYAWaaSRsjpJmHWdRhCKXHp/hKXorx7Hq7NaRrWjS/WmIzYARrHbBbYbzp56 +Mlya1XLDnYZNK4TTHrWI2hB4nCLDOyO16xMHvW9T7Jvsm9Nl9QcJ412nmbV+ho7V +Av+3hQnjRxTdlmYYNN4I1d/LGJliCyvsAF1SRNPGlvwyViWRz80ZO5U5PgKHmWO2 +1T40eg8RdYG8fQTKYLQoddcCUd1SAC7H/YnxXPPLpCcSOI+7+4nw5MQ4LL6CoHFh +YpGPSAwvK6mw8csQBOd0vzeQ708qQzWXEsYqcA3eLFVHeWMp9cofagZSHK4tJCKD +Iq/QqjC3Kh//ZSNYZZPIjn1AEDGGeNlVyzww8N5RKgA20idFX9jooSE9fkZWOylF +8R0FCc62QzDcRZAQMEyka4aLPz0vMZFx7ya59r6dsGzfEe5YP0N5hjmA8SYXB5jw +maowLENZFM7t4kAThQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FJrOrCrsAfplcN6XnfHSAIylo2S7MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgw +FoAUms6sKuwB+mVw3ped8dIAjKWjZLswDQYJKoZIhvcNAQEMBQADggIBAONQ/fVA +FiIJljoNqe+B5y4y8KHxSV57iA0Ecte+Z6i6He5Qu3JuetG7DHIwRsjV1wISFplO +Ht9alu6Pkb6uhvgQd6XEbkdhwPIm2U9haAVIdQgVpaF71biziXnm7fHzYQCGey4x +/qNc+Hk9tFuIe+Ajuw2hF/rLaA2Yd3EI4m1DdGvENsWUQaQA1lctmYqLIBIVAjIO +0knsgUjFaidS17JzVVOWPJ5PTLWg0E9X0GcoSGS+xri67GTPyHvFaucq5llXttbU +1sBnXNmeKAlAv/OpNTFlYAPLGWyClQMeXz/hvepJceVbtwtHFhsgiW2UmQx+iGwd +DfS3IRpZl6zL6L4XH5V8U5uvUFKqjQsur1rXYPIqaSq57lRwGKq99aE/0t2hYxkA ++KcM66N58nBZo/iiEgPsE//kAoY218HDpLXUpMI3RbaUcD3FveujFR3jNnoVaSpW +NDnPpZo2qsjtebzP9s4EUwvaslAjfLw+Jq3wDkO7JsuuwkDeNx8KoFHNY522T9jG +R3y82LTtnovzEeKotT7srnA+fiK7NUgXYGIUkTCjdj2mUTaLHw3dajEcpe3dlqNu +cg8TTaqnqVx4+QMSGJM3RRKJPfi+yr3ZvgzZGGSnyEE+dYIhOH1l9KDUE0sHeCn5 +nX7Mhz/E2i6I3eML3FpRWunZEk+eAtv3BSVR +-----END CERTIFICATE-----'`)) + + // Telekom Security TLS ECC Root 2020 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw +CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH +bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw +MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx +JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE +AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O +tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP +f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA +MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di +z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn +27iQ7t0l +-----END CERTIFICATE-----'`)) + + // Telekom Security TLS RSA Root 2023 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj +MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 +eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy +MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC +REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG +A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9 +cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV +cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA +U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6 +Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug +BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy +8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J +co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg +8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8 +rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12 +mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg ++y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX +gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 +p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ +pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm +9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw +M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd +GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+ +CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t +xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+ +w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK +L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj +X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q +ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm +dTdmQRCsu/WU48IxK63nI1bMNSWSs1A= +-----END CERTIFICATE-----'`)) + + // Baltimore CyberTrust Root + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE-----'`)) + + // DigiCert Assured ID Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE-----'`)) + + // DigiCert Assured ID Root G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE-----'`)) + + // DigiCert Assured ID Root G3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE-----'`)) + + // DigiCert Global Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE-----'`)) + + // DigiCert Global Root G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE-----'`)) + + // DigiCert Global Root G3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE-----'`)) + + // DigiCert High Assurance EV Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE-----'`)) + + // DigiCert SMIME ECC P384 Root G5 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICHDCCAaOgAwIBAgIQBT9uoAYBcn3tP8OjtqPW7zAKBggqhkjOPQQDAzBQMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xKDAmBgNVBAMTH0Rp +Z2lDZXJ0IFNNSU1FIEVDQyBQMzg0IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBQMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xKDAmBgNVBAMTH0RpZ2lDZXJ0IFNNSU1FIEVDQyBQMzg0IFJvb3QgRzUw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQWnVXlttT7+2drGtShqtJ3lT6I5QeftnBm +ICikiOxwNa+zMv83E0qevAED3oTBuMbmZUeJ8hNVv82lHghgf61/6GGSKc8JR14L +HMAfpL/yW7yY75lMzHBrtrrQKB2/vgSjQjBAMB0GA1UdDgQWBBRzemuW20IHi1Jm +wmQyF/7gZ5AurTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNnADBkAjA3RPUygONx6/Rtz3zMkZrDbnHY0iNdkk2CQm1cYZX2kfWn +CPZql+mclC2YcP0ztgkCMAc8L7lYgl4Po2Kok2fwIMNpvwMsO1CnO69BOMlSSJHW +Dvu8YDB8ZD8SHkV/UT70pg== +-----END CERTIFICATE-----'`)) + + // DigiCert SMIME RSA4096 Root G5 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQBfa6BCODRst9XOa5W7ocVTANBgkqhkiG9w0BAQwFADBP +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJzAlBgNVBAMT +HkRpZ2lDZXJ0IFNNSU1FIFJTQTQwOTYgUm9vdCBHNTAeFw0yMTAxMTUwMDAwMDBa +Fw00NjAxMTQyMzU5NTlaME8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy +dCwgSW5jLjEnMCUGA1UEAxMeRGlnaUNlcnQgU01JTUUgUlNBNDA5NiBSb290IEc1 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4Gpb2fj5fey1e+9f3Vw0 +2Npd0ctldashfFsA1IJvRYVBiqkSAnIy8BT1A3W7Y5dJD0CZCxoeVqfS0OGr3eUE +G+MfFBICiPWggAn2J5pQ8LrjouCsahSRtWs4EHqiMeGRG7e58CtbyHcJdrdRxDYK +mVNURCW3CTWGFwVWkz1BtwLXYh+KkhGH6hFt6ggR3LF4SEmS9rRRgHgj2P7hVho6 +kBNWNInV4pWLX96yzPs/OLeF9+qevy6hLi9NfWoRLjag/xEIBJVV4Bs7Z5OplFXq +Mu0GOn/Cf+OtEyfRNEGzMMO/tIj4A4Kk3z6reHegWZNx593rAAR7zEg5KOAeoxVp +yDayoQuX31XW75GcpPYW91EK7gMjkdwE/+DdOPYiAwDCB3EaEsnXRiqUG83Wuxvu +v75NUFiwC80wdin1z+W2ai92sLBpatBtZRg1fpO8chfBVULNL8Ilu/T9HaFkIlRd +4p5yQYRucZbqRQe2XnpKhp1zZHc4A9IPU6VVIMRN/2hvVanq3XHkT9mFo3xOKQKe +CwnyGlPMAKbd0TT2DcEwsZwCZKw17aWwKbHSlTMP0iAzvewjS/IZ+dqYZOQsMR8u +4Y0cBJUoTYxYzUvlc4KGjOyo1nlc+2S73AxMKPYXr+Jo1haGmNv8AdwxuvicDvko +Rkrh/ZYGRXkRaBdlXIsmh1sCAwEAAaNCMEAwHQYDVR0OBBYEFNGj1FcdT1XbdUxc +Qp5jFs60xjsfMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBDAUAA4ICAQAHpwreU7ua63C/sjaQzeSnuPEM5F1aHXhl/Mm4HiMRV3xp +NW0B/1NQvwcOuscBP1gqlHUDqxwLI9wbih43PR1Yj3PZsypv3xCgWwynyrB/uSSi +ATUy5V5GQevYf3PnQumkUSZ3gQqo6w8KUJ1+iiBn/AuOOhHTxYxgGNlLsfzU8bRJ +Tq6H4dH7dqFf8wbPl5YM6Z51gVxTDSL8NuZJbnTbAIWNfCKgjvsQTNRiE1vvS3Im +i/xOio/+lxBTxXiLQmQbX+CJ/bsJf1DgVIUmEWodZflJKdx8Nt/7PffSrO4yjW6m +fTmcRcTKDfU7tHlTpS9Wx1HFikxkXZBDI45rTBd4zOi/9TvkqEjPrZsM3zJK09kS +jiN4DS2vn6+ePAnClwDtOmkccT8539OPxGb17zaUD/PdkraWX5Cm3XOqpiCUlCVq +CQxy5BMjYEyjyhcue2cA29DN6nofOSZXiTB3y07llUVPX/s2XD35ILU6ECVPkzJa +7sGW6OlWBLBJYU3seKidGMH/2OovVu+VK3sEXmfjVUDtOQT5C3n1aoxcD4makMfN +i97bJjWhbs2zQvKiDzsMjpP/FM/895P35EEIbhlSEQ9TGXN4DM/YhYH4rVXIsJ5G +Y6+cUu5cv/DAWzceCSDSPiPGoRVKDjZ+MMV5arwiiNkMUkAf3U4PZyYW0q0XHA== +-----END CERTIFICATE-----'`)) + + // DigiCert TLS ECC P384 Root G5 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE-----'`)) + + // DigiCert TLS RSA4096 Root G5 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE-----'`)) + + // DigiCert Trusted Root G4 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE-----'`)) + + // DIGITALSIGN GLOBAL ROOT ECDSA CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICajCCAfCgAwIBAgIUNi2PcoiiKCfkAP8kxi3k6/qdtuEwCgYIKoZIzj0EAwMw +ZDELMAkGA1UEBhMCUFQxKjAoBgNVBAoMIURpZ2l0YWxTaWduIENlcnRpZmljYWRv +cmEgRGlnaXRhbDEpMCcGA1UEAwwgRElHSVRBTFNJR04gR0xPQkFMIFJPT1QgRUNE +U0EgQ0EwHhcNMjEwMTIxMTEwNzUwWhcNNDYwMTE1MTEwNzUwWjBkMQswCQYDVQQG +EwJQVDEqMCgGA1UECgwhRGlnaXRhbFNpZ24gQ2VydGlmaWNhZG9yYSBEaWdpdGFs +MSkwJwYDVQQDDCBESUdJVEFMU0lHTiBHTE9CQUwgUk9PVCBFQ0RTQSBDQTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABG4Lo6szTRzqSuj8BI0UoH3wCCxfg6uT0dJ7utdJ +fY/sElBf1LnL5fD5M2MfyVfsQNgRC5foUhbMKY70BoYeONw9V8Tuqr3IVAQmWicT +UUc9Hx8ajqiVpDPQzEfMbbj8SKNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME +GDAWgBTOr0qLGnXi8TjnAvAWrV7qZNV7tDAdBgNVHQ4EFgQUzq9Kixp14vE45wLw +Fq1e6mTVe7QwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMAqIxHGc +RANNjbTHvKiu2TAnNWprFmPX/OdZ4aeJG0wxmiNVRObzQyHVRydvbVcBqgIxAPuy +6uKXf1G1n0jrvG81iahkcKtXds3AxhRgyn/iggBz98w16o4km+UIWccEjHN4/g== +-----END CERTIFICATE-----'`)) + + // DIGITALSIGN GLOBAL ROOT RSA CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIUXVnIyqsJV/XmtdoplARq/8XUlYcwDQYJKoZIhvcNAQEN +BQAwYjELMAkGA1UEBhMCUFQxKjAoBgNVBAoMIURpZ2l0YWxTaWduIENlcnRpZmlj +YWRvcmEgRGlnaXRhbDEnMCUGA1UEAwweRElHSVRBTFNJR04gR0xPQkFMIFJPT1Qg +UlNBIENBMB4XDTIxMDEyMTEwNTAzNFoXDTQ2MDExNTEwNTAzNFowYjELMAkGA1UE +BhMCUFQxKjAoBgNVBAoMIURpZ2l0YWxTaWduIENlcnRpZmljYWRvcmEgRGlnaXRh +bDEnMCUGA1UEAwweRElHSVRBTFNJR04gR0xPQkFMIFJPT1QgUlNBIENBMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyIe2ONMc8N4S+IPHxIriibi0Inp4 ++AxmUWh2NwrVT8JaCLgWXPdyAQk3hIEqVGvXktBs+qinQxI06w7bNw8p/ooxUULo +S5yQqMgsEdP9oCl+zt6U9oLgWLRORSXxIvI90w97VBrcMrbWUU5+QbRXuCzGuQ4u +ylfx1cjTWOel6UIRrtMgJZRp14/Kog3D058HaD8V0mcuU/12gpsLc6kpDZ4RkxQI +mOyeVBJKVqIGFexrbC6SYC6GDa6CH1FN47IH1xAZVyL2qWlEhPPZPaAGv8yIfn/1 +zlulwipqdELqb6b/+Wix0F+9kdJVbzNXTB6d5OKLwYVloOBqnAAAiJLdWAgW8nAx +qBzh3r1OcenWvn61oVrDTfe/m72UpP31qlOTRskmAQRwxKBxus4lZvuRflVw7kkK +TWJ/wlCacvIYZ53pRag0hOj4gfbRWiIeB087s3/dEaVz3L6pGTppqW0bMuKJqqUn +C1p+dOIPZDldfly5wRf8x41eyewk7dLyP3qERTcCvj5rWcTmWxZtwKqeqrVZLixw +VZzMmZaYJFTRjtrKtBG0t3BDH2+QCyCgqHYTZdvbI1p1S6ELMXcK7n1oYRoTjOpR +flxWo1dMXaHrE2W/VBTM8+7c1+w8l/J4Vrjfclxw/M4G3Z/SBzHv51KRns2618AY +RAcxZUkyaRNK648CAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAW +gBS1Nrw8jBqrLPZZGS2DFNqTJRXWhjAdBgNVHQ4EFgQUtTa8PIwaqyz2WRktgxTa +kyUV1oYwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQAU+zElODH4 +ygiyI3Y4rfjTWfXMtFcl4US+fvwW7K76Jp9PZxZKVvD97ccZATSOkFot1oBc7HHS +gSWCHgBx35rR1R0iu9Gl82IPtOvcJHP+plbNmhTFBDUWMaIH66UA4rb4X3L9P2FJ +jt5+TTjXeh50N2xR3L4ABLg4FPMgwe2bpyP9DUKEHX/yc8PQeGPxn+zXW+nxvmyg +SwOejWnhFNqIEIEjU//aVCsLxrmWlQQYRvN7qJfYW2ik5DgcDkXlmNMJrppe7LN5 +DTly8vSUnQ6eYCLmqPZMhc0HgjpoOc09X+M49LavO2tKn2BRRaJAAuWqDOM+0XjU +onScJroFmihwSj6mC9AdSfC6+K5BEH6kBxK9qM8pPVe7x/FDRwA+rnAYWiB7Ccs6 +OnCA5UxgmMEVwR1K98jwm+FyreddaFgLBLGMvJ+3+26LWwRV++sjVdd4UNoly74n +NrskGnkcUdH+E7v/eCzcpL4v9sVLU8+nTJlecKxZiASuZAS/e6Z6TdPod72hflAV +8+9JMIVNIVeq2yx1l62BAYeisXCdHgZaA2CxP6ZtgizUFLGBpeg9iB20cixYN4qO +OJS4c92p4Lj2d6KzfFjermk6tYulGrvy2HQGnP1icyAhdrF+cJ4Z1OsXYhk4mc02 +K0f+McvfueSsCNPYpuvUnn5LZKRVXSsXyQ== +-----END CERTIFICATE-----'`)) + + // CA Disig Root R2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE-----'`)) + + // GLOBALTRUST 2020 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG +A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw +FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx +MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u +aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b +RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z +YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3 +QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw +yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+ +BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ +SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH +r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0 +4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me +dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw +q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2 +nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu +H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC +XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd +6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf ++I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi +kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7 +wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB +TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C +MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn +4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I +aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy +qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE-----'`)) + + // emSign ECC Root CA - C3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG +EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx +IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND +IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci +MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti +sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O +BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c +3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J +0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE-----'`)) + + // emSign ECC Root CA - G3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE-----'`)) + + // emSign Root CA - C1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG +A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg +SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v +dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ +BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ +HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH +3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH +GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c +xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 +aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq +TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 +/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 +kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG +YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT ++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo +WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE-----'`)) + + // emSign Root CA - G1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE-----'`)) + + // AffirmTrust Commercial + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE-----'`)) + + // AffirmTrust Networking + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE-----'`)) + + // AffirmTrust Premium + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE-----'`)) + + // AffirmTrust Premium ECC + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE-----'`)) + + // Entrust Root Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE-----'`)) + + // Entrust Root Certification Authority - EC1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE-----'`)) + + // Entrust Root Certification Authority - G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE-----'`)) + + // Entrust Root Certification Authority - G4 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw +gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL +Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg +MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw +BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 +MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 +c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ +bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ +2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E +T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j +5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM +C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T +DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX +wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A +2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm +nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl +N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj +c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS +5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS +Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr +hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/ +B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI +AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw +H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+ +b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk +2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol +IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk +5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY +n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE-----'`)) + + // Entrust.net Certification Authority (2048) + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE-----'`)) + + // Atos TrustedRoot 2011 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE-----'`)) + + // Atos TrustedRoot Root CA ECC G2 2020 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICMTCCAbagAwIBAgIMC3MoERh0MBzvbwiEMAoGCCqGSM49BAMDMEsxCzAJBgNV +BAYTAkRFMQ0wCwYDVQQKDARBdG9zMS0wKwYDVQQDDCRBdG9zIFRydXN0ZWRSb290 +IFJvb3QgQ0EgRUNDIEcyIDIwMjAwHhcNMjAxMjE1MDgzOTEwWhcNNDAxMjEwMDgz +OTA5WjBLMQswCQYDVQQGEwJERTENMAsGA1UECgwEQXRvczEtMCsGA1UEAwwkQXRv +cyBUcnVzdGVkUm9vdCBSb290IENBIEVDQyBHMiAyMDIwMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEyFyAyk7CKB9XvzjmYSP80KlblhYWwwxeFaWQCf84KLR6HgrWUyrB +u5BAdDfpgeiNL2gBNXxSLtj0WLMRHFvZhxiTkS3sndpsnm2ESPzCiQXrmBMCAWxT +Hg5JY1hHsa/Co2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFFsfxHFs +shufvlwfjP2ztvuzDgmHMB0GA1UdDgQWBBRbH8RxbLIbn75cH4z9s7b7sw4JhzAO +BgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAOzgmf3d5FTByx/oPijX +FVlKgspTMOzrNqW5yM6TR1bIYabhbZJTlY/241VT8N165wIxALCH1RuzYPyRjYDK +ohtRSzhUy6oee9flRJUWLzxEeC4luuqQ5OxS7lfsA4TzXtsWDQ== +-----END CERTIFICATE-----'`)) + + // Atos TrustedRoot Root CA ECC TLS 2021 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w +LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w +CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0 +MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF +Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI +zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X +tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4 +AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2 +KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD +aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu +CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo +9H1/IISpQuQo +-----END CERTIFICATE-----'`)) + + // Atos TrustedRoot Root CA RSA G2 2020 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIMR7opRlU+FpKXsKtAMA0GCSqGSIb3DQEBDAUAMEsxCzAJ +BgNVBAYTAkRFMQ0wCwYDVQQKDARBdG9zMS0wKwYDVQQDDCRBdG9zIFRydXN0ZWRS +b290IFJvb3QgQ0EgUlNBIEcyIDIwMjAwHhcNMjAxMjE1MDg0MTIzWhcNNDAxMjEw +MDg0MTIyWjBLMQswCQYDVQQGEwJERTENMAsGA1UECgwEQXRvczEtMCsGA1UEAwwk +QXRvcyBUcnVzdGVkUm9vdCBSb290IENBIFJTQSBHMiAyMDIwMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAljGFSqoPMv554UOHnPsjt45/DVS9x2KTd+Qc +NQR2owOLIu7EhN2lk25uso4JA+tRFjEXqmkVGA5ndCNe6pp9tTk+PYKpa+H+qRyw +rVpNTHiDQYvP8h1impgEnGPpq2X+SB0kZQdHPrmRLumdm38aNak0sLflcDPvSnJR +tge/YD8qn51U3/PXlElRA1pAqWjdEVlc+HamvFBSEO2s7JXg1INrSdoKT5mD3jKD +SINnlbJ+54GFPc2C98oC7W2IXQiNuDW/KmkwmbtL0UHbRaCTmVGBkDYIqoq26I+z +y+7lRg1ydfVJbOGify+87YSmN+7ewk85Tvae8MnRmzCdSW3h2v8SEIzW5Zl7BbZ9 +sAnHpPiyHDmVOTP0Nc4lYnuwXyDzy234bFIUZESP08ipdgflr3GZLS0EJUh2r8Pn +zEPyB7xKJCQ33fpulAlvTF4BtP5U7COWpV7dhv/pRirx6NzspT2vb6oOD7R1+j4I +uSZFT2aGTLwZuOHVNe6ChMjTqxLnzXMzYnf0F8u9NHYqBc6V5Xh5S56wjfk8WDiR +6l6HOMC3Qv2qTIcjrQQgsX52Qtq7tha6V8iOE/p11QhMrziRqu+P+p9JLlR8Clax +evrETi/Uo/oWitCV5Zem/8P8fA5HWPN/B3sS3Fc/LeOhTVtSTDOHmagJe2x+DvLP +VkKe6wUCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQgJfMH +/adv8ZbukRBpzJrvfchoeDAdBgNVHQ4EFgQUICXzB/2nb/GW7pEQacya733IaHgw +DgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQAkK06Y8h0X7dl2JrYw +M+hpRaFRS1LYejowtuQS6r+fTOAEpPY1xv6hMPdThZKtVAVXX5LlKt42J557E0fJ +anWv/PM35wz1PQFztWlR+L1Z0boL+Lq6ZCdDs3yDlYrnnhOW129KlkFJiw4grRbG +96aHW4gSiYuJyhLSVq8iASFG6auYP6eI3uTLKpp1Gfo5XgkF1wMyGrgXUQjHAEB9 +9L74DFn0aXZu06RYW14mc+RCVQZeeEAP0zif7yZRcHSR8XdiAejZy+uh3zkyHbtr +/XH+68+l5hT9AIATxpoASLCZBemugEj7CT9RFLW552BNTcovgSHuUgxletz1iUlM +MJI0WIAyWbEN/yRhD+cKQtB7vPiOJ0c/cJ0n2bYGPaW7y16Prg5Tx5xqbztMD6NA +cKiaB87UblsHotLiVLa9bzNyY61RmOGPdvFqBzgl/vZizl/bY8Jume8G3LneGRro +VD190nZ12V4+MkinjPKecgz4uFi4FyOlFId1WHoAgQciOWpMlKC1otunLMGw8aOb +wEz3bXDqMZ/xrn0+cyjZod/6k/CbsPDizSUgde/ifTIFyZt27su9MR75lJhLJFhW +SMDeBky9pjRd7RZhY3P7GeL6W9iXddRtnmA5XpSLAizrmc5gKm4bjKdLvP025pgf +ZfJ/8eOPTIBGNli2oWXLzhxEdQ== +-----END CERTIFICATE-----'`)) + + // Atos TrustedRoot Root CA RSA TLS 2021 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM +MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx +MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00 +MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD +QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z +4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv +Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ +kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs +GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln +nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh +3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD +0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy +geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8 +ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB +c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI +pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS +4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs +o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ +qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw +xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM +rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4 +AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR +0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY +o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5 +dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE +oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ== +-----END CERTIFICATE-----'`)) + + // GlobalSign + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE-----'`)) + + // GlobalSign + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE-----'`)) + + // GlobalSign + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE-----'`)) + + // GlobalSign Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE-----'`)) + + // GlobalSign Root E46 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE-----'`)) + + // GlobalSign Root R46 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE-----'`)) + + // GlobalSign Secure Mail Root E45 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICITCCAaegAwIBAgIQdlP+qicdlUZd1vGe5biQCjAKBggqhkjOPQQDAzBSMQsw +CQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEoMCYGA1UEAxMf +R2xvYmFsU2lnbiBTZWN1cmUgTWFpbCBSb290IEU0NTAeFw0yMDAzMTgwMDAwMDBa +Fw00NTAzMTgwMDAwMDBaMFIxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxT +aWduIG52LXNhMSgwJgYDVQQDEx9HbG9iYWxTaWduIFNlY3VyZSBNYWlsIFJvb3Qg +RTQ1MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+XmLgUc3iZY/RUlQfxomC5Myfi7A +wKcImsNuj5s+CyLsN1O3b4qwvCc3S22pRjvZH/+loUS7LXO/nkEHXFObUQg6Wrtv +OMcWkXjCShNpHYLfWi8AiJaiLhx0+Z1+ZjeKo0IwQDAOBgNVHQ8BAf8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU3xNei1/CQAL9VreUTLYe1aaxFJYw +CgYIKoZIzj0EAwMDaAAwZQIwE7C+13EgPuSrnM42En1fTB8qtWlFM1/TLVqy5IjH +3go2QjJ5naZruuH5RCp7isMSAjEAoGYcToedh8ntmUwbCu4tYMM3xx3NtXKw2cbv +vPL/P/BS3QjnqmR5w+RpV5EvpMt8 +-----END CERTIFICATE-----'`)) + + // GlobalSign Secure Mail Root R45 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIQdlP+qExQq5+NMrUdA49X3DANBgkqhkiG9w0BAQwFADBS +MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEoMCYGA1UE +AxMfR2xvYmFsU2lnbiBTZWN1cmUgTWFpbCBSb290IFI0NTAeFw0yMDAzMTgwMDAw +MDBaFw00NTAzMTgwMDAwMDBaMFIxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMSgwJgYDVQQDEx9HbG9iYWxTaWduIFNlY3VyZSBNYWlsIFJv +b3QgUjQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3HnMbQb5bbvg +VgRsf+B1zC0FSehL3FTsW3eVcr9/Yp2FqYokUF9T5dt0b6QpWxMqCa2axS/C93Y7 +oUVGqkPmJP4rsG8ycBlGWnkmL/w9fV9ky1fMYWGo2ZVu45Wgbn9HEhjW7wPJ+4r6 +mr2CFalVd0sRT1nga8Nx8wzYVNWBaD4TuRUuh4o8RCc2YiRu+CwFcjBhvUKRI8Sd +JafZVJoUozGtgHkMp2NsmKOsV0czH2WW4dDSNdr5cfehpiW1QV3fPmDY0fafpfK4 +zBOqj/mybuGDLZPdPoUa3eixXCYBy0mF/PzS1H+FYoZ0+cvsNSKiDDCPO6t561by ++kLz7fkfRYlAKa3qknTqUv1WtCvaou11wm6rzlKQS/be8EmPmkjUiBltRebMjLnd +ZGBgAkD4uc+8WOs9hbnGCtOcB2aPxxg5I0bhPB6jL1Bhkgs9K2zxo0c4V5GrDY/G +nU0E0iZSXOWl/SotFioBaeepfeE2t7Eqxdmxjb25i87Mi6E+C0jNUJU0xNgIWdhr +JvS+9dQiFwBXya6bBDAznwv731aiyW5Udtqxl2InWQ8RiiIbZJY/qPG3JEqNPFN8 +bYN2PbImSHP1RBYBLQkqjhaWUNBzBl27IkiCTApGWj+A/1zy8pqsLAjg1urwEjiB +T6YQ7UarzBacC89kppkChURnRq39TecCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgGG +MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKCTFShu7o8IsjXGnmJ5dKexDit7 +MA0GCSqGSIb3DQEBDAUAA4ICAQBFCvjRXKxigdAE17b/V1GJCwzL3iRlN/urnu1m +9OoMGWmJuBmxMFa02fb3vsaul8tF9hGMOjBkTMGfWcBGQggGR2QXeOCVBwbWjKKs +qdk/03tWT/zEhyjftisWI8CfH1vj1kReIk8jBIw1FrV5B4ZcL5fi9ghkptzbqIrj +pHt3DdEpkyggtFOjS05f3sH2dSP8Hzx4T3AxeC+iNVRxBKzIxG3D9pGx/s3uRG6B +9kDFPioBv6tMsQM/DRHkD9Ik4yKIm59fRz1RSeAJN34XITF2t2dxSChLJdcQ6J9h +WRbFPjJOHwzOo8wP5McRByIvOAjdW5frQmxZmpruetCd38XbCUMuCqoZPWvoajB6 +V+a/s2o5qY/j8U9laLa9nyiPoRZaCVA6Mi4dL0QRQqYA5jGY/y2hD+akYFbPedey +Ttew+m4MVyPHzh+lsUxtGUmeDn9wj3E/WCifdd1h4Dq3Obbul9Q1UfuLSWDIPGau +l+6NJllXu3jwelAwCbBgqp9O3Mk+HjrcYpMzsDpUdG8sMUXRaxEyamh29j32ahNe +JJjn6h2az3iCB2D3TRDTgZpFjZ6vm9yAx0OylWikww7oCkcVv1Qz3AHn1aYec9h6 +sr8vreNVMJ7fDkG84BH1oQyoIuHjAKNOcHyS4wTRekKKdZBZ45vRTKJkvXN5m2/y +s8H2PA== +-----END CERTIFICATE-----'`)) + + // Go Daddy Class 2 Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE-----'`)) + + // Go Daddy Root Certificate Authority - G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE-----'`)) + + // Starfield Class 2 Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE-----'`)) + + // Starfield Root Certificate Authority - G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE-----'`)) + + // GlobalSign + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD +VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw +MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g +UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx +uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV +HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ ++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 +bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE-----'`)) + + // GTS Root R1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE-----'`)) + + // GTS Root R2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt +nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY +6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu +MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k +RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg +f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV ++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo +dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa +G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq +gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H +vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC +B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u +NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg +yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev +HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 +xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR +TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg +JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV +7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl +6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL +-----END CERTIFICATE-----'`)) + + // GTS Root R3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE-----'`)) + + // GTS Root R4 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE-----'`)) + + // Hongkong Post Root CA 3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ +SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n +a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 +NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT +CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u +Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO +dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI +VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV +9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY +2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY +vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt +bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb +x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ +l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK +TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj +Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw +DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG +7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk +MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr +gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk +GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS +3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm +Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ +l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c +JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP +L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa +LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG +mpv0 +-----END CERTIFICATE-----'`)) + + // ACCVRAIZ1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE-----'`)) + + // AC RAIZ FNMT-RCM + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE-----'`)) + + // AC RAIZ FNMT-RCM SERVIDORES SEGUROS + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE-----'`)) + + // Staat der Nederlanden Root CA - G3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE-----'`)) + + // TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE-----'`)) + + // HARICA Client ECC Root CA 2021 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICWjCCAeGgAwIBAgIQMWjZ2OFiVx7SGUSI5hB98DAKBggqhkjOPQQDAzBvMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEnMCUGA1UEAwweSEFSSUNBIENsaWVudCBFQ0Mg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDMzNFoXDTQ1MDIxMzExMDMzM1owbzEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJzAlBgNVBAMMHkhBUklDQSBDbGllbnQgRUND +IFJvb3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABAcYrZWWlNBcD4L3 +KkD6AsnJPTamowRqwW2VAYhgElRsXKIrbhM6iJUMHCaGNkqJGbcY3jvoqFAfyt9b +v0mAFdvjMOEdWscqigEH/m0sNO8oKJe8wflXhpWLNc+eWtFolaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUUgjSvjKBJf31GpfsTl8au1PNkK0wDgYDVR0P +AQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMEwxRUZPqOa+w3eyGhhLLYh7WOar +lGtEA7AX/9+Cc0RRLP2THQZ7FNKJ7EAM7yEBLgIwL8kuWmwsHdmV4J6wuVxSfPb4 +OMou8dQd8qJJopX4wVheT/5zCu8xsKsjWBOMi947 +-----END CERTIFICATE-----'`)) + + // HARICA Client RSA Root CA 2021 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFqjCCA5KgAwIBAgIQVVL4HtsbJCyeu5YYzQIoPjANBgkqhkiG9w0BAQsFADBv +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEnMCUGA1UEAwweSEFSSUNBIENsaWVudCBS +U0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTg0NloXDTQ1MDIxMzEwNTg0NVow +bzELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBS +ZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJzAlBgNVBAMMHkhBUklDQSBDbGllbnQg +UlNBIFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AIHbV0KQLHQ19Pi4dBlNqwlad0WBc2KwNZ/40LczAIcTtparDlQSMAe8m7dI19EZ +g66O2KnxqQCEsIxenugMj1Rpv/bUCE8mcP4YQWMaszKLQPgHq1cx8MYWdmeatN0v +8tFrxdCShJFxbg8uY+kfU6TdUhPMCYMpgQzFU3VEsQ5nUxjQwx+IS5+UJLQpvLvo +Tv1v0hUdSdyNcPIRGiBRVRG6iG/E91B51qox4oQ9XjLIdypQceULL+m26u+rCjM5 +Dv2PpWdDgo6YaQkJG0DNOGdH6snsl3ES3iT1cjzR90NMJveQsonpRUtVPTEFekHi +lbpDwBfFtoU9GY1kcPNbrM2f0yl1h0uVZ2qm+NHdvJCGiUMpqTdb9V2wJlpTQnaQ +K8+eVmwrVM9cmmXfW4tIYDh8+8ULz3YEYwIzKn31g2fn+sZD/SsP1CYvd6QywSTq +ZJ2/szhxMUTyR7iiZkGh+5t7vMdGanW/WqKM6GpEwbiWtcAyCC17dDVzssrG/q8R +chj258jCz6Uq6nvWWeh8oLJqQAlpDqWW29EAufGIbjbwiLKd8VLyw3y/MIk8Cmn5 +IqRl4ZvgdMaxhZeWLK6Uj1CmORIfvkfygXjTdTaefVogl+JSrpmfxnybZvP+2M/u +vZcGHS2F3D42U5Z7ILroyOGtlmI+EXyzAISep0xxq0o3AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFKDWBz1eJPd7oEQuJFINGaorBJGnMA4GA1Ud +DwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEADUf5CWYxUux57sKo8mg+7ZZF +yzqmmGM/6itNTgPQHILhy9Pl1qtbZyi8nf4MmQqAVafOGyNhDbBX8P7gyr7mkNuD +LL6DjvR5tv7QDUKnWB9p6oH1BaX+RmjrbHjJ4Orn5t4xxdLVLIJjKJ1dqBp+iObn +K/Es1dAFntwtvTdm1ASip62/OsKoO63/jZ0z4LmahKGHH3b0gnTXDvkwSD5biD6q +XGvWLwzojnPCGJGDObZmWtAfYCddTeP2Og1mUJx4e6vzExCuDy+r6GSzGCCdRjVk +JXPqmxBcWDWJsUZIp/Ss1B2eW8yppRoTTyRQqtkbbbFA+53dWHTEwm8UcuzbNZ+4 +VHVFw6bIGig1Oq5l8qmYzq9byTiMMTt/zNyW/eJb1tBZ9Ha6C8tPgxDHQNAdYOkq +5UhYdwxFab4ZcQQk4uMkH0rIwT6Z9ZaYOEgloRWwG9fihBhb9nE1mmh7QMwYXAwk +ndSV9ZmqRuqurL/0FBkk6Izs4/W8BmiKKgwFXwqXdafcfsD913oY3zDROEsfsJhw +v8x8c/BuxDGlpJcdrL/ObCFKvicjZ/MGVoEKkY624QMFMyzaNAhNTlAjrR+lxdR6 +/uoJ7KcoYItGfLXqm91P+edrFcaIz0Pb5SfcBFZub0YV8VYt6FwMc8MjgTggy8kM +ac8sqzuEYDMZUv1pFDM= +-----END CERTIFICATE-----'`)) + + // HARICA TLS ECC Root CA 2021 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v +dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG +A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 +KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y +STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw +SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN +nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE-----'`)) + + // HARICA TLS RSA Root CA 2021 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l +mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE +4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv +a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M +pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw +Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b +LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY +AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB +AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq +E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr +W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ +CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU +X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 +f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja +H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP +JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P +zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt +jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 +/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT +BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 +aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW +xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU +63ZTGI0RmLo= +-----END CERTIFICATE-----'`)) + + // Hellenic Academic and Research Institutions ECC RootCA 2015 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE-----'`)) + + // Hellenic Academic and Research Institutions RootCA 2015 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE-----'`)) + + // IdenTrust Commercial Root CA 1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE-----'`)) + + // IdenTrust Public Sector Root CA 1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE-----'`)) + + // ISRG Root X1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE-----'`)) + + // ISRG Root X2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE-----'`)) + + // Izenpe.com + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE-----'`)) + + // SZAFIR ROOT CA2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE-----'`)) + + // LAWtrust Root CA2 (4096) + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFmDCCA4CgAwIBAgIEVRpusTANBgkqhkiG9w0BAQsFADBDMQswCQYDVQQGEwJa +QTERMA8GA1UEChMITEFXdHJ1c3QxITAfBgNVBAMTGExBV3RydXN0IFJvb3QgQ0Ey +ICg0MDk2KTAgFw0yMzAyMTQwOTE5MzhaGA8yMDUzMDIxNDA5NDkzOFowQzELMAkG +A1UEBhMCWkExETAPBgNVBAoTCExBV3RydXN0MSEwHwYDVQQDExhMQVd0cnVzdCBS +b290IENBMiAoNDA5NikwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +F8srQ7ps+cmTimUNEkzsJxS3E3ng1NUtGFbx+eoqEBZObETHamVG85qJNdGH+DOJ +L4gJGpIQkZDBa58Obn8mihNdGKxoAQ0QeGVw2I6PhFqXMBjQEQ5KjVIQpYErUSj1 +Y8S27ECzAeWtd73lOO+8jbPdGaB7DY2022r7JTNa+pGvxHFFMPiIKXvLv9W6JwSO +3bIA98pcmTUU6v11BhUIu8pXaPs/+7Q0c2PR1ePIOFppfWp6RAwNik7tkh0Qjzsi +LLbf7cXG8Il5VGVeXxu9j33fubft6+TFB9FnPJU7kf5CelJAgATSOVdL9JJ9/5vv +5Z3JCbKREjimKQg7ruvKzO1N504hAQf8bzLOaYyEUsZ36icwCt6lrzAraB+s1Owh +rSJJds4PwvIHKvlqEoOaOwSuGXr+oYYk+kFeJXxArCe24yk2bzXiV9AZWN//ZPbD +AUl22yu+vLlPFArVG1gh9hwuAHz4lLXLNxoU5DK5FtRg7AWqXzL6aiMSrNQQu9Ki +grRLDotwJ6rWB8FniPqEwwjJioTI0jdygQ+NFkrk1zVRpTgPjIRLlTbA9ded4F2P +q5HuAAi5nVIf7PiZu3lWsUna0uXYYYtbr/CrN8V7Go6Gvn7FexUeYWjoC4eLc0mh +F3N+KXiOyuBBL3VzdKKXOn/3LnQJuExgi0Y2GRAtnQIDAQABo4GRMIGOMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMCsGA1UdEAQkMCKADzIwMjMwMjE0 +MDkxOTM4WoEPMjA1MzAyMTQwOTQ5MzhaMB8GA1UdIwQYMBaAFNfWVmJcPxeB5nNE +KfVRBe8LYDesMB0GA1UdDgQWBBTX1lZiXD8XgeZzRCn1UQXvC2A3rDANBgkqhkiG +9w0BAQsFAAOCAgEASZwp/j3snkV/qz48/iNvNz53p1P/eJ/8SUSAV2acbtp5/81F +rUyTv7VZxukQt+X4jPuHxR6L2LM/ApYKu4qO79e0wIMgOJdZRWT89ncT8gnXocg4 +dAjq+UhM+h8EnLT/7G5WNnKTbJU+LF/eDwurycwVPhaPZvyyELih0bTewGMZzO9T +qnU2IoslH7+byNfBX+ymNwmqe2K89iIt8dZY3Yy7UvQLp3apensajdytmoFiLoYF +kHJHL6HJZ4SwDWywuJsWt9CZFC+cEpsjqI2mQx7p5S3leKcfZJRktneyqFz7Casp +6x5tddH20MWlwx2fHvMaLbLIH+UoCm7zX/3a5iOhdpBcS5gBgizuRy0CGl9/NMVp +tXKtPvPPnm34KegRJyvgWQsbYetKymmlpNXNURuUjnnN3/audF2xLBuGU/7RMAZB +NAdigkz0fseHdA6wIR4JIIDBsxU9Rm3T8QaSP++glYocbncxtut4KQx77oKlT36k +KV6eqi34jsDz/A0GhZtO3PfiCXzQFFEeerMjr/rRYSpltQHZuOMHyiR20vBKvu+G +BIBCFXARaH7Xx7v+506bnJWlHEqkydAJjKrOSNIekpfXEentZsw33PXXG3SbpupC +rF0y4Fj0gUf/0hLifhzcSXaWwx2fS8pcKjdbPYrROJsh2uO/RUPT4Fh3Hyg= +-----END CERTIFICATE-----'`)) + + // e-Szigno Root CA 2017 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE-----'`)) + + // Microsec e-Szigno Root CA 2009 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE-----'`)) + + // Microsoft ECC Root Certificate Authority 2017 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE-----'`)) + + // Microsoft RSA Root Certificate Authority 2017 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE-----'`)) + + // NAVER Global Root Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE-----'`)) + + // NetLock Arany (Class Gold) Főtanúsítvány + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE-----'`)) + + // OISTE WISeKey Global Root GA CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE-----'`)) + + // OISTE WISeKey Global Root GB CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE-----'`)) + + // OISTE WISeKey Global Root GC CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE-----'`)) + + // QuoVadis Root CA 1 G3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE-----'`)) + + // QuoVadis Root CA 2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE-----'`)) + + // QuoVadis Root CA 2 G3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE-----'`)) + + // QuoVadis Root CA 3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE-----'`)) + + // QuoVadis Root CA 3 G3 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE-----'`)) + + // Security Communication ECC RootCA1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT +AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD +VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx +NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT +HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 +IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl +dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK +ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu +9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O +be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= +-----END CERTIFICATE-----'`)) + + // Security Communication RootCA2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE-----'`)) + + // AAA Certificate Services + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE-----'`)) + + // COMODO Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE-----'`)) + + // COMODO ECC Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE-----'`)) + + // COMODO RSA Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE-----'`)) + + // Sectigo Public Email Protection Root E46 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICMTCCAbegAwIBAgIQbvXTp0GOoFlApzBr0kBlVjAKBggqhkjOPQQDAzBaMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTEwLwYDVQQDEyhT +ZWN0aWdvIFB1YmxpYyBFbWFpbCBQcm90ZWN0aW9uIFJvb3QgRTQ2MB4XDTIxMDMy +MjAwMDAwMFoXDTQ2MDMyMTIzNTk1OVowWjELMAkGA1UEBhMCR0IxGDAWBgNVBAoT +D1NlY3RpZ28gTGltaXRlZDExMC8GA1UEAxMoU2VjdGlnbyBQdWJsaWMgRW1haWwg +UHJvdGVjdGlvbiBSb290IEU0NjB2MBAGByqGSM49AgEGBSuBBAAiA2IABLinUpT1 +PgWwG/YfsdN+ueQFZlSAzmylaH3kU1LbgvrEht9DePfIrRa8P3gyy2vTSdZE5bN+ +n3umxizy4rbTibCaPEvOiUvGxss6SWAPRrxtTnqcyZuFewq2sEfCiOPU0aNCMEAw +HQYDVR0OBBYEFC1OjKfCI7JXqQZrPmsrifPDXkfOMA4GA1UdDwEB/wQEAwIBhjAP +BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQCSnRpZY0VYjhsW5H16 +bDZIMB8rcueQMzT9JKLGBoxvOzJXWvj+xkkSU5rZELKZUXICMAUlKjMh/JPmIqLM +cFUoNVaiB8QhhCMaTEyZUJmSFMtK3Fb79dOPaiz1cTr4izsDng== +-----END CERTIFICATE-----'`)) + + // Sectigo Public Email Protection Root R46 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFgDCCA2igAwIBAgIQHUSeuQ2DkXSu3fLriLemozANBgkqhkiG9w0BAQwFADBa +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTEwLwYDVQQD +EyhTZWN0aWdvIFB1YmxpYyBFbWFpbCBQcm90ZWN0aW9uIFJvb3QgUjQ2MB4XDTIx +MDMyMjAwMDAwMFoXDTQ2MDMyMTIzNTk1OVowWjELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1NlY3RpZ28gTGltaXRlZDExMC8GA1UEAxMoU2VjdGlnbyBQdWJsaWMgRW1h +aWwgUHJvdGVjdGlvbiBSb290IFI0NjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAJHlG/qqbTcrdccuXxSl2yyXtixGj2nZ7JYt8x1avtMdI+ZoCf9KEXMa +rmefdprS5+y42V8r+SZWUa92nan8F+8yCtAjPLosT0eD7J0FaEJeBuDV6CtoSJey ++vOkcTV9NJsXi39NDdvcTwVMlGK/NfovyKccZtlxX+XmWlXKq/S4dxlFUEVOSqvb +nmbBGbc3QshWpUAS+TPoOEU6xoSjAo4vJLDDQYUHSZzP3NHyJm/tMxwzZypFN9mF +ZSIasbUQUglrA8YfcD2RxH2QPe1m+JD/JeDtkqKLMSmtnBJmeGOdV+z7C96O3IvL +Oql39Lrl7DiMi+YTZqdpWMOCGhrN8Z/YU5JOSX2pRefxQyFatz5AzWOJz9m/x1AL +4bzniJatntQX2l3P4JH9phDUuQOBm2ms+4SogTXrG+tobHxgPsPfybSudB1Ird1u +EYbhKmo2Fq7IzrzbWPxAk0DYjlOXwqwiOOWIMbMuoe/s4EIN6v+TVkoGpJtMAmhk +j1ZQwYEF/cvbxdcV8mu1dsOj+TLOyrVKqRt9Gdx/x2p+ley2uI39lUqcoytti/Fw +5UcrAFzkuZ7U+NlYKdDL4ChibK6cYuLMvDaTQfXv/kZilbBXSnQsR1Ipnd2ioU9C +wpLOLVBSXowKoffYncX4/TaHTlf9aKFfmYMc8LXd6JLTZUBVypaFAgMBAAGjQjBA +MB0GA1UdDgQWBBSn15V360rDJ82TvjdMJoQhFH1dmDAOBgNVHQ8BAf8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQwFAAOCAgEANNLxFfOTAdRyi/Cr +CB8TPHO0sKvoeNlsupqvJuwQgOUNUzHd4/qMUSIkMze4GH46+ljoNOWM4KEfCUHS +Nz/Mywk1Qojp/BHXz0KqpHC2ccFTvcV0r8QiJGPPYoJ9yctRwYiQbVtcvvuZqLq2 +hrDpZgvlG2uv6iuGp9+oI0yWP09XQhgVg0Pxhia3KgPOC53opWgejG+9heMbUY/n +Fy8r0NZ4wi3dcojUZZ76mdR+55cKkgGapamEOgwqdD0zGMiH9+ik9YZCOf1rdSn8 +AAasoqUaVI7pUEkXZq9LBC2blIClVKuMVxdEnw/WaGRytEseAcfZm5TZg5mvEgUR +o5gi0vJXyiT5ujgVEki6Yzv8i5V41nIHVszN/J0c0MVkO2M0zwSZircweXq28sbV +2VR6hwt+TveE7BTziBYS8dWuChoJ7oat5av9rsMpeXTDAV8Rm991mcZK95uPbEns +IS+0AlmzLdBykLoLFHR4S8/BX1VyjlQrE876WAzTuyzZqZFh+PjxtnvevKnMkgTM +S2tfc4C2Ie1QT9d2h27O39K3vWKhfVhiaEVStj/eEtvtBGmedoiqAW3ahsdgG8NS +rDfsUHGAciohRQpTRzwZ643SWQTeJbDrHzVvYH3Xtca7CyeN4E1U5c8dJgFuOzXI +IBKJg/DS7Vg7NJ27MfUy/THzVho= +-----END CERTIFICATE-----'`)) + + // Sectigo Public Server Authentication Root E46 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T +ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN +MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG +A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT +ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC +WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ +6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B +Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa +qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q +4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== +-----END CERTIFICATE-----'`)) + + // Sectigo Public Server Authentication Root R46 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa +ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz +SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf +iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X +ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3 +IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS +VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE +SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu ++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt +8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L +HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt +zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c +mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ +YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52 +gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA +Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB +JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX +DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui +TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5 +dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 +LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp +0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY +QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL +-----END CERTIFICATE-----'`)) + + // USERTrust ECC Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE-----'`)) + + // USERTrust RSA Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE-----'`)) + + // SSL.com Client ECC Root CA 2022 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICQDCCAcagAwIBAgIQdvhIHq7wPHAf4D8lVAGD1TAKBggqhkjOPQQDAzBRMQsw +CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9T +U0wuY29tIENsaWVudCBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzAzMloX +DTQ2MDgxOTE2MzAzMVowUTELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw +b3JhdGlvbjEoMCYGA1UEAwwfU1NMLmNvbSBDbGllbnQgRUNDIFJvb3QgQ0EgMjAy +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABC1Tfp+LPrM2ulDizOvcuiaK04wGP2cP +7/UX5dSumkYqQQEHaedncfHCAzbG8CtSjs8UkmikPnBREmmNeKKCyikUwOSUIrJE +kmBvyASkZ9Wi0PPQ1+qOPA+60kBHkDTufaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAf +BgNVHSMEGDAWgBS3/i1ixYFTzVIaL11goMNd+7IcHDAdBgNVHQ4EFgQUt/4tYsWB +U81SGi9dYKDDXfuyHBwwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUC +ME0HES0R+7kmwyHdcuEX/MHPFOpJznGHjtZT3BHNXVSKr9kt9IxR6rxmR+J/lYNg +ZQIxAIwhTE+75bBQ35BiSebMkdv4P11xkQiOT5LJf6Zc6hN+7W3E6MMqb1wR4aXz +alqaTQ== +-----END CERTIFICATE-----'`)) + + // SSL.com Client RSA Root CA 2022 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFjzCCA3egAwIBAgIQdq/uiJMVRbZQU5uAnKTfmjANBgkqhkiG9w0BAQsFADBR +MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSgwJgYDVQQD +DB9TU0wuY29tIENsaWVudCBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzEw +N1oXDTQ2MDgxOTE2MzEwNlowUTELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBD +b3Jwb3JhdGlvbjEoMCYGA1UEAwwfU1NMLmNvbSBDbGllbnQgUlNBIFJvb3QgQ0Eg +MjAyMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALhY20Yw+8k/48jw +ATM04tpIqBjpIG6a1wHh1SmPMLQjauTLYrC+4p8gvT5UoDlox4Y3ZnQGBu90K9rc +n4SpUi+Q0u5+fPulIq1vcEZnlj0p1KO7VnsUBFnBIWNEHrIfElyQh2UNiPYeiCLi +Y1S78zb41n/c2v8pNanGbg5pWz/YvoKHFXBdsMdcEg9jpjjNz3O5ww6JJjcbP2Ic +MmnRm9n/VZAx3rFj3c/FdHf874ghU78AMRomLAAwpV9s4+T2AIrKmIecdAN6i2bs +fv2jjzUlXHils6T7PW2pivBsiIKL/UrQb+TXo7SONEk4vs5F5dIcyl7CNxSLzWZW +Mzed5WvsQ5JkoELadW/AFez5ab00uYp7+hb7Vf5SIOgEBFZWZfU3RJjIikbpt6y4 +6L5ijlQ2W/c7cL9d7i26X95CGYbwf4vrCMvYvuoOQkKgNnNXF+0y6tCN6Acbm5no +xJpiBA5I9zwSuvdYwZqM6cewIzZWNB3LbNq6B4Qd/dGsn+bCie/DuWwYs2mHV1+1 +DDhbpyEkKjunNJGetFTqKE/TwaOL5OYr1fKdv5thACLd1ktEHz9dVv7enHjMmVuq +5L2620NLrUwmTKNNNIpsdDYT22L8m7IFgf+uPwzN9hui9DnnyvVMXPtUdzWAWsAS +oRMBM2c9nYGhqfWFJFiIeOf042hVAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8w +HwYDVR0jBBgwFoAU8DhClDSpPAB/Uu45pfdLDbxqfSMwHQYDVR0OBBYEFPA4QpQ0 +qTwAf1LuOaX3Sw28an0jMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC +AgEAmU/b8OrWEfoq/cirbeQOc2LSQp8V/nxwUj9kh4IxP0VALuEinwZmKfyW0y2N +tjjH2fMnwVkpoIz2cyQPKCLXTmHdE93bnzJSk/tPzOo4PJhqA6sWryHRQq59RSvq +xM+KWZ+CcHY6+GImyRCXWEAkpC25LymAJ+GJa3LKSQhxN1MF8YDO00IC0vzC0ZQG +7gfi9oPif5/nu1bDW7/dlZMJHiTBzybNraSuwrRp56q17TeU6d3RY4VrmnpKVnbc +GYUo1OTGpNi4lkF30LRZ8UYFh4cCH2m5ghjQQ9km2hpnqNZ1durybQ5C/4gmom6E +/n5iG/DGPe3AHGrHkda4ADdJm7mEBaHNbjHWROpTi7pTmB2hkIrphfgb8pNYw8jc +miZPPiDPT0PzEIx/EGF6NsqqC33Mn0dEWa6llcaZU+MHaz1JELAY/10OhUMUS+dr +00q1smBh3GlJAiNd6JJxw5yfRWd5HtwyhrqqVTxkbzK1EEAV3nJAeOBucLtu6wno +OdmsupJ13UPKugGVrRqBKzrw48UvDBhNEMauwO3+BVJ/GQXLqa81CAw4IuT+VuVT +Pr/k1rPZCMM91TMygSTFqeFlEbgyMzBxGEkdGkXGmhSKWDkobvPLUblJJmR4A8eR +EYOpuZA0tm+qBZ6FKFeZvn8nBkliTaH8CeErRglMFJtWj0U= +-----END CERTIFICATE-----'`)) + + // SSL.com EV Root Certification Authority ECC + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE-----'`)) + + // SSL.com EV Root Certification Authority RSA R2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE-----'`)) + + // SSL.com Root Certification Authority ECC + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE-----'`)) + + // SSL.com Root Certification Authority RSA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE-----'`)) + + // SSL.com TLS ECC Root CA 2022 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT +U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 +MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh +dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm +acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN +SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME +GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW +uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp +15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN +b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== +-----END CERTIFICATE-----'`)) + + // SSL.com TLS RSA Root CA 2022 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO +MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD +DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX +DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw +b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP +L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY +t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins +S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3 +PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO +L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3 +R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w +dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS ++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS +d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG +AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f +gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z +NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt +hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM +QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf +R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ +DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW +P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy +lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq +bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w +AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q +r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji +Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU +98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= +-----END CERTIFICATE-----'`)) + + // SwissSign Gold CA - G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE-----'`)) + + // SwissSign Silver CA - G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE-----'`)) + + // TWCA CYBER Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQ +MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290 +IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5 +WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FO +LUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1sTs6P +40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxF +avcokPFhV8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/ +34bKS1PE2Y2yHer43CdTo0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684i +JkXXYJndzk834H/nY62wuFm40AZoNWDTNq5xQwTxaWV4fPMf88oon1oglWa0zbfu +j3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK/c/WMw+f+5eesRycnupf +Xtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkHIuNZW0CP +2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDA +S9TMfAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDA +oS/xUgXJP+92ZuJF2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzC +kHDXShi8fgGwsOsVHkQGzaRP6AzRwyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW +5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83QOGt4A1WNzAd +BgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB +AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0t +tGlTITVX1olNc79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn +68xDiBaiA9a5F/gZbG0jAn/xX9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNn +TKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDRIG4kqIQnoVesqlVYL9zZyvpoBJ7t +RCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq/p1hvIbZv97Tujqx +f36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0RFxbI +Qh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz +8ppy6rBePm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4 +NxKfKjLji7gh7MMrZQzvIt6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzX +xeSDwWrruoBa3lwtcHb4yOWHh8qgnaHlIhInD0Q9HWzq1MKLL295q39QpsQZp6F6 +t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X +-----END CERTIFICATE-----'`)) + + // TWCA Global Root CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE-----'`)) + + // TWCA Global Root CA G2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFlTCCA32gAwIBAgIQQAE0jMIAAAAAAAAAAZdY9DANBgkqhkiG9w0BAQwFADBU +MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290 +IENBMR8wHQYDVQQDExZUV0NBIEdsb2JhbCBSb290IENBIEcyMB4XDTIyMTEyMjA2 +NDIyMVoXDTQ3MTEyMjE1NTk1OVowVDELMAkGA1UEBhMCVFcxEjAQBgNVBAoTCVRB +SVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEfMB0GA1UEAxMWVFdDQSBHbG9iYWwg +Um9vdCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKoO1SCS +Aa2C+QwIkTRrihbQRhb/A7jYjeqTNPv/K739bqrcm/KGgVX1iRzEjXVqWHiREx4C +E3A9774K5wCPuDHldMUwvv991pnlwkKjzyHWswh/kdVh5qKVEA3vXpcLSTjVIrDX +i1lvnzWbf9KRzHp/u6Cf3lUz9kuNCup9CcB53L1E4v4c52QhKM8ESuK0v4Z5KrsO +k8mPXqwwOVKQB7nqnCZCFMRnRv7RGmihPlAZoyYKJymQwva063OaeB7hmPRlDDUh +BvgL3mLlTcGzXdm5+mGXKuPqx0RVJJL+Eqc/xHfgLQKBB9X7feYQnjq0qO/s+1Dq +Nc/MfrtCuURsUum/KnIfP96bcOncWsU7u7/wWYWvL8GwFHkFrHWfJfURJwZgIcdt +Zb6oiZzlrEbf+F1EA41gvfexDcwv70FUL+5rlblOfDTfO/l3nX3NBz0cBjMSgOxy +nPItgtrVO8TH+QTDZAJ89TVgp7RGKS4b76VYgC56iVE4Njz9oXe4gDDQit6NpzQm +7CO7GFUYNkXu7QEGqk2/ZAzKmJcaMQJm+HhoW4jfCajnm/o0bXAcIa0Ii/Khtqx2 +ar/xgCUAvjweTa65PLaVY71rfkcSkFVFEY3sFx/BvieBk1djaQAmd4vDWeV70Q1E +8qjw94WaBffCLnCak4XYlZAxkFSm7AufN0UPAgMBAAGjYzBhMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFJKM1DbRW0dTxHENhN1k +KvU2ZEDnMB0GA1UdDgQWBBSSjNQ20VtHU8RxDYTdZCr1NmRA5zANBgkqhkiG9w0B +AQwFAAOCAgEAJfxL2pC02nXnQTqB0ab+oGrzGHFiaiQIi6l6TclVzs8QKC4EGZYF +z10CICo7s1U/Ac1CzbJ37f9183x325alz4xnBvSkm3L2IUkJmKMyXndaYwnvYkOX +Aji16jwYUGj8WVvZedTx5FZIE1bY03ELXniUOBFF+gUX9Q51HmJSYUa6LhmthrSI +D7FQ5kAANBqVnZPgUfnUVUbplTwlhi6X1wExGETsHGDpfWmvMviXQCUkto0aVTzF +t/e8BlI7cTBwPnEXfvFmBF5dvIoxQ6aSHXtU0qU2i2+N1l7a1MMuHd85VWCCMJ4n +/46A3WNMplU12NAzqYBtPl6dzKhngGb6mVcMUsoZdbA4NVUqgcWMHlbXX5DyINja +4GZx6bJ4q2e5JG5rNnL8b439f3I5KGdSkQUfV2XSo6cNYfqh59U1RpXJBof2MOwy +UamsVsAhTqMUdAU6vOO/bT1OP16lpG0pv4RRdVOOhhr1UXAqDRxOQOH9o+OlK2eQ +ksdsroW/OpsXFcqcKpPUTTkNvCAIo42IbAkNjK5EIU3JcezYJtcXni0RGDyjIn24 +J1S/aMg7QsyPXk7n3MLF+mpED41WiHrfiYRsoLM+PfFlAAmI6irrQM6zXawyF67B +m+nQwfVJlN2nznxaB+uuIJwXMJJpk3Lzmltxm/5q33owaY6zLtsPLN0= +-----END CERTIFICATE-----'`)) + + // TWCA Root Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE-----'`)) + + // Telia Root CA v2 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx +CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE +AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 +NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ +MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq +AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 +vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 +lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD +n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT +7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o +6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC +TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 +WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R +DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI +pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj +YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy +rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi +0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM +A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS +SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K +TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF +6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er +3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt +Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT +VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW +ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA +rBPuUBQemMc= +-----END CERTIFICATE-----'`)) + + // TeliaSonera Root CA v1 + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE-----'`)) + + // Secure Global CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE-----'`)) + + // SecureTrust CA + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE-----'`)) + + // Trustwave Global Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw +CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x +ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 +c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx +OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI +SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn +swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu +7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8 +1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW +80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP +JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l +RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw +hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10 +coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc +BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n +twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud +DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W +0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe +uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q +lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB +aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE +sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT +MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe +qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh +VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 +h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 +EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK +yeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE-----'`)) + + // Trustwave Global ECC P256 Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN +FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w +DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw +CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh +DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE-----'`)) + + // Trustwave Global ECC P384 Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB +BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ +j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF +1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G +A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 +AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC +MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu +Sw== +-----END CERTIFICATE-----'`)) + + // XRamp Global Certification Authority + mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE-----'`)) +} diff --git a/common/certificate/store.go b/common/certificate/store.go new file mode 100644 index 00000000..34f20019 --- /dev/null +++ b/common/certificate/store.go @@ -0,0 +1,185 @@ +package certificate + +import ( + "context" + "crypto/x509" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/sagernet/fswatch" + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/experimental/libbox/platform" + "github.com/sagernet/sing-box/option" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + "github.com/sagernet/sing/service" +) + +var _ adapter.CertificateStore = (*Store)(nil) + +type Store struct { + systemPool *x509.CertPool + currentPool *x509.CertPool + certificate string + certificatePaths []string + certificateDirectoryPaths []string + watcher *fswatch.Watcher +} + +func NewStore(ctx context.Context, logger logger.Logger, options option.CertificateOptions) (*Store, error) { + var systemPool *x509.CertPool + switch options.Store { + case C.CertificateStoreSystem, "": + systemPool = x509.NewCertPool() + platformInterface := service.FromContext[platform.Interface](ctx) + var systemValid bool + if platformInterface != nil { + for _, cert := range platformInterface.SystemCertificates() { + if systemPool.AppendCertsFromPEM([]byte(cert)) { + systemValid = true + } + } + } + if !systemValid { + certPool, err := x509.SystemCertPool() + if err != nil { + return nil, err + } + systemPool = certPool + } + case C.CertificateStoreMozilla: + systemPool = mozillaIncluded + case C.CertificateStoreNone: + systemPool = nil + default: + return nil, E.New("unknown certificate store: ", options.Store) + } + store := &Store{ + systemPool: systemPool, + certificate: strings.Join(options.Certificate, "\n"), + certificatePaths: options.CertificatePath, + certificateDirectoryPaths: options.CertificateDirectoryPath, + } + var watchPaths []string + for _, target := range options.CertificatePath { + watchPaths = append(watchPaths, target) + } + for _, target := range options.CertificateDirectoryPath { + watchPaths = append(watchPaths, target) + } + if len(watchPaths) > 0 { + watcher, err := fswatch.NewWatcher(fswatch.Options{ + Path: watchPaths, + Logger: logger, + Callback: func(_ string) { + err := store.update() + if err != nil { + logger.Error(E.Cause(err, "reload certificates")) + } + }, + }) + if err != nil { + return nil, E.Cause(err, "fswatch: create fsnotify watcher") + } + store.watcher = watcher + } + err := store.update() + if err != nil { + return nil, E.Cause(err, "initializing certificate store") + } + return store, nil +} + +func (s *Store) Name() string { + return "certificate" +} + +func (s *Store) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + if s.watcher != nil { + return s.watcher.Start() + } + return nil +} + +func (s *Store) Close() error { + if s.watcher != nil { + return s.watcher.Close() + } + return nil +} + +func (s *Store) Pool() *x509.CertPool { + return s.currentPool +} + +func (s *Store) update() error { + 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)) { + return E.New("invalid certificate PEM strings") + } + } + for _, path := range s.certificatePaths { + pemContent, err := os.ReadFile(path) + if err != nil { + return err + } + if !currentPool.AppendCertsFromPEM(pemContent) { + return E.New("invalid certificate PEM file: ", path) + } + } + var firstErr error + for _, directoryPath := range s.certificateDirectoryPaths { + directoryEntries, err := readUniqueDirectoryEntries(directoryPath) + if err != nil { + if firstErr == nil && !os.IsNotExist(err) { + firstErr = E.Cause(err, "invalid certificate directory: ", directoryPath) + } + continue + } + for _, directoryEntry := range directoryEntries { + pemContent, err := os.ReadFile(filepath.Join(directoryPath, directoryEntry.Name())) + if err == nil { + currentPool.AppendCertsFromPEM(pemContent) + } + } + } + if firstErr != nil { + return firstErr + } + s.currentPool = currentPool + return nil +} + +func readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) { + files, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + uniq := files[:0] + for _, f := range files { + if !isSameDirSymlink(f, dir) { + uniq = append(uniq, f) + } + } + return uniq, nil +} + +func isSameDirSymlink(f fs.DirEntry, dir string) bool { + if f.Type()&fs.ModeSymlink == 0 { + return false + } + target, err := os.Readlink(filepath.Join(dir, f.Name())) + return err == nil && !strings.Contains(target, "/") +} diff --git a/common/tls/ech_client.go b/common/tls/ech_client.go index fff1873d..80219350 100644 --- a/common/tls/ech_client.go +++ b/common/tls/ech_client.go @@ -100,6 +100,7 @@ func NewECHClient(ctx context.Context, serverAddress string, options option.Outb var tlsConfig cftls.Config tlsConfig.Time = ntp.TimeFuncFromContext(ctx) + tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx) if options.DisableSNI { tlsConfig.ServerName = "127.0.0.1" } else { diff --git a/common/tls/ech_server.go b/common/tls/ech_server.go index d41783fe..03f5d876 100644 --- a/common/tls/ech_server.go +++ b/common/tls/ech_server.go @@ -90,7 +90,7 @@ func (c *echServerConfig) startWatcher() error { Callback: func(path string) { err := c.credentialsUpdated(path) if err != nil { - c.logger.Error(E.Cause(err, "reload credentials from ", path)) + c.logger.Error(E.Cause(err, "reload credentials")) } }, }) diff --git a/common/tls/reality_client.go b/common/tls/reality_client.go index f9a11a0b..748567b5 100644 --- a/common/tls/reality_client.go +++ b/common/tls/reality_client.go @@ -27,9 +27,11 @@ import ( "time" "unsafe" + "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common/debug" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/ntp" aTLS "github.com/sagernet/sing/common/tls" utls "github.com/sagernet/utls" @@ -40,6 +42,7 @@ import ( var _ ConfigCompat = (*RealityClientConfig)(nil) type RealityClientConfig struct { + ctx context.Context uClient *UTLSClientConfig publicKey []byte shortID [8]byte @@ -70,7 +73,7 @@ func NewRealityClient(ctx context.Context, serverAddress string, options option. if decodedLen > 8 { return nil, E.New("invalid short_id") } - return &RealityClientConfig{uClient, publicKey, shortID}, nil + return &RealityClientConfig{ctx, uClient, publicKey, shortID}, nil } func (e *RealityClientConfig) ServerName() string { @@ -180,20 +183,24 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn } if !verifier.verified { - go realityClientFallback(uConn, e.uClient.ServerName(), e.uClient.id) + go realityClientFallback(e.ctx, uConn, e.uClient.ServerName(), e.uClient.id) return nil, E.New("reality verification failed") } return &realityClientConnWrapper{uConn}, nil } -func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) { +func realityClientFallback(ctx context.Context, uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) { defer uConn.Close() client := &http.Client{ Transport: &http2.Transport{ DialTLSContext: func(ctx context.Context, network, addr string, config *tls.Config) (net.Conn, error) { return uConn, nil }, + TLSClientConfig: &tls.Config{ + Time: ntp.TimeFuncFromContext(ctx), + RootCAs: adapter.RootPoolFromContext(ctx), + }, }, } request, _ := http.NewRequest("GET", "https://"+serverName, nil) @@ -213,6 +220,7 @@ func (e *RealityClientConfig) SetSessionIDGenerator(generator func(clientHello [ func (e *RealityClientConfig) Clone() Config { return &RealityClientConfig{ + e.ctx, e.uClient.Clone().(*UTLSClientConfig), e.publicKey, e.shortID, diff --git a/common/tls/std_client.go b/common/tls/std_client.go index 7cd130a6..78f4e5cf 100644 --- a/common/tls/std_client.go +++ b/common/tls/std_client.go @@ -8,6 +8,7 @@ import ( "os" "strings" + "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/ntp" @@ -58,6 +59,7 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb var tlsConfig tls.Config tlsConfig.Time = ntp.TimeFuncFromContext(ctx) + tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx) if options.DisableSNI { tlsConfig.ServerName = "127.0.0.1" } else { diff --git a/common/tls/std_server.go b/common/tls/std_server.go index 949521d7..f62f3d12 100644 --- a/common/tls/std_server.go +++ b/common/tls/std_server.go @@ -100,7 +100,7 @@ func (c *STDServerConfig) startWatcher() error { Callback: func(path string) { err := c.certificateUpdated(path) if err != nil { - c.logger.Error(err) + c.logger.Error(E.Cause(err, "reload certificate")) } }, }) diff --git a/common/tls/utls_client.go b/common/tls/utls_client.go index 15103f05..fe8e4296 100644 --- a/common/tls/utls_client.go +++ b/common/tls/utls_client.go @@ -12,6 +12,7 @@ import ( "os" "strings" + "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/ntp" @@ -130,6 +131,7 @@ func NewUTLSClient(ctx context.Context, serverAddress string, options option.Out var tlsConfig utls.Config tlsConfig.Time = ntp.TimeFuncFromContext(ctx) + tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx) if options.DisableSNI { tlsConfig.ServerName = "127.0.0.1" } else { diff --git a/common/urltest/urltest.go b/common/urltest/urltest.go index 9efd0404..cfe1e532 100644 --- a/common/urltest/urltest.go +++ b/common/urltest/urltest.go @@ -2,32 +2,32 @@ package urltest import ( "context" + "crypto/tls" "net" "net/http" "net/url" "sync" "time" + "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing/common" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/ntp" ) -type History struct { - Time time.Time `json:"time"` - Delay uint16 `json:"delay"` -} +var _ adapter.URLTestHistoryStorage = (*HistoryStorage)(nil) type HistoryStorage struct { access sync.RWMutex - delayHistory map[string]*History + delayHistory map[string]*adapter.URLTestHistory updateHook chan<- struct{} } func NewHistoryStorage() *HistoryStorage { return &HistoryStorage{ - delayHistory: make(map[string]*History), + delayHistory: make(map[string]*adapter.URLTestHistory), } } @@ -35,7 +35,7 @@ func (s *HistoryStorage) SetHook(hook chan<- struct{}) { s.updateHook = hook } -func (s *HistoryStorage) LoadURLTestHistory(tag string) *History { +func (s *HistoryStorage) LoadURLTestHistory(tag string) *adapter.URLTestHistory { if s == nil { return nil } @@ -51,7 +51,7 @@ func (s *HistoryStorage) DeleteURLTestHistory(tag string) { s.notifyUpdated() } -func (s *HistoryStorage) StoreURLTestHistory(tag string, history *History) { +func (s *HistoryStorage) StoreURLTestHistory(tag string, history *adapter.URLTestHistory) { s.access.Lock() s.delayHistory[tag] = history s.access.Unlock() @@ -110,6 +110,10 @@ func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err e DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return instance, nil }, + TLSClientConfig: &tls.Config{ + Time: ntp.TimeFuncFromContext(ctx), + RootCAs: adapter.RootPoolFromContext(ctx), + }, }, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse diff --git a/constant/certificate.go b/constant/certificate.go new file mode 100644 index 00000000..7138242c --- /dev/null +++ b/constant/certificate.go @@ -0,0 +1,7 @@ +package constant + +const ( + CertificateStoreSystem = "system" + CertificateStoreMozilla = "mozilla" + CertificateStoreNone = "none" +) diff --git a/experimental/clashapi/api_meta_group.go b/experimental/clashapi/api_meta_group.go index 8f09ced9..31dbdaf6 100644 --- a/experimental/clashapi/api_meta_group.go +++ b/experimental/clashapi/api_meta_group.go @@ -111,7 +111,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request) server.urlTestHistory.DeleteURLTestHistory(realTag) } else { server.logger.Debug("outbound ", tag, " available: ", t, "ms") - server.urlTestHistory.StoreURLTestHistory(realTag, &urltest.History{ + server.urlTestHistory.StoreURLTestHistory(realTag, &adapter.URLTestHistory{ Time: time.Now(), Delay: t, }) diff --git a/experimental/clashapi/proxies.go b/experimental/clashapi/proxies.go index 6246b9da..ef88ff37 100644 --- a/experimental/clashapi/proxies.go +++ b/experimental/clashapi/proxies.go @@ -72,9 +72,9 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject { info.Put("udp", common.Contains(detour.Network(), N.NetworkUDP)) delayHistory := server.urlTestHistory.LoadURLTestHistory(adapter.OutboundTag(detour)) if delayHistory != nil { - info.Put("history", []*urltest.History{delayHistory}) + info.Put("history", []*adapter.URLTestHistory{delayHistory}) } else { - info.Put("history", []*urltest.History{}) + info.Put("history", []*adapter.URLTestHistory{}) } if group, isGroup := detour.(adapter.OutboundGroup); isGroup { info.Put("now", group.Now()) @@ -116,7 +116,7 @@ func getProxies(server *Server) func(w http.ResponseWriter, r *http.Request) { "type": "Fallback", "name": "GLOBAL", "udp": true, - "history": []*urltest.History{}, + "history": []*adapter.URLTestHistory{}, "all": allProxies, "now": defaultTag, }) @@ -208,7 +208,7 @@ func getProxyDelay(server *Server) func(w http.ResponseWriter, r *http.Request) if err != nil { server.urlTestHistory.DeleteURLTestHistory(realTag) } else { - server.urlTestHistory.StoreURLTestHistory(realTag, &urltest.History{ + server.urlTestHistory.StoreURLTestHistory(realTag, &adapter.URLTestHistory{ Time: time.Now(), Delay: delay, }) diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index d01e180a..e6d8c4cf 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -48,7 +48,7 @@ type Server struct { logger log.Logger httpServer *http.Server trafficManager *trafficontrol.Manager - urlTestHistory *urltest.HistoryStorage + urlTestHistory adapter.URLTestHistoryStorage mode string modeList []string modeUpdateHook chan<- struct{} @@ -79,7 +79,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op externalUIDownloadURL: options.ExternalUIDownloadURL, externalUIDownloadDetour: options.ExternalUIDownloadDetour, } - s.urlTestHistory = service.PtrFromContext[urltest.HistoryStorage](ctx) + s.urlTestHistory = service.FromContext[adapter.URLTestHistoryStorage](ctx) if s.urlTestHistory == nil { s.urlTestHistory = urltest.NewHistoryStorage() } @@ -234,7 +234,7 @@ func (s *Server) SetMode(newMode string) { s.logger.Info("updated mode: ", newMode) } -func (s *Server) HistoryStorage() *urltest.HistoryStorage { +func (s *Server) HistoryStorage() adapter.URLTestHistoryStorage { return s.urlTestHistory } diff --git a/experimental/clashapi/server_resources.go b/experimental/clashapi/server_resources.go index 7cf0000b..ad9fff53 100644 --- a/experimental/clashapi/server_resources.go +++ b/experimental/clashapi/server_resources.go @@ -3,6 +3,7 @@ package clashapi import ( "archive/zip" "context" + "crypto/tls" "io" "net" "net/http" @@ -15,6 +16,7 @@ import ( "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/service/filemanager" ) @@ -60,6 +62,10 @@ func (s *Server) downloadExternalUI() error { DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) }, + TLSClientConfig: &tls.Config{ + Time: ntp.TimeFuncFromContext(s.ctx), + RootCAs: adapter.RootPoolFromContext(s.ctx), + }, }, } defer httpClient.CloseIdleConnections() diff --git a/experimental/libbox/command_urltest.go b/experimental/libbox/command_urltest.go index c30d996e..5dcb3d67 100644 --- a/experimental/libbox/command_urltest.go +++ b/experimental/libbox/command_urltest.go @@ -76,7 +76,7 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error { if err != nil { historyStorage.DeleteURLTestHistory(outboundTag) } else { - historyStorage.StoreURLTestHistory(outboundTag, &urltest.History{ + historyStorage.StoreURLTestHistory(outboundTag, &adapter.URLTestHistory{ Time: time.Now(), Delay: t, }) diff --git a/experimental/libbox/config.go b/experimental/libbox/config.go index 603d8b6d..d3149dc2 100644 --- a/experimental/libbox/config.go +++ b/experimental/libbox/config.go @@ -108,6 +108,10 @@ func (s *platformInterfaceStub) ReadWIFIState() adapter.WIFIState { return adapter.WIFIState{} } +func (s *platformInterfaceStub) SystemCertificates() []string { + return nil +} + func (s *platformInterfaceStub) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*process.Info, error) { return nil, os.ErrInvalid } diff --git a/experimental/libbox/platform.go b/experimental/libbox/platform.go index f0590367..affcad38 100644 --- a/experimental/libbox/platform.go +++ b/experimental/libbox/platform.go @@ -21,6 +21,7 @@ type PlatformInterface interface { UnderNetworkExtension() bool IncludeAllNetworks() bool ReadWIFIState() *WIFIState + SystemCertificates() StringIterator ClearDNSCache() SendNotification(notification *Notification) error } diff --git a/experimental/libbox/platform/interface.go b/experimental/libbox/platform/interface.go index ef37daea..35b0830b 100644 --- a/experimental/libbox/platform/interface.go +++ b/experimental/libbox/platform/interface.go @@ -19,6 +19,7 @@ type Interface interface { IncludeAllNetworks() bool ClearDNSCache() ReadWIFIState() adapter.WIFIState + SystemCertificates() []string process.Searcher SendNotification(notification *Notification) error } diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index bd891f60..48d614ff 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -34,7 +34,7 @@ import ( type BoxService struct { ctx context.Context cancel context.CancelFunc - urlTestHistoryStorage *urltest.HistoryStorage + urlTestHistoryStorage adapter.URLTestHistoryStorage instance *box.Box clashServer adapter.ClashServer pauseManager pause.Manager @@ -233,6 +233,10 @@ func (w *platformInterfaceWrapper) ReadWIFIState() adapter.WIFIState { return (adapter.WIFIState)(*wifiState) } +func (w *platformInterfaceWrapper) SystemCertificates() []string { + return iteratorToArray[string](w.iif.SystemCertificates()) +} + func (w *platformInterfaceWrapper) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*process.Info, error) { var uid int32 if w.useProcFS { diff --git a/option/certificate.go b/option/certificate.go new file mode 100644 index 00000000..ab524b99 --- /dev/null +++ b/option/certificate.go @@ -0,0 +1,36 @@ +package option + +import ( + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badoption" +) + +type _CertificateOptions struct { + Store string `json:"store,omitempty"` + Certificate badoption.Listable[string] `json:"certificate,omitempty"` + CertificatePath badoption.Listable[string] `json:"certificate_path,omitempty"` + CertificateDirectoryPath badoption.Listable[string] `json:"certificate_directory_path,omitempty"` +} + +type CertificateOptions _CertificateOptions + +func (o CertificateOptions) MarshalJSON() ([]byte, error) { + switch o.Store { + case C.CertificateStoreSystem: + o.Store = "" + } + return json.Marshal((*_CertificateOptions)(&o)) +} + +func (o *CertificateOptions) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, (*_CertificateOptions)(o)) + if err != nil { + return err + } + switch o.Store { + case C.CertificateStoreSystem, "": + o.Store = C.CertificateStoreSystem + } + return nil +} diff --git a/option/options.go b/option/options.go index 85ff81eb..c0e579cc 100644 --- a/option/options.go +++ b/option/options.go @@ -14,6 +14,7 @@ type _Options struct { Log *LogOptions `json:"log,omitempty"` DNS *DNSOptions `json:"dns,omitempty"` NTP *NTPOptions `json:"ntp,omitempty"` + Certificate *CertificateOptions `json:"certificate,omitempty"` Endpoints []Endpoint `json:"endpoints,omitempty"` Inbounds []Inbound `json:"inbounds,omitempty"` Outbounds []Outbound `json:"outbounds,omitempty"` diff --git a/protocol/group/urltest.go b/protocol/group/urltest.go index c1a5c597..f44698e1 100644 --- a/protocol/group/urltest.go +++ b/protocol/group/urltest.go @@ -182,7 +182,7 @@ type URLTestGroup struct { interval time.Duration tolerance uint16 idleTimeout time.Duration - history *urltest.HistoryStorage + history adapter.URLTestHistoryStorage checking atomic.Bool selectedOutboundTCP adapter.Outbound selectedOutboundUDP adapter.Outbound @@ -208,8 +208,9 @@ func NewURLTestGroup(ctx context.Context, outboundManager adapter.OutboundManage if interval > idleTimeout { return nil, E.New("interval must be less or equal than idle_timeout") } - var history *urltest.HistoryStorage - if history = service.PtrFromContext[urltest.HistoryStorage](ctx); history != nil { + var history adapter.URLTestHistoryStorage + if historyFromCtx := service.PtrFromContext[urltest.HistoryStorage](ctx); historyFromCtx != nil { + history = historyFromCtx } else if clashServer := service.FromContext[adapter.ClashServer](ctx); clashServer != nil { history = clashServer.HistoryStorage() } else { @@ -376,7 +377,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint g.history.DeleteURLTestHistory(realTag) } else { g.logger.Debug("outbound ", tag, " available: ", t, "ms") - g.history.StoreURLTestHistory(realTag, &urltest.History{ + g.history.StoreURLTestHistory(realTag, &adapter.URLTestHistory{ Time: time.Now(), Delay: t, }) diff --git a/route/router.go b/route/router.go index 0872421f..ae2ecb55 100644 --- a/route/router.go +++ b/route/router.go @@ -90,7 +90,7 @@ func (r *Router) Start(stage adapter.StartStage) error { var cacheContext *adapter.HTTPStartContext if len(r.ruleSets) > 0 { monitor.Start("initialize rule-set") - cacheContext = adapter.NewHTTPStartContext() + cacheContext = adapter.NewHTTPStartContext(r.ctx) var ruleSetStartGroup task.Group for i, ruleSet := range r.ruleSets { ruleSetInPlace := ruleSet diff --git a/route/rule/rule_set_remote.go b/route/rule/rule_set_remote.go index c29d6616..16a95bb6 100644 --- a/route/rule/rule_set_remote.go +++ b/route/rule/rule_set_remote.go @@ -3,6 +3,7 @@ package rule import ( "bytes" "context" + "crypto/tls" "io" "net" "net/http" @@ -23,6 +24,7 @@ import ( "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/common/x/list" "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/pause" @@ -238,6 +240,10 @@ func (s *RemoteRuleSet) fetch(ctx context.Context, startContext *adapter.HTTPSta DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return s.dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) }, + TLSClientConfig: &tls.Config{ + Time: ntp.TimeFuncFromContext(s.ctx), + RootCAs: adapter.RootPoolFromContext(s.ctx), + }, }, } } From 0e0ca7508dd704d08eb380d380105920f78c36c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 25 Jan 2025 20:35:10 +0800 Subject: [PATCH 21/94] documentation: Refactor DNS --- docs/configuration/dns/fakeip.md | 8 + docs/configuration/dns/fakeip.zh.md | 8 + docs/configuration/dns/index.md | 2 - docs/configuration/dns/index.zh.md | 2 - docs/configuration/dns/rule_action.md | 11 + docs/configuration/dns/rule_action.zh.md | 14 +- docs/configuration/dns/server/dhcp.md | 38 ++ docs/configuration/dns/server/fakeip.md | 35 ++ docs/configuration/dns/server/http3.md | 71 +++ docs/configuration/dns/server/https.md | 71 +++ docs/configuration/dns/server/index.md | 46 ++ .../dns/{server.md => server/legacy.md} | 8 + .../dns/{server.zh.md => server/legacy.zh.md} | 8 + docs/configuration/dns/server/local.md | 33 ++ docs/configuration/dns/server/predefined.md | 93 ++++ docs/configuration/dns/server/quic.md | 58 ++ docs/configuration/dns/server/tcp.md | 52 ++ docs/configuration/dns/server/tls.md | 58 ++ docs/configuration/dns/server/udp.md | 52 ++ docs/deprecated.md | 9 + docs/deprecated.zh.md | 7 + docs/migration.md | 509 +++++++++++++++++ docs/migration.zh.md | 511 +++++++++++++++++- mkdocs.yml | 14 +- 24 files changed, 1710 insertions(+), 8 deletions(-) create mode 100644 docs/configuration/dns/server/dhcp.md create mode 100644 docs/configuration/dns/server/fakeip.md create mode 100644 docs/configuration/dns/server/http3.md create mode 100644 docs/configuration/dns/server/https.md create mode 100644 docs/configuration/dns/server/index.md rename docs/configuration/dns/{server.md => server/legacy.md} (93%) rename docs/configuration/dns/{server.zh.md => server/legacy.zh.md} (92%) create mode 100644 docs/configuration/dns/server/local.md create mode 100644 docs/configuration/dns/server/predefined.md create mode 100644 docs/configuration/dns/server/quic.md create mode 100644 docs/configuration/dns/server/tcp.md create mode 100644 docs/configuration/dns/server/tls.md create mode 100644 docs/configuration/dns/server/udp.md diff --git a/docs/configuration/dns/fakeip.md b/docs/configuration/dns/fakeip.md index 63490ac1..f9204d34 100644 --- a/docs/configuration/dns/fakeip.md +++ b/docs/configuration/dns/fakeip.md @@ -1,3 +1,11 @@ +--- +icon: material/delete-clock +--- + +!!! failure "Deprecated in sing-box 1.12.0" + + Legacy fake-ip configuration is deprecated and will be removed in sing-box 1.14.0, check [Migration](/migration/#migrate-to-new-dns-servers). + ### Structure ```json diff --git a/docs/configuration/dns/fakeip.zh.md b/docs/configuration/dns/fakeip.zh.md index 10c6dc68..e4d77b35 100644 --- a/docs/configuration/dns/fakeip.zh.md +++ b/docs/configuration/dns/fakeip.zh.md @@ -1,3 +1,11 @@ +--- +icon: material/delete-clock +--- + +!!! failure "已在 sing-box 1.12.0 废弃" + + 旧的 fake-ip 配置已废弃且将在 sing-box 1.14.0 中被移除,参阅 [迁移指南](/migration/#migrate-to-new-dns-servers)。 + ### 结构 ```json diff --git a/docs/configuration/dns/index.md b/docs/configuration/dns/index.md index 0756281d..43c7d573 100644 --- a/docs/configuration/dns/index.md +++ b/docs/configuration/dns/index.md @@ -49,8 +49,6 @@ Default domain strategy for resolving the domain names. One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`. -Take no effect if `server.strategy` is set. - #### disable_cache Disable dns cache. diff --git a/docs/configuration/dns/index.zh.md b/docs/configuration/dns/index.zh.md index 76c07b6a..8ed6a854 100644 --- a/docs/configuration/dns/index.zh.md +++ b/docs/configuration/dns/index.zh.md @@ -48,8 +48,6 @@ icon: material/new-box 可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。 -如果设置了 `server.strategy`,则不生效。 - #### disable_cache 禁用 DNS 缓存。 diff --git a/docs/configuration/dns/rule_action.md b/docs/configuration/dns/rule_action.md index af19131f..c3f8c2cb 100644 --- a/docs/configuration/dns/rule_action.md +++ b/docs/configuration/dns/rule_action.md @@ -2,6 +2,10 @@ icon: material/new-box --- +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [strategy](#strategy) + !!! question "Since sing-box 1.11.0" ### route @@ -10,6 +14,7 @@ icon: material/new-box { "action": "route", // default "server": "", + "strategy": "", "disable_cache": false, "rewrite_ttl": 0, "client_subnet": null @@ -24,6 +29,12 @@ icon: material/new-box Tag of target server. +#### strategy + +Set domain strategy for this query. + +One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`. + #### disable_cache Disable cache and save cache in this query. diff --git a/docs/configuration/dns/rule_action.zh.md b/docs/configuration/dns/rule_action.zh.md index 219a5fd7..427f8a8d 100644 --- a/docs/configuration/dns/rule_action.zh.md +++ b/docs/configuration/dns/rule_action.zh.md @@ -2,6 +2,10 @@ icon: material/new-box --- +!!! quote "sing-box 1.12.0 中的更改" + + :material-plus: [strategy](#strategy) + !!! question "自 sing-box 1.11.0 起" ### route @@ -10,8 +14,8 @@ icon: material/new-box { "action": "route", // 默认 "server": "", - - // 兼容性 + + "strategy": "", "disable_cache": false, "rewrite_ttl": 0, "client_subnet": null @@ -26,6 +30,12 @@ icon: material/new-box 目标 DNS 服务器的标签。 +#### strategy + +为此查询设置域名策略。 + +可选项:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。 + #### disable_cache 在此查询中禁用缓存。 diff --git a/docs/configuration/dns/server/dhcp.md b/docs/configuration/dns/server/dhcp.md new file mode 100644 index 00000000..b26da2a5 --- /dev/null +++ b/docs/configuration/dns/server/dhcp.md @@ -0,0 +1,38 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# DHCP + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "dhcp", + "tag": "", + + "interface": "", + + // Dial Fields + } + ] + } +} +``` + +### Fields + +#### interface + +Interface name to listen on. + +Tge default interface will be used by default. + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/dns/server/fakeip.md b/docs/configuration/dns/server/fakeip.md new file mode 100644 index 00000000..6d2bba43 --- /dev/null +++ b/docs/configuration/dns/server/fakeip.md @@ -0,0 +1,35 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# Fake IP + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "fakeip", + "tag": "", + + "inet4_range": "198.18.0.0/15", + "inet6_range": "fc00::/18" + } + ] + } +} +``` + +### Fields + +#### inet4_range + +IPv4 address range for FakeIP. + +#### inet6_address + +IPv6 address range for FakeIP. diff --git a/docs/configuration/dns/server/http3.md b/docs/configuration/dns/server/http3.md new file mode 100644 index 00000000..89f000e8 --- /dev/null +++ b/docs/configuration/dns/server/http3.md @@ -0,0 +1,71 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# DNS over HTTP3 (DoH3) + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "h3", + "tag": "", + + "server": "", + "server_port": 443, + + "path": "", + "headers": {}, + + "tls": {}, + + // Dial Fields + } + ] + } +} +``` + +!!! info "Difference from legacy H3 server" + + * The old server uses default outbound by default unless detour is specified; the new one uses dialer just like outbound, which is equivalent to using an empty direct outbound by default. + * The old server uses `address_resolver` and `address_strategy` to resolve the domain name in the server; the new one uses `domain_resolver` and `domain_strategy` in [Dial Fields](/configuration/shared/dial/) instead. + +### Fields + +#### server + +==Required== + +The address of the DNS server. + +If domain name is used, `domain_resolver` must also be set to resolve IP address. + +#### server_port + +The port of the DNS server. + +`853` will be used by default. + +#### path + +The path of the DNS server. + +`/dns-query` will be used by default. + +#### headers + +Additional headers to be sent to the DNS server. + +#### tls + +TLS configuration, see [TLS](/configuration/shared/tls/#outbound). + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/dns/server/https.md b/docs/configuration/dns/server/https.md new file mode 100644 index 00000000..93dc71a4 --- /dev/null +++ b/docs/configuration/dns/server/https.md @@ -0,0 +1,71 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# DNS over HTTPS (DoH) + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "https", + "tag": "", + + "server": "", + "server_port": 443, + + "path": "", + "headers": {}, + + "tls": {}, + + // Dial Fields + } + ] + } +} +``` + +!!! info "Difference from legacy HTTPS server" + + * The old server uses default outbound by default unless detour is specified; the new one uses dialer just like outbound, which is equivalent to using an empty direct outbound by default. + * The old server uses `address_resolver` and `address_strategy` to resolve the domain name in the server; the new one uses `domain_resolver` and `domain_strategy` in [Dial Fields](/configuration/shared/dial/) instead. + +### Fields + +#### server + +==Required== + +The address of the DNS server. + +If domain name is used, `domain_resolver` must also be set to resolve IP address. + +#### server_port + +The port of the DNS server. + +`853` will be used by default. + +#### path + +The path of the DNS server. + +`/dns-query` will be used by default. + +#### headers + +Additional headers to be sent to the DNS server. + +#### tls + +TLS configuration, see [TLS](/configuration/shared/tls/#outbound). + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/dns/server/index.md b/docs/configuration/dns/server/index.md new file mode 100644 index 00000000..5393b0bd --- /dev/null +++ b/docs/configuration/dns/server/index.md @@ -0,0 +1,46 @@ +--- +icon: material/alert-decagram +--- + +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [type](#type) + +# DNS Server + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "", + "tag": "" + } + ] + } +} +``` + +#### type + +The type of the DNS server. + +| Type | Format | +|-----------------|-----------------------------------------------------| +| empty (default) | [Legacy](/configuration/dns/server/legacy/) | +| `tcp` | [TCP](/configuration/dns/server/tcp/) | +| `udp` | [UDP](/configuration/dns/server/udp/) | +| `tls` | [TLS](/configuration/dns/server/tls/) | +| `https` | [HTTPS](/configuration/dns/server/https/) | +| `quic` | [QUIC](/configuration/dns/server/quic/) | +| `h3` | [HTTP/3](/configuration/dns/server/http3/) | +| `predefined` | [Predefined](/configuration/dns/server/predefined/) | +| `dhcp` | [DHCP](/configuration/dns/server/dhcp/) | +| `fakeip` | [Fake IP](/configuration/dns/server/fakeip/) | + + +#### tag + +The tag of the DNS server. diff --git a/docs/configuration/dns/server.md b/docs/configuration/dns/server/legacy.md similarity index 93% rename from docs/configuration/dns/server.md rename to docs/configuration/dns/server/legacy.md index 5ec75faa..387d76ec 100644 --- a/docs/configuration/dns/server.md +++ b/docs/configuration/dns/server/legacy.md @@ -1,3 +1,11 @@ +--- +icon: material/delete-clock +--- + +!!! failure "Deprecated in sing-box 1.12.0" + + Legacy DNS servers is deprecated and will be removed in sing-box 1.14.0, check [Migration](/migration/#migrate-to-new-dns-servers). + !!! quote "Changes in sing-box 1.9.0" :material-plus: [client_subnet](#client_subnet) diff --git a/docs/configuration/dns/server.zh.md b/docs/configuration/dns/server/legacy.zh.md similarity index 92% rename from docs/configuration/dns/server.zh.md rename to docs/configuration/dns/server/legacy.zh.md index 9f470541..4bf2bcd3 100644 --- a/docs/configuration/dns/server.zh.md +++ b/docs/configuration/dns/server/legacy.zh.md @@ -1,3 +1,11 @@ +--- +icon: material/delete-clock +--- + +!!! failure "Deprecated in sing-box 1.12.0" + + 旧的 DNS 服务器配置已废弃且将在 sing-box 1.14.0 中被移除,参阅 [迁移指南](/migration/#migrate-to-new-dns-servers)。 + !!! quote "sing-box 1.9.0 中的更改" :material-plus: [client_subnet](#client_subnet) diff --git a/docs/configuration/dns/server/local.md b/docs/configuration/dns/server/local.md new file mode 100644 index 00000000..debcba98 --- /dev/null +++ b/docs/configuration/dns/server/local.md @@ -0,0 +1,33 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# Local + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "local", + "tag": "", + + // Dial Fields + } + ] + } +} +``` + +!!! info "Difference from legacy local server" + + * The old legacy local server only handles IP requests; the new one handles all types of requests and supports concurrent for IP requests. + * The old local server uses default outbound by default unless detour is specified; the new one uses dialer just like outbound, which is equivalent to using an empty direct outbound by default. + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/dns/server/predefined.md b/docs/configuration/dns/server/predefined.md new file mode 100644 index 00000000..ac75d6bb --- /dev/null +++ b/docs/configuration/dns/server/predefined.md @@ -0,0 +1,93 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# Predefined + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "predefined", + "tag": "", + "responses": [] + } + ] + } +} +``` + +### Fields + +#### responses + +==Required== + +List of [Response](#response-structure). + +### Response Structure + +```json +{ + "query": [], + "query_type": [], + "rcode": "", + "answer": [], + "ns": [], + "extra": [] +} +``` + +!!! note "" + + You can ignore the JSON Array [] tag when the content is only one item + +### Response Fields + +#### query + +List of domain name to match. + +#### query_type + +List of query type to match. + +#### rcode + +The response code. + +| Value | Value in the legacy rcode server | Description | +|------------|----------------------------------|-----------------| +| `NOERROR` | `success` | Ok | +| `FORMERR` | `format_error` | Bad request | +| `SERVFAIL` | `server_failure` | Server failure | +| `NXDOMAIN` | `name_error` | Not found | +| `NOTIMP` | `not_implemented` | Not implemented | +| `REFUSED` | `refused` | Refused | + +`NOERROR` will be used by default. + +#### answer + +List of text DNS record to respond as answers. + +Examples: + +| Record Type | Example | +|-------------|-------------------------------| +| `A` | `localhost. IN A 127.0.0.1` | +| `AAAA` | `localhost. IN AAAA ::1` | +| `TXT` | `localhost. IN TXT \"Hello\"` | + +#### ns + +List of text DNS record to respond as name servers. + +#### extra + +List of text DNS record to respond as extra records. diff --git a/docs/configuration/dns/server/quic.md b/docs/configuration/dns/server/quic.md new file mode 100644 index 00000000..096264fe --- /dev/null +++ b/docs/configuration/dns/server/quic.md @@ -0,0 +1,58 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# DNS over QUIC (DoQ) + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "quic", + "tag": "", + + "server": "", + "server_port": 853, + + "tls": {}, + + // Dial Fields + } + ] + } +} +``` + +!!! info "Difference from legacy QUIC server" + + * The old server uses default outbound by default unless detour is specified; the new one uses dialer just like outbound, which is equivalent to using an empty direct outbound by default. + * The old server uses `address_resolver` and `address_strategy` to resolve the domain name in the server; the new one uses `domain_resolver` and `domain_strategy` in [Dial Fields](/configuration/shared/dial/) instead. + +### Fields + +#### server + +==Required== + +The address of the DNS server. + +If domain name is used, `domain_resolver` must also be set to resolve IP address. + +#### server_port + +The port of the DNS server. + +`853` will be used by default. + +#### tls + +TLS configuration, see [TLS](/configuration/shared/tls/#outbound). + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/dns/server/tcp.md b/docs/configuration/dns/server/tcp.md new file mode 100644 index 00000000..c0a4ceaf --- /dev/null +++ b/docs/configuration/dns/server/tcp.md @@ -0,0 +1,52 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# TCP + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "tcp", + "tag": "", + + "server": "", + "server_port": 53, + + // Dial Fields + } + ] + } +} +``` + +!!! info "Difference from legacy TCP server" + + * The old server uses default outbound by default unless detour is specified; the new one uses dialer just like outbound, which is equivalent to using an empty direct outbound by default. + * The old server uses `address_resolver` and `address_strategy` to resolve the domain name in the server; the new one uses `domain_resolver` and `domain_strategy` in [Dial Fields](/configuration/shared/dial/) instead. + +### Fields + +#### server + +==Required== + +The address of the DNS server. + +If domain name is used, `domain_resolver` must also be set to resolve IP address. + +#### server_port + +The port of the DNS server. + +`53` will be used by default. + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/dns/server/tls.md b/docs/configuration/dns/server/tls.md new file mode 100644 index 00000000..0e3be226 --- /dev/null +++ b/docs/configuration/dns/server/tls.md @@ -0,0 +1,58 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# DNS over TLS (DoT) + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "tls", + "tag": "", + + "server": "", + "server_port": 853, + + "tls": {}, + + // Dial Fields + } + ] + } +} +``` + +!!! info "Difference from legacy TLS server" + + * The old server uses default outbound by default unless detour is specified; the new one uses dialer just like outbound, which is equivalent to using an empty direct outbound by default. + * The old server uses `address_resolver` and `address_strategy` to resolve the domain name in the server; the new one uses `domain_resolver` and `domain_strategy` in [Dial Fields](/configuration/shared/dial/) instead. + +### Fields + +#### server + +==Required== + +The address of the DNS server. + +If domain name is used, `domain_resolver` must also be set to resolve IP address. + +#### server_port + +The port of the DNS server. + +`853` will be used by default. + +#### tls + +TLS configuration, see [TLS](/configuration/shared/tls/#outbound). + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/dns/server/udp.md b/docs/configuration/dns/server/udp.md new file mode 100644 index 00000000..4ffbf4d8 --- /dev/null +++ b/docs/configuration/dns/server/udp.md @@ -0,0 +1,52 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# TCP + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "udp", + "tag": "", + + "server": "", + "server_port": 53, + + // Dial Fields + } + ] + } +} +``` + +!!! info "Difference from legacy UDP server" + + * The old server uses default outbound by default unless detour is specified; the new one uses dialer just like outbound, which is equivalent to using an empty direct outbound by default. + * The old server uses `address_resolver` and `address_strategy` to resolve the domain name in the server; the new one uses `domain_resolver` and `domain_strategy` in [Dial Fields](/configuration/shared/dial/) instead. + +### Fields + +#### server + +==Required== + +The address of the DNS server. + +If domain name is used, `domain_resolver` must also be set to resolve IP address. + +#### server_port + +The port of the DNS server. + +`53` will be used by default. + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/deprecated.md b/docs/deprecated.md index 1868d952..40bbc3a4 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -4,6 +4,15 @@ icon: material/delete-alert # Deprecated Feature List +## 1.12.0 + +#### Legacy DNS server formats + +DNS servers are refactored, +check [Migration](../migration/#migrate-to-new-dns-servers). + +Compatibility for old formats will be removed in sing-box 1.14.0. + ## 1.11.0 #### Legacy special outbounds diff --git a/docs/deprecated.zh.md b/docs/deprecated.zh.md index 2cda6386..29f38742 100644 --- a/docs/deprecated.zh.md +++ b/docs/deprecated.zh.md @@ -4,6 +4,13 @@ icon: material/delete-alert # 废弃功能列表 +#### 旧的 DNS 服务器格式 + +DNS 服务器已重构, +参阅 [迁移指南](/migration/#migrate-to-new-dns-servers). + +对旧格式的兼容性将在 sing-box 1.14.0 中被移除。 + ## 1.11.0 #### 旧的特殊出站 diff --git a/docs/migration.md b/docs/migration.md index 4943bb86..a2818e8f 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -2,6 +2,515 @@ icon: material/arrange-bring-forward --- +## 1.12.0 + +### Migrate to new DNS server formats + +DNS servers are refactored. + +!!! info "References" + + [DNS Server](/configuration/dns/server/) / + [Legacy DNS Server](/configuration/dns/server/legacy/) + +=== "Local" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "local" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "local" + } + ] + } + } + ``` + +=== "TCP" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "tcp://1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "tcp", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "UDP" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "udp", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "TLS" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "tls://1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "tls", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "HTTPS" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "https://1.1.1.1/dns-query" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "https", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "QUIC" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "quic://1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "quic", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "HTTP3" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "h3://1.1.1.1/dns-query" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "h3", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "DHCP" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "dhcp://auto" + }, + { + "address": "dhcp://en0" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "dhcp", + }, + { + "type": "dhcp", + "interface": "en0" + } + ] + } + } + ``` + +=== "FakeIP" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "1.1.1.1" + }, + { + "address": "fakeip", + "tag": "fakeip" + } + ], + "rules": [ + { + "query_type": [ + "A", + "AAAA" + ], + "server": "fakeip" + } + ], + "fakeip": { + "enable": true, + "inet4_range": "198.18.0.0/15", + "inet6_range": "fc00::/18" + } + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "udp", + "server": "1.1.1.1" + }, + { + "type": "fakeip", + "tag": "fakeip", + "inet4_range": "198.18.0.0/15", + "inet6_range": "fc00::/18" + } + ], + "rules": [ + { + "query_type": [ + "A", + "AAAA" + ], + "server": "fakeip" + } + ] + } + } + ``` + +=== "RCode" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "rcode://refused" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "predefined", + "responses": [ + { + "rcode": "REFUSED" + } + ] + } + ] + } + } + ``` + +=== "Servers with domain address" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "https://dns.google/dns-query", + "address_resolver": "google" + }, + { + "tag": "google", + "address": "1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "https", + "server": "dns.google", + "domain_resolver": "google" + }, + { + "type": "udp", + "tag": "google", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "Servers with strategy" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "1.1.1.1", + "strategy": "ipv4_only" + }, + { + "tag": "google", + "address": "8.8.8.8", + "strategy": "prefer_ipv6" + } + ], + "rules": [ + { + "domain": "google.com", + "server": "google" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "udp", + "server": "1.1.1.1" + }, + { + "type": "udp", + "tag": "google", + "server": "8.8.8.8" + } + ], + "rules": [ + { + "domain": "google.com", + "server": "google", + "strategy": "prefer_ipv6" + } + ], + "strategy": "ipv4_only" + } + } + ``` + +=== "Servers with client subnet" + + === ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "1.1.1.1" + }, + { + "tag": "google", + "address": "8.8.8.8", + "client_subnet": "1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "udp", + "server": "1.1.1.1" + }, + { + "type": "udp", + "tag": "google", + "server": "8.8.8.8" + } + ], + "rules": [ + { + "domain": "google.com", + "server": "google", + "client_subnet": "1.1.1.1" + } + ] + } + } + ``` + ## 1.11.0 ### Migrate legacy special outbounds to rule actions diff --git a/docs/migration.zh.md b/docs/migration.zh.md index 32be5604..3cc45c2f 100644 --- a/docs/migration.zh.md +++ b/docs/migration.zh.md @@ -2,6 +2,515 @@ icon: material/arrange-bring-forward --- +## 1.12.0 + +### 迁移到新的 DNS 服务器格式 + +DNS 服务器已经重构。 + +!!! info "饮用" + + [DNS 服务器](/configuration/dns/server/) / + [旧 DNS 服务器](/configuration/dns/server/legacy/) + +=== "Local" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "local" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "local" + } + ] + } + } + ``` + +=== "TCP" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "tcp://1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "tcp", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "UDP" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "udp", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "TLS" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "tls://1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "tls", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "HTTPS" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "https://1.1.1.1/dns-query" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "https", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "QUIC" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "quic://1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "quic", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "HTTP3" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "h3://1.1.1.1/dns-query" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "h3", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "DHCP" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "dhcp://auto" + }, + { + "address": "dhcp://en0" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "dhcp", + }, + { + "type": "dhcp", + "interface": "en0" + } + ] + } + } + ``` + +=== "FakeIP" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "1.1.1.1" + }, + { + "address": "fakeip", + "tag": "fakeip" + } + ], + "rules": [ + { + "query_type": [ + "A", + "AAAA" + ], + "server": "fakeip" + } + ], + "fakeip": { + "enable": true, + "inet4_range": "198.18.0.0/15", + "inet6_range": "fc00::/18" + } + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "udp", + "server": "1.1.1.1" + }, + { + "type": "fakeip", + "tag": "fakeip", + "inet4_range": "198.18.0.0/15", + "inet6_range": "fc00::/18" + } + ], + "rules": [ + { + "query_type": [ + "A", + "AAAA" + ], + "server": "fakeip" + } + ] + } + } + ``` + +=== "RCode" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "rcode://refused" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "predefined", + "responses": [ + { + "rcode": "REFUSED" + } + ] + } + ] + } + } + ``` + +=== "带有域名地址的服务器" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "https://dns.google/dns-query", + "address_resolver": "google" + }, + { + "tag": "google", + "address": "1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "https", + "server": "dns.google", + "domain_resolver": "google" + }, + { + "type": "udp", + "tag": "google", + "server": "1.1.1.1" + } + ] + } + } + ``` + +=== "带有域策略的服务器" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "1.1.1.1", + "strategy": "ipv4_only" + }, + { + "tag": "google", + "address": "8.8.8.8", + "strategy": "prefer_ipv6" + } + ], + "rules": [ + { + "domain": "google.com", + "server": "google" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "udp", + "server": "1.1.1.1" + }, + { + "type": "udp", + "tag": "google", + "server": "8.8.8.8" + } + ], + "rules": [ + { + "domain": "google.com", + "server": "google", + "strategy": "prefer_ipv6" + } + ], + "strategy": "ipv4_only" + } + } + ``` + +=== "带有客户端子网的服务器" + + === ":material-card-remove: 弃用的" + + ```json + { + "dns": { + "servers": [ + { + "address": "1.1.1.1" + }, + { + "tag": "google", + "address": "8.8.8.8", + "client_subnet": "1.1.1.1" + } + ] + } + } + ``` + + === ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "udp", + "server": "1.1.1.1" + }, + { + "type": "udp", + "tag": "google", + "server": "8.8.8.8" + } + ], + "rules": [ + { + "domain": "google.com", + "server": "google", + "client_subnet": "1.1.1.1" + } + ] + } + } + ``` + ## 1.11.0 ### 迁移旧的特殊出站到规则动作 @@ -129,7 +638,7 @@ icon: material/arrange-bring-forward } ``` -=== ":material-card-multiple: New" +=== ":material-card-multiple: 新的" ```json { diff --git a/mkdocs.yml b/mkdocs.yml index 4854fa4a..acab5853 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -80,7 +80,19 @@ nav: - configuration/log/index.md - DNS: - configuration/dns/index.md - - DNS Server: configuration/dns/server.md + - DNS Server: + - configuration/dns/server/index.md + - Legacy: configuration/dns/server/legacy.md + - Local: configuration/dns/server/local.md + - TCP: configuration/dns/server/tcp.md + - UDP: configuration/dns/server/udp.md + - TLS: configuration/dns/server/tls.md + - QUIC: configuration/dns/server/quic.md + - HTTPS: configuration/dns/server/https.md + - HTTP3: configuration/dns/server/http3.md + - Predefined: configuration/dns/server/predefined.md + - DHCP: configuration/dns/server/dhcp.md + - FakeIP: configuration/dns/server/fakeip.md - DNS Rule: configuration/dns/rule.md - DNS Rule Action: configuration/dns/rule_action.md - FakeIP: configuration/dns/fakeip.md From 9d4bab4723f0f410305b5501ad66275afd8baaec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 29 Jan 2025 13:13:33 +0800 Subject: [PATCH 22/94] documentation: Outbound domain resolver --- docs/configuration/dns/rule.md | 10 +++- docs/configuration/dns/rule.zh.md | 10 +++- docs/configuration/route/geoip.md | 6 +- docs/configuration/route/geoip.zh.md | 6 +- docs/configuration/route/geosite.md | 6 +- docs/configuration/route/geosite.zh.md | 6 +- docs/configuration/route/index.md | 26 +++++++-- docs/configuration/route/index.zh.md | 16 +++++- docs/configuration/shared/dial.md | 48 +++++++++++++--- docs/configuration/shared/dial.zh.md | 44 ++++++++++++--- docs/deprecated.md | 6 ++ docs/deprecated.zh.md | 8 ++- docs/migration.md | 77 +++++++++++++++++++++++++- docs/migration.zh.md | 67 ++++++++++++++++++++++ 14 files changed, 297 insertions(+), 39 deletions(-) diff --git a/docs/configuration/dns/rule.md b/docs/configuration/dns/rule.md index 1f04b299..3039bffc 100644 --- a/docs/configuration/dns/rule.md +++ b/docs/configuration/dns/rule.md @@ -1,7 +1,11 @@ --- -icon: material/new-box +icon: material/alert-decagram --- +!!! quote "Changes in sing-box 1.12.0" + + :material-delete-clock: [outbound](#outbound) + !!! quote "Changes in sing-box 1.11.0" :material-plus: [action](#action) @@ -395,6 +399,10 @@ Invert match result. #### outbound +!!! failure "Deprecated in sing-box 1.12.0" + + `outbound` rule items are deprecated and will be removed in sing-box 1.14.0, check [Migration](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver). + Match outbound. `any` can be used as a value to match any outbound. diff --git a/docs/configuration/dns/rule.zh.md b/docs/configuration/dns/rule.zh.md index bf0a03e2..94ca4535 100644 --- a/docs/configuration/dns/rule.zh.md +++ b/docs/configuration/dns/rule.zh.md @@ -1,7 +1,11 @@ --- -icon: material/new-box +icon: material/alert-decagram --- +!!! quote "sing-box 1.12.0 中的更改" + + :material-delete-clock: [outbound](#outbound) + !!! quote "sing-box 1.11.0 中的更改" :material-plus: [action](#action) @@ -395,6 +399,10 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`. #### outbound +!!! failure "已在 sing-box 1.12.0 废弃" + + `outbound` 规则项已废弃且将在 sing-box 1.14.0 中被移除,参阅 [迁移指南](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver)。 + 匹配出站。 `any` 可作为值用于匹配任意出站。 diff --git a/docs/configuration/route/geoip.md b/docs/configuration/route/geoip.md index a045574a..62162cdf 100644 --- a/docs/configuration/route/geoip.md +++ b/docs/configuration/route/geoip.md @@ -1,10 +1,10 @@ --- -icon: material/delete-clock +icon: material/note-remove --- -!!! failure "Deprecated in sing-box 1.8.0" +!!! failure "Removed in sing-box 1.12.0" - GeoIP is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets). + GeoIP is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets). ### Structure diff --git a/docs/configuration/route/geoip.zh.md b/docs/configuration/route/geoip.zh.md index eb7bbe2d..3d63a3b7 100644 --- a/docs/configuration/route/geoip.zh.md +++ b/docs/configuration/route/geoip.zh.md @@ -1,10 +1,10 @@ --- -icon: material/delete-clock +icon: material/note-remove --- -!!! failure "已在 sing-box 1.8.0 废弃" +!!! failure "已在 sing-box 1.12.0 中被移除" - GeoIP 已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。 + GeoIP 已在 sing-box 1.8.0 废弃且在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。 ### 结构 diff --git a/docs/configuration/route/geosite.md b/docs/configuration/route/geosite.md index 9a1b9dce..830d1158 100644 --- a/docs/configuration/route/geosite.md +++ b/docs/configuration/route/geosite.md @@ -1,10 +1,10 @@ --- -icon: material/delete-clock +icon: material/note-remove --- -!!! failure "Deprecated in sing-box 1.8.0" +!!! failure "Removed in sing-box 1.12.0" - Geosite is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets). + Geosite is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geosite-to-rule-sets). ### Structure diff --git a/docs/configuration/route/geosite.zh.md b/docs/configuration/route/geosite.zh.md index 7cec5b20..9afea3d7 100644 --- a/docs/configuration/route/geosite.zh.md +++ b/docs/configuration/route/geosite.zh.md @@ -1,10 +1,10 @@ --- -icon: material/delete-clock +icon: material/note-remove --- -!!! failure "已在 sing-box 1.8.0 废弃" +!!! failure "已在 sing-box 1.12.0 中被移除" - Geosite 已废弃且将在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geosite)。 + Geosite 已在 sing-box 1.8.0 废弃且在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geosite)。 ### 结构 diff --git a/docs/configuration/route/index.md b/docs/configuration/route/index.md index 1a1919e9..1fc9bfd2 100644 --- a/docs/configuration/route/index.md +++ b/docs/configuration/route/index.md @@ -1,9 +1,15 @@ --- -icon: material/new-box +icon: material/alert-decagram --- # Route +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [default_domain_resolver](#default_domain_resolver) + :material-note-remove: [geoip](#geoip) + :material-note-remove: [geosite](#geosite) + !!! quote "Changes in sing-box 1.11.0" :material-plus: [default_network_strategy](#default_network_strategy) @@ -22,8 +28,6 @@ icon: material/new-box ```json { "route": { - "geoip": {}, - "geosite": {}, "rules": [], "rule_set": [], "final": "", @@ -31,10 +35,16 @@ icon: material/new-box "override_android_vpn": false, "default_interface": "", "default_mark": 0, + "default_domain_resolver": "", // or {} "default_network_strategy": "", "default_network_type": [], "default_fallback_network_type": [], - "default_fallback_delay": "" + "default_fallback_delay": "", + + // Removed + + "geoip": {}, + "geosite": {} } } ``` @@ -97,6 +107,14 @@ Set routing mark by default. Takes no effect if `outbound.routing_mark` is set. +#### default_domain_resolver + +!!! question "Since sing-box 1.12.0" + +See [Dial Fields](/configuration/shared/dial/#domain_resolver) for details. + +Can be overrides by `outbound.domain_resolver`. + #### default_network_strategy !!! question "Since sing-box 1.11.0" diff --git a/docs/configuration/route/index.zh.md b/docs/configuration/route/index.zh.md index a224ddc4..3748a522 100644 --- a/docs/configuration/route/index.zh.md +++ b/docs/configuration/route/index.zh.md @@ -1,9 +1,15 @@ --- -icon: material/new-box +icon: material/alert-decagram --- # 路由 +!!! quote "sing-box 1.12.0 中的更改" + + :material-plus: [default_domain_resolver](#default_domain_resolver) + :material-note-remove: [geoip](#geoip) + :material-note-remove: [geosite](#geosite) + !!! quote "sing-box 1.11.0 中的更改" :material-plus: [network_strategy](#network_strategy) @@ -100,6 +106,14 @@ icon: material/new-box 如果设置了 `outbound.routing_mark` 设置,则不生效。 +#### default_domain_resolver + +!!! question "自 sing-box 1.12.0 起" + +详情参阅 [拨号字段](/configuration/shared/dial/#domain_resolver)。 + +可以被 `outbound.domain_resolver` 覆盖。 + #### network_strategy !!! question "自 sing-box 1.11.0 起" diff --git a/docs/configuration/shared/dial.md b/docs/configuration/shared/dial.md index 5f654ae2..e852d57b 100644 --- a/docs/configuration/shared/dial.md +++ b/docs/configuration/shared/dial.md @@ -2,6 +2,11 @@ icon: material/new-box --- +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [domain_resolver](#domain_resolver) + :material-delete-clock: [domain_strategy](#domain_strategy) + !!! quote "Changes in sing-box 1.11.0" :material-plus: [network_strategy](#network_strategy) @@ -23,11 +28,14 @@ icon: material/new-box "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, - "domain_strategy": "prefer_ipv6", + "domain_resolver": "", // or {} "network_strategy": "default", "network_type": [], "fallback_network_type": [], - "fallback_delay": "300ms" + "fallback_delay": "300ms", + + // Deprecated + "domain_strategy": "prefer_ipv6" } ``` @@ -92,16 +100,22 @@ decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". -#### domain_strategy +#### domain_resolver -Available values: `prefer_ipv4`, `prefer_ipv6`, `ipv4_only`, `ipv6_only`. +!!! warning "" -If set, the requested domain name will be resolved to IP before connect. + `outbound` DNS rule items are deprecated and will be removed in sing-box 1.14.0, so this item will be required for outbound/endpoints using domain name in server address since sing-box 1.14.0. -| Outbound | Effected domains | Fallback Value | -|----------|--------------------------|-------------------------------------------| -| `direct` | Domain in request | Take `inbound.domain_strategy` if not set | -| others | Domain in server address | / | +Set domain resolver to use for resolving domain names. + +This option uses the same format as the [route DNS rule action](/configuration/dns/rule_action/#route) without the `action` field. + +Setting this option directly to a string is equivalent to setting `server` of this options. + +| Outbound/Endpoints | Effected domains | +|--------------------|--------------------------| +| `direct` | Domain in request | +| others | Domain in server address | #### network_strategy @@ -171,3 +185,19 @@ back to other interfaces. Only take effect when `domain_strategy` or `network_strategy` is set. `300ms` is used by default. + +#### domain_strategy + +!!! failure "Deprecated in sing-box 1.12.0" + + `domain_strategy` is merged to [domain_resolver](#domain_resolver) in sing-box 1.12.0. + +Available values: `prefer_ipv4`, `prefer_ipv6`, `ipv4_only`, `ipv6_only`. + +If set, the requested domain name will be resolved to IP before connect. + +| Outbound | Effected domains | Fallback Value | +|----------|--------------------------|-------------------------------------------| +| `direct` | Domain in request | Take `inbound.domain_strategy` if not set | +| others | Domain in server address | / | + diff --git a/docs/configuration/shared/dial.zh.md b/docs/configuration/shared/dial.zh.md index ab83c44c..66e1436e 100644 --- a/docs/configuration/shared/dial.zh.md +++ b/docs/configuration/shared/dial.zh.md @@ -2,6 +2,11 @@ icon: material/new-box --- +!!! quote "sing-box 1.12.0 中的更改" + + :material-plus: [domain_resolver](#domain_resolver) + :material-delete-clock: [domain_strategy](#domain_strategy) + !!! quote "sing-box 1.11.0 中的更改" :material-plus: [network_strategy](#network_strategy) @@ -23,11 +28,15 @@ icon: material/new-box "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, - "domain_strategy": "prefer_ipv6", + "domain_resolver": "", // 或 {} "network_strategy": "", "network_type": [], "fallback_network_type": [], - "fallback_delay": "300ms" + "fallback_delay": "300ms", + + // 废弃的 + + "domain_strategy": "prefer_ipv6" } ``` @@ -90,16 +99,22 @@ icon: material/new-box 持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。 有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。 -#### domain_strategy +#### domain_resolver -可选值:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。 +!!! warning "" -如果设置,域名将在请求发出之前解析为 IP。 + `outbound` DNS 规则项已弃用,且将在 sing-box 1.14.0 中被移除。因此,从 sing-box 1.14.0 版本开始,所有在服务器地址中使用域名的出站/端点均需配置此项。 -| 出站 | 受影响的域名 | 默认回退值 | -|----------|-----------|---------------------------| -| `direct` | 请求中的域名 | `inbound.domain_strategy` | -| others | 服务器地址中的域名 | / | +用于设置解析域名的域名解析器。 + +此选项的格式与 [路由 DNS 规则动作](/configuration/dns/rule_action/#route) 相同,但不包含 `action` 字段。 + +若直接将此选项设置为字符串,则等同于设置该选项的 `server` 字段。 + +| 出站/端点 | 受影响的域名 | +|----------------|---------------------------| +| `direct` | 请求中的域名 | +| 其他类型 | 服务器地址中的域名 | #### network_strategy @@ -160,3 +175,14 @@ icon: material/new-box 仅当 `domain_strategy` 或 `network_strategy` 已设置时生效。 默认使用 `300ms`。 + +#### domain_strategy + +可选值:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。 + +如果设置,域名将在请求发出之前解析为 IP。 + +| 出站 | 受影响的域名 | 默认回退值 | +|----------|-----------|---------------------------| +| `direct` | 请求中的域名 | `inbound.domain_strategy` | +| others | 服务器地址中的域名 | / | \ No newline at end of file diff --git a/docs/deprecated.md b/docs/deprecated.md index 40bbc3a4..128c0709 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -13,6 +13,12 @@ check [Migration](../migration/#migrate-to-new-dns-servers). Compatibility for old formats will be removed in sing-box 1.14.0. +#### `outbound` DNS rule item + +Legacy `outbound` DNS rules are deprecated +and can be replaced by dial fields, +check [Migration](../migration/#migrate-outbound-dns-rule-items-to-domain-resolver). + ## 1.11.0 #### Legacy special outbounds diff --git a/docs/deprecated.zh.md b/docs/deprecated.zh.md index 29f38742..2c0f5578 100644 --- a/docs/deprecated.zh.md +++ b/docs/deprecated.zh.md @@ -11,6 +11,12 @@ DNS 服务器已重构, 对旧格式的兼容性将在 sing-box 1.14.0 中被移除。 +#### `outbound` DNS 规则项 + +旧的 `outbound` DNS 规则已废弃, +且可被拨号字段代替, +参阅 [迁移指南](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver). + ## 1.11.0 #### 旧的特殊出站 @@ -27,7 +33,7 @@ DNS 服务器已重构, 旧字段将在 sing-box 1.13.0 中被移除。 -#### direct 出站中的目标地址覆盖字段 +#### direct 出站中的目标地址覆盖字段 direct 出站中的目标地址覆盖字段(`override_address` / `override_port`)已废弃且可以通过规则动作替代, 参阅 [迁移指南](/migration/#migrate-destination-override-fields-to-route-options)。 diff --git a/docs/migration.md b/docs/migration.md index a2818e8f..62e09e71 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -6,7 +6,7 @@ icon: material/arrange-bring-forward ### Migrate to new DNS server formats -DNS servers are refactored. +DNS servers are refactored for better performance and scalability. !!! info "References" @@ -511,6 +511,81 @@ DNS servers are refactored. } ``` +### Migrate outbound DNS rule items to domain resolver + +The legacy outbound DNS rules are deprecated and can be replaced by new domain resolver options. + +!!! info "References" + + [DNS rule](/configuration/dns/rule/#outbound) / + [Dial Fields](/configuration/shared/dial/#domain_resolver) / + [Route](/configuration/route/#domain_resolver) + +=== ":material-card-remove: Deprecated" + + ```json + { + "dns": { + "servers": [ + { + "address": "local", + "tag": "local" + } + ], + "rules": [ + { + "outbound": "any", + "server": "local" + } + ] + }, + "outbounds": [ + { + "type": "socks", + "server": "example.org", + "server_port": 2080 + } + ] + } + ``` + +=== ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "local" + } + ] + }, + "outbounds": [ + { + "type": "socks", + "server": "example.org", + "server_port": 2080, + "domain_resolver": { + "server": "local", + "rewrite_tll": 60, + "client_subnet": "1.1.1.1" + }, + // or "domain_resolver": "local", + } + ], + + // or + + "route": { + "default_domain_resolver": { + "server": "local", + "rewrite_tll": 60, + "client_subnet": "1.1.1.1" + } + } + } + ``` + ## 1.11.0 ### Migrate legacy special outbounds to rule actions diff --git a/docs/migration.zh.md b/docs/migration.zh.md index 3cc45c2f..12044669 100644 --- a/docs/migration.zh.md +++ b/docs/migration.zh.md @@ -511,6 +511,73 @@ DNS 服务器已经重构。 } ``` +### 迁移 outbound DNS 规则项到域解析选项 + +旧的 `outbound` DNS 规则已废弃,且可新的域解析选项代替。 + +!!! info "参考" + + [DNS 规则](/configuration/dns/rule/#outbound) / + [拨号字段](/configuration/shared/dial/#domain_resolver) / + [路由](/configuration/route/#default_domain_resolver) + +=== ":material-card-remove: 废弃的" + + ```json + { + "dns": { + "servers": [ + { + "address": "local", + "tag": "local" + } + ], + "rules": [ + { + "outbound": "any", + "server": "local" + } + ] + }, + "outbounds": [ + { + "type": "socks", + "server": "example.org", + "server_port": 2080 + } + ] + } + ``` + +=== ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "local" + } + ] + }, + "outbounds": [ + { + "type": "socks", + "server": "example.org", + "server_port": 2080, + "domain_resolver": "local", + } + ], + "route": { + "default_domain_resolver": { + "server": "local", + "rewrite_tll": 60, + "client_subnet": "1.1.1.1" + } + } + } + ``` + ## 1.11.0 ### 迁移旧的特殊出站到规则动作 From ceb7a25cf796356775f86a8f67b856d21f8cf7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 30 Jan 2025 13:29:16 +0800 Subject: [PATCH 23/94] documentation: TLS fragment --- docs/configuration/route/rule_action.md | 31 +++++++++++++++++++++- docs/configuration/route/rule_action.zh.md | 27 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/configuration/route/rule_action.md b/docs/configuration/route/rule_action.md index 31256e25..567b9eb6 100644 --- a/docs/configuration/route/rule_action.md +++ b/docs/configuration/route/rule_action.md @@ -2,6 +2,11 @@ icon: material/new-box --- +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [tls_fragment](#tls_fragment) + :material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay) + ## Final actions ### route @@ -81,7 +86,9 @@ Not available when `method` is set to drop. "fallback_delay": "", "udp_disable_domain_unmapping": false, "udp_connect": false, - "udp_timeout": "" + "udp_timeout": "", + "tls_fragment": false, + "tls_fragment_fallback_delay": "" } ``` @@ -148,6 +155,28 @@ If no protocol is sniffed, the following ports will be recognized as protocols b | 443 | `quic` | | 3478 | `stun` | +#### tls_fragment + +!!! question "Since sing-box 1.12.0" + +Fragment TLS handshakes to bypass firewalls. + +This feature is intended to circumvent simple firewalls based on **plaintext packet matching**, and should not be used to circumvent real censorship. + +Since it is not designed for performance, it should not be applied to all connections, but only to server names that are known to be blocked. + +On Linux, Apple platforms, (administrator privileges required) Windows, the wait time can be automatically detected, otherwise it will fall back to waiting for a fixed time specified by `tls_fragment_fallback_delay`. + +In addition, if the actual wait time is less than 20ms, it will also fall back to waiting for a fixed time, because the target is considered to be local or behind a transparent proxy. + +#### tls_fragment_fallback_delay + +!!! question "Since sing-box 1.12.0" + +The fallback value used when TLS segmentation cannot automatically determine the wait time. + +`500ms` is used by default. + ### sniff ```json diff --git a/docs/configuration/route/rule_action.zh.md b/docs/configuration/route/rule_action.zh.md index 544918d4..a8eca8a2 100644 --- a/docs/configuration/route/rule_action.zh.md +++ b/docs/configuration/route/rule_action.zh.md @@ -2,6 +2,11 @@ icon: material/new-box --- +!!! quote "sing-box 1.12.0 中的更改" + + :material-plus: [tls_fragment](#tls_fragment) + :material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay) + ## 最终动作 ### route @@ -146,6 +151,28 @@ UDP 连接超时时间。 | 443 | `quic` | | 3478 | `stun` | +#### tls_fragment + +!!! question "自 sing-box 1.12.0 起" + +通过分段 TLS 握手数据包来绕过防火墙检测。 + +此功能旨在规避基于**明文数据包匹配**的简单防火墙,不应该用于规避真的审查。 + +由于它不是为性能设计的,不应被应用于所有连接,而仅应用于已知被阻止的服务器名称。 + +在 Linux、Apple 平台和需要管理员权限的 Windows 系统上,可自动检测等待时间。若无法自动检测,将回退使用 `tls_fragment_fallback_delay` 指定的固定等待时间。 + +此外,若实际等待时间小于 20 毫秒,同样会回退至固定等待时间模式,因为此时判定目标处于本地或透明代理之后。 + +#### tls_fragment_fallback_delay + +!!! question "自 sing-box 1.12.0 起" + +当 TLS 分片功能无法自动判定等待时间时使用的回退值。 + +默认使用 `500ms`。 + ### sniff ```json From 9c5391611549d02165b41809dd08591ca41c0211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 30 Jan 2025 14:27:25 +0800 Subject: [PATCH 24/94] documentation: Certificate store --- docs/configuration/certificate/index.md | 54 +++++++++++++++++++++++++ mkdocs.yml | 4 +- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 docs/configuration/certificate/index.md diff --git a/docs/configuration/certificate/index.md b/docs/configuration/certificate/index.md new file mode 100644 index 00000000..698fec70 --- /dev/null +++ b/docs/configuration/certificate/index.md @@ -0,0 +1,54 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# Certificate + +### Structure + +```json +{ + "store": "", + "certificate": [], + "certificate_path": [], + "certificate_directory_path": [] +} +``` + +!!! note "" + + You can ignore the JSON Array [] tag when the content is only one item + +### Fields + +#### store + +The default X509 trusted CA certificate list. + +| Type | Description | +|--------------------|---------------------------------------------------------------------------------------------------------------| +| `system` (default) | System trusted CA certificates | +| `mozilla` | [Mozilla Included List](https://wiki.mozilla.org/CA/Included_Certificates) with China CA certificates removed | +| `none` | Empty list | + +#### certificate + +The certificate line array to trust, in PEM format. + +#### certificate_path + +!!! note "" + + Will be automatically reloaded if file modified. + +The paths to certificates to trust, in PEM format. + +#### certificate_directory_path + +!!! note "" + + Will be automatically reloaded if file modified. + +The directory path to search for certificates to trust,in PEM format. diff --git a/mkdocs.yml b/mkdocs.yml index acab5853..5758525a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -96,8 +96,8 @@ nav: - DNS Rule: configuration/dns/rule.md - DNS Rule Action: configuration/dns/rule_action.md - FakeIP: configuration/dns/fakeip.md - - NTP: - - configuration/ntp/index.md + - NTP: configuration/ntp/index.md + - Certificate: configuration/certificate/index.md - Route: - configuration/route/index.md - GeoIP: configuration/route/geoip.md From ad4f82243aa1daae9884bea85a548d33e9f7cdfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 30 Jan 2025 14:30:37 +0800 Subject: [PATCH 25/94] documentation: Remove outdated icons --- docs/configuration/experimental/clash-api.md | 4 ---- docs/configuration/experimental/clash-api.zh.md | 4 ---- docs/configuration/route/sniff.md | 4 ---- docs/configuration/route/sniff.zh.md | 4 ---- docs/configuration/rule-set/adguard.md | 4 ---- docs/configuration/rule-set/adguard.zh.md | 4 ---- docs/configuration/rule-set/index.md | 4 ---- docs/configuration/rule-set/index.zh.md | 4 ---- docs/configuration/shared/tls.md | 4 ---- docs/configuration/shared/tls.zh.md | 4 ---- 10 files changed, 40 deletions(-) diff --git a/docs/configuration/experimental/clash-api.md b/docs/configuration/experimental/clash-api.md index 7425143e..a02e87a9 100644 --- a/docs/configuration/experimental/clash-api.md +++ b/docs/configuration/experimental/clash-api.md @@ -1,7 +1,3 @@ ---- -icon: material/new-box ---- - !!! quote "Changes in sing-box 1.10.0" :material-plus: [access_control_allow_origin](#access_control_allow_origin) diff --git a/docs/configuration/experimental/clash-api.zh.md b/docs/configuration/experimental/clash-api.zh.md index b3d8aeaf..c6146fb8 100644 --- a/docs/configuration/experimental/clash-api.zh.md +++ b/docs/configuration/experimental/clash-api.zh.md @@ -1,7 +1,3 @@ ---- -icon: material/new-box ---- - !!! quote "sing-box 1.10.0 中的更改" :material-plus: [access_control_allow_origin](#access_control_allow_origin) diff --git a/docs/configuration/route/sniff.md b/docs/configuration/route/sniff.md index 3880f790..24bc8ef6 100644 --- a/docs/configuration/route/sniff.md +++ b/docs/configuration/route/sniff.md @@ -1,7 +1,3 @@ ---- -icon: material/new-box ---- - !!! quote "Changes in sing-box 1.10.0" :material-plus: QUIC client type detect support for QUIC diff --git a/docs/configuration/route/sniff.zh.md b/docs/configuration/route/sniff.zh.md index 4efa4538..cd582517 100644 --- a/docs/configuration/route/sniff.zh.md +++ b/docs/configuration/route/sniff.zh.md @@ -1,7 +1,3 @@ ---- -icon: material/new-box ---- - !!! quote "sing-box 1.10.0 中的更改" :material-plus: QUIC 的 客户端类型探测支持 diff --git a/docs/configuration/rule-set/adguard.md b/docs/configuration/rule-set/adguard.md index bda73794..c8bd32fa 100644 --- a/docs/configuration/rule-set/adguard.md +++ b/docs/configuration/rule-set/adguard.md @@ -1,7 +1,3 @@ ---- -icon: material/new-box ---- - !!! question "Since sing-box 1.10.0" sing-box supports some rule-set formats from other projects which cannot be fully translated to sing-box, diff --git a/docs/configuration/rule-set/adguard.zh.md b/docs/configuration/rule-set/adguard.zh.md index 026f2e0b..82773280 100644 --- a/docs/configuration/rule-set/adguard.zh.md +++ b/docs/configuration/rule-set/adguard.zh.md @@ -1,7 +1,3 @@ ---- -icon: material/new-box ---- - !!! question "自 sing-box 1.10.0 起" sing-box 支持其他项目的一些规则集格式,这些格式无法完全转换为 sing-box, diff --git a/docs/configuration/rule-set/index.md b/docs/configuration/rule-set/index.md index c905e110..944ea838 100644 --- a/docs/configuration/rule-set/index.md +++ b/docs/configuration/rule-set/index.md @@ -1,7 +1,3 @@ ---- -icon: material/new-box ---- - !!! quote "Changes in sing-box 1.10.0" :material-plus: `type: inline` diff --git a/docs/configuration/rule-set/index.zh.md b/docs/configuration/rule-set/index.zh.md index c3b86b6d..c38a57b5 100644 --- a/docs/configuration/rule-set/index.zh.md +++ b/docs/configuration/rule-set/index.zh.md @@ -1,7 +1,3 @@ ---- -icon: material/new-box ---- - !!! quote "sing-box 1.10.0 中的更改" :material-plus: `type: inline` diff --git a/docs/configuration/shared/tls.md b/docs/configuration/shared/tls.md index 7a6c9d5e..f2a716a7 100644 --- a/docs/configuration/shared/tls.md +++ b/docs/configuration/shared/tls.md @@ -1,7 +1,3 @@ ---- -icon: material/alert-decagram ---- - !!! quote "Changes in sing-box 1.10.0" :material-alert-decagram: [utls](#utls) diff --git a/docs/configuration/shared/tls.zh.md b/docs/configuration/shared/tls.zh.md index b254e083..6cfc5f36 100644 --- a/docs/configuration/shared/tls.zh.md +++ b/docs/configuration/shared/tls.zh.md @@ -1,7 +1,3 @@ ---- -icon: material/alert-decagram ---- - !!! quote "sing-box 1.10.0 中的更改" :material-alert-decagram: [utls](#utls) From 06b6369f897ff1c266d28ca860495fce7a451bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 12 Feb 2025 08:31:21 +0800 Subject: [PATCH 26/94] Build legacy binaries with latest Go --- .github/setup_legacy_go.sh | 25 +++++++++++++++++++++++++ .github/workflows/build.yml | 28 ++++++++++++++++++---------- .goreleaser.yaml | 5 ++--- 3 files changed, 45 insertions(+), 13 deletions(-) create mode 100755 .github/setup_legacy_go.sh diff --git a/.github/setup_legacy_go.sh b/.github/setup_legacy_go.sh new file mode 100755 index 00000000..3f1a31ad --- /dev/null +++ b/.github/setup_legacy_go.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +VERSION="1.23.6" + +mkdir -p $HOME/go +cd $HOME/go +wget "https://dl.google.com/go/go${VERSION}.linux-amd64.tar.gz" +tar -xzf "go${VERSION}.linux-amd64.tar.gz" +mv go go_legacy +cd go_legacy + +# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 +# this patch file only works on golang1.23.x +# that means after golang1.24 release it must be changed +# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/ +# revert: +# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" +# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7" +# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround" +# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries" + +curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1 +curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1 +curl https://github.com/MetaCubeX/go/commit/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.diff | patch --verbose -p 1 +curl https://github.com/MetaCubeX/go/commit/69e2eed6dd0f6d815ebf15797761c13f31213dd6.diff | patch --verbose -p 1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 025fe845..f6b2af9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -94,7 +94,6 @@ jobs: - { os: windows, arch: arm64 } - { os: darwin, arch: amd64 } - - { os: darwin, arch: amd64, legacy_go: true } - { os: darwin, arch: arm64 } - { os: android, arch: arm64, ndk: "aarch64-linux-android21" } @@ -106,16 +105,28 @@ jobs: uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 with: fetch-depth: 0 - - name: Setup Go - if: matrix.legacy_go - uses: actions/setup-go@v5 - with: - go-version: ~1.20 - name: Setup Go if: ${{ ! matrix.legacy_go }} uses: actions/setup-go@v5 with: go-version: ^1.24 + - name: Cache Legacy Go + if: matrix.require_legacy_go + id: cache-legacy-go + uses: actions/cache@v4 + with: + path: | + ~/go/go_legacy + key: go_legacy_1236 + - name: Setup Legacy Go + if: matrix.legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true' + run: |- + .github/setup_legacy_go.sh + - name: Setup Legacy Go 2 + if: matrix.legacy_go + run: |- + echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV + echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV - name: Setup Android NDK if: matrix.os == 'android' uses: nttld/setup-ndk@v1 @@ -129,10 +140,7 @@ jobs: - name: Set build tags run: | set -xeuo pipefail - TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api' - if [ ! '${{ matrix.legacy_go }}' = 'true' ]; then - TAGS="${TAGS},with_ech" - fi + TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api,with_tailscale' echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}" - name: Build if: matrix.os != 'android' diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 87a2f458..069e5eac 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -51,12 +51,11 @@ builds: - with_clash_api env: - CGO_ENABLED=0 - - GOROOT={{ .Env.GOPATH }}/go1.20.14 - tool: "{{ .Env.GOPATH }}/go1.20.14/bin/go" + - GOROOT={{ .Env.GOPATH }}/go_legacy + tool: "{{ .Env.GOPATH }}/go_legacy/bin/go" targets: - windows_amd64_v1 - windows_386 - - darwin_amd64_v1 - id: android <<: *template env: From eccace2329ba790316d177ebc2ee275d7a6c01ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 6 Apr 2025 19:14:41 +0800 Subject: [PATCH 27/94] Add Tailscale endpoint --- .goreleaser.fury.yaml | 1 + .goreleaser.yaml | 2 + Makefile | 3 +- cmd/internal/build_libbox/main.go | 2 +- common/dialer/dialer.go | 3 + constant/dns.go | 1 + constant/proxy.go | 1 + dns/router.go | 3 - docs/configuration/dns/server/tailscale.md | 83 ++++ docs/configuration/endpoint/tailscale.md | 99 +++++ docs/installation/build-from-source.md | 1 + docs/installation/build-from-source.zh.md | 1 + go.mod | 70 ++- go.sum | 161 +++++-- include/registry.go | 2 + include/tailscale.go | 17 + include/tailscale_stub.go | 27 ++ mkdocs.yml | 2 + option/tailscale.go | 24 ++ protocol/tailscale/dns_transport.go | 320 ++++++++++++++ protocol/tailscale/endpoint.go | 473 +++++++++++++++++++++ protocol/tailscale/protect_android.go | 16 + protocol/tailscale/protect_nonandroid.go | 8 + 23 files changed, 1269 insertions(+), 51 deletions(-) create mode 100644 docs/configuration/dns/server/tailscale.md create mode 100644 docs/configuration/endpoint/tailscale.md create mode 100644 include/tailscale.go create mode 100644 include/tailscale_stub.go create mode 100644 option/tailscale.go create mode 100644 protocol/tailscale/dns_transport.go create mode 100644 protocol/tailscale/endpoint.go create mode 100644 protocol/tailscale/protect_android.go create mode 100644 protocol/tailscale/protect_nonandroid.go diff --git a/.goreleaser.fury.yaml b/.goreleaser.fury.yaml index 4761a76a..8f3835f4 100644 --- a/.goreleaser.fury.yaml +++ b/.goreleaser.fury.yaml @@ -19,6 +19,7 @@ builds: - with_reality_server - with_acme - with_clash_api + - with_tailscale env: - CGO_ENABLED=0 targets: diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 069e5eac..858f4ca0 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -21,6 +21,7 @@ builds: - with_reality_server - with_acme - with_clash_api + - with_tailscale env: - CGO_ENABLED=0 targets: @@ -49,6 +50,7 @@ builds: - with_reality_server - with_acme - with_clash_api + - with_tailscale env: - CGO_ENABLED=0 - GOROOT={{ .Env.GOPATH }}/go_legacy diff --git a/Makefile b/Makefile index 8ae443d4..b4a262b6 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ NAME = sing-box COMMIT = $(shell git rev-parse --short HEAD) TAGS_GO120 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api,with_quic,with_utls TAGS_GO121 = with_ech -TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121) +TAGS_GO123 = with_tailscale +TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121),$(TAGS_GO123) TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server GOHOSTOS = $(shell go env GOHOSTOS) diff --git a/cmd/internal/build_libbox/main.go b/cmd/internal/build_libbox/main.go index e37a9ff4..56cf8688 100644 --- a/cmd/internal/build_libbox/main.go +++ b/cmd/internal/build_libbox/main.go @@ -58,7 +58,7 @@ func init() { sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=") debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag) - sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api") + sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api", "with_tailscale") iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack") debugTags = append(debugTags, "debug") } diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index 812e5575..0efc7e9d 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -22,6 +22,7 @@ type Options struct { RemoteIsDomain bool DirectResolver bool ResolverOnDetour bool + NewDialer bool } // TODO: merge with NewWithOptions @@ -100,6 +101,8 @@ func NewWithOptions(options Options) (N.Dialer, error) { } dnsQueryOptions.Transport = transport resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay) + } else if options.NewDialer { + return nil, E.New("missing domain resolver for domain server address") } else { deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) } diff --git a/constant/dns.go b/constant/dns.go index 99461a27..0a444ff0 100644 --- a/constant/dns.go +++ b/constant/dns.go @@ -27,6 +27,7 @@ const ( DNSTypePreDefined = "predefined" DNSTypeFakeIP = "fakeip" DNSTypeDHCP = "dhcp" + DNSTypeTailscale = "tailscale" ) const ( diff --git a/constant/proxy.go b/constant/proxy.go index 3197de60..45e79f84 100644 --- a/constant/proxy.go +++ b/constant/proxy.go @@ -23,6 +23,7 @@ const ( TypeVLESS = "vless" TypeTUIC = "tuic" TypeHysteria2 = "hysteria2" + TypeTailscale = "tailscale" ) const ( diff --git a/dns/router.go b/dns/router.go index 8ecb8891..4102128e 100644 --- a/dns/router.go +++ b/dns/router.go @@ -174,7 +174,6 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, options.ClientSubnet = legacyTransport.LegacyClientSubnet() } } - r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action()) return transport, currentRule, currentRuleIndex case *R.RuleActionDNSRouteOptions: if action.Strategy != C.DomainStrategyAsIS { @@ -189,9 +188,7 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, if action.ClientSubnet.IsValid() { options.ClientSubnet = action.ClientSubnet } - r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action()) case *R.RuleActionReject: - r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action()) return nil, currentRule, currentRuleIndex } } diff --git a/docs/configuration/dns/server/tailscale.md b/docs/configuration/dns/server/tailscale.md new file mode 100644 index 00000000..71744858 --- /dev/null +++ b/docs/configuration/dns/server/tailscale.md @@ -0,0 +1,83 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# Tailscale + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "tailscale", + "tag": "", + + "endpoint": "ts-ep", + "accept_default_resolvers": false + } + ] + } +} +``` + +### Fields + +#### endpoint + +==Required== + +The tag of the Tailscale endpoint. + +#### accept_default_resolvers + +Indicates whether default DNS resolvers should be accepted for fallback queries in addition to MagicDNS。 + +if not enabled, NXDOMAIN will be returned for non-Tailscale domain queries. + +### Examples + +=== "MagicDNS only" + + ```json + { + "dns": { + "servers": [ + { + "type": "local", + "tag": "local" + }, + { + "type": "tailscale", + "tag": "ts", + "endpoint": "ts-ep" + } + ], + "rules": [ + { + "ip_accept_any": true, + "server": "ts" + } + ] + } + } + ``` + +=== "Use as global DNS" + + ```json + { + "dns": { + "servers": [ + { + "type": "tailscale", + "endpoint": "ts-ep", + "accept_default_resolvers": true + } + ] + } + } + ``` \ No newline at end of file diff --git a/docs/configuration/endpoint/tailscale.md b/docs/configuration/endpoint/tailscale.md new file mode 100644 index 00000000..298b68ce --- /dev/null +++ b/docs/configuration/endpoint/tailscale.md @@ -0,0 +1,99 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +### Structure + +```json +{ + "type": "tailscale", + "tag": "ts-ep", + "state_directory": "", + "auth_key": "", + "control_url": "", + "ephemeral": false, + "hostname": "", + "exit_node": "", + "exit_node_allow_lan_access": false, + "advertise_routes": [], + "advertise_exit_node": false, + "udp_timeout": "5m", + + ... // Dial Fields +} +``` + +### Fields + +#### state_directory + +The directory where the Tailscale state is stored. + +`tailscale` is used by default. + +Example: `$HOME/.tailscale` + +#### auth_key + +!!! note + + Auth key is not required. By default, sing-box will log the login URL (or popup a notification on graphical clients). + +The auth key to create the node. If the node is already created (from state previously stored), then this field is not +used. + +#### control_url + +The coordination server URL. + +`https://controlplane.tailscale.com` is used by default. + +#### ephemeral + +Indicates whether the instance should register as an Ephemeral node (https://tailscale.com/s/ephemeral-nodes). + +#### hostname + +The hostname of the node. + +System hostname is used by default. + +Example: `localhost` + +#### exit_node + +The exit node name or IP address to use. + +#### exit_node_allow_lan_access + +!!! note + + When the exit node does not have a corresponding advertised route, private traffics cannot be routed to the exit node even if `exit_node_allow_lan_access is` set. + +Indicates whether locally accessible subnets should be routed directly or via the exit node. + +#### advertise_routes + +CIDR prefixes to advertise into the Tailscale network as reachable through the current node. + +Example: `["192.168.1.1/24"]` + +#### advertise_exit_node + +Indicates whether the node should advertise itself as an exit node. + +#### udp_timeout + +UDP NAT expiration time. + +`5m` will be used by default. + +### Dial Fields + +!!! note + + Dial Fields in Tailscale endpoints only control how it connects to the control plane and have nothing to do with actual connections. + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/installation/build-from-source.md b/docs/installation/build-from-source.md index e7c92ef4..8c4d4923 100644 --- a/docs/installation/build-from-source.md +++ b/docs/installation/build-from-source.md @@ -60,5 +60,6 @@ go build -tags "tag_a tag_b" ./cmd/sing-box | `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | | `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | | `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). | +| `with_tailscale` | :material-check: | Build with Tailscale support, see [Tailscale endpoint](/configuration/endpoint/tailscale) | It is not recommended to change the default build tag list unless you really know what you are adding. diff --git a/docs/installation/build-from-source.zh.md b/docs/installation/build-from-source.zh.md index f0bcc49b..7de7fcf4 100644 --- a/docs/installation/build-from-source.zh.md +++ b/docs/installation/build-from-source.zh.md @@ -60,5 +60,6 @@ go build -tags "tag_a tag_b" ./cmd/sing-box | `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | | `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | | `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). | +| `with_tailscale` | :material-check: | Build with Tailscale support, see [Tailscale endpoint](/configuration/endpoint/tailscale) | 除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。 diff --git a/go.mod b/go.mod index ac86825a..0d38a4a1 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/sagernet/sing-box -go 1.20 +go 1.23.1 + +toolchain go1.24.0 require ( github.com/caddyserver/certmagic v0.20.0 @@ -35,6 +37,7 @@ require ( github.com/sagernet/sing-tun v0.6.4 github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 + github.com/sagernet/tailscale v1.79.0-mod.1 github.com/sagernet/utls v1.6.7 github.com/sagernet/wireguard-go v0.0.1-beta.7 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 @@ -56,48 +59,87 @@ require ( //replace github.com/sagernet/sing => ../sing require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/ajg/form v1.5.1 // indirect - github.com/andybalholm/brotli v1.0.6 // indirect + github.com/akutz/memconn v0.1.0 // indirect + github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/coder/websocket v1.8.12 // indirect + github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect + github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.6.0 // indirect + github.com/gaissmai/bart v0.11.1 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect + github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect + github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/csrf v1.7.2 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect github.com/hashicorp/yamux v0.1.2 // indirect + github.com/hdevalence/ed25519consensus v0.2.0 // indirect + github.com/illarion/gonotify/v2 v2.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect + github.com/jsimonetti/rtnetlink v1.4.0 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect github.com/libdns/libdns v0.2.2 // indirect + github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect - github.com/mdlayher/socket v0.4.1 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/onsi/ginkgo/v2 v2.9.7 // indirect - github.com/pierrec/lz4/v4 v4.1.14 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/mdlayher/sdnotify v1.0.0 // indirect + github.com/mdlayher/socket v0.5.0 // indirect + github.com/mitchellh/go-ps v1.0.0 // indirect + github.com/onsi/ginkgo/v2 v2.17.2 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus-community/pro-bing v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + github.com/safchain/ethtool v0.3.0 // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect + github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect + github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect + github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 // indirect + github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect + github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect + github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect + github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect + github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect + github.com/tcnksm/go-httpstat v0.2.0 // indirect + github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect github.com/vishvananda/netns v0.0.4 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.uber.org/multierr v1.11.0 // indirect + go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect golang.org/x/sync v0.10.0 // indirect + golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect + golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect ) + +//replace github.com/sagernet/sing => ../sing diff --git a/go.sum b/go.sum index 4f2a8eeb..9ba48be3 100644 --- a/go.sum +++ b/go.sum @@ -1,39 +1,69 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= -github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= +github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= +github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= +github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0= +github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk= +github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ= github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU= github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= +github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q= +github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= +github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc= +github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= +github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= +github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU= github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0= github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -41,26 +71,41 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI= +github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= +github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= +github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/illarion/gonotify/v2 v2.0.3 h1:B6+SKPo/0Sw8cRJh1aLzNEeNVFfzE3c6N+o+vyxM+9A= +github.com/illarion/gonotify/v2 v2.0.3/go.mod h1:38oIJTgFqupkEydkkClkbL6i5lXV/bxdH9do5TALPEE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4= github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk= +github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8= +github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= +github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= +github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054= @@ -70,32 +115,45 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= +github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= +github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= +github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= +github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI= github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY= github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= +github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= -github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4= +github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0= +github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs= github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO+NOcZ81gv4uzOfALUIdhkiIFvmG4= github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= @@ -137,6 +195,8 @@ github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2 github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= +github.com/sagernet/tailscale v1.79.0-mod.1 h1:wIuAH7VqBYJNk0h2+bTyk4F0OlSqHvyLDCBrD3i+XNI= +github.com/sagernet/tailscale v1.79.0-mod.1/go.mod h1:RKY5WjYLj3JJ7VO/8ZCw8eAFa4+kWU6A1Ftdk84uB14= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI= @@ -148,14 +208,36 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= -github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ= +github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4= +github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= +github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= +github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 h1:rXZGgEa+k2vJM8xT0PoSKfVXwFGPQ3z3CJfmnHJkZZw= +github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ= +github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio= +github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= +github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw= +github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= +github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= +github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= +github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w= +github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU= +github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g= +github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= +github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= +github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= +github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= +github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= +github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw= +github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= @@ -163,10 +245,13 @@ github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvv github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= +go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= @@ -174,18 +259,25 @@ golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -193,6 +285,7 @@ golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= @@ -202,21 +295,23 @@ golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= +golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= +golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= @@ -225,3 +320,5 @@ howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= +software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/include/registry.go b/include/registry.go index cbf793f4..866c506a 100644 --- a/include/registry.go +++ b/include/registry.go @@ -92,6 +92,7 @@ func EndpointRegistry() *endpoint.Registry { registry := endpoint.NewRegistry() registerWireGuardEndpoint(registry) + registerTailscaleEndpoint(registry) return registry } @@ -110,6 +111,7 @@ func DNSTransportRegistry() *dns.TransportRegistry { registerQUICTransports(registry) registerDHCPTransport(registry) + registerTailscaleTransport(registry) return registry } diff --git a/include/tailscale.go b/include/tailscale.go new file mode 100644 index 00000000..05eed2cd --- /dev/null +++ b/include/tailscale.go @@ -0,0 +1,17 @@ +//go:build with_tailscale + +package include + +import ( + "github.com/sagernet/sing-box/adapter/endpoint" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/protocol/tailscale" +) + +func registerTailscaleEndpoint(registry *endpoint.Registry) { + tailscale.RegisterEndpoint(registry) +} + +func registerTailscaleTransport(registry *dns.TransportRegistry) { + tailscale.RegistryTransport(registry) +} diff --git a/include/tailscale_stub.go b/include/tailscale_stub.go new file mode 100644 index 00000000..ddf6485e --- /dev/null +++ b/include/tailscale_stub.go @@ -0,0 +1,27 @@ +//go:build !with_tailscale + +package include + +import ( + "context" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/endpoint" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + E "github.com/sagernet/sing/common/exceptions" +) + +func registerTailscaleEndpoint(registry *endpoint.Registry) { + endpoint.Register[option.TailscaleEndpointOptions](registry, C.TypeTailscale, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) { + return nil, E.New(`Tailscale is not included in this build, rebuild with -tags with_tailscale`) + }) +} + +func registerTailscaleTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.TailscaleDNSServerOptions](registry, C.DNSTypeTailscale, func(ctx context.Context, logger log.ContextLogger, tag string, options option.TailscaleDNSServerOptions) (adapter.DNSTransport, error) { + return nil, E.New(`Tailscale is not included in this build, rebuild with -tags with_tailscale`) + }) +} diff --git a/mkdocs.yml b/mkdocs.yml index 5758525a..e1a74fb0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -93,6 +93,7 @@ nav: - Predefined: configuration/dns/server/predefined.md - DHCP: configuration/dns/server/dhcp.md - FakeIP: configuration/dns/server/fakeip.md + - Tailscale: configuration/dns/server/tailscale.md - DNS Rule: configuration/dns/rule.md - DNS Rule Action: configuration/dns/rule_action.md - FakeIP: configuration/dns/fakeip.md @@ -127,6 +128,7 @@ nav: - Endpoint: - configuration/endpoint/index.md - WireGuard: configuration/endpoint/wireguard.md + - Tailscale: configuration/endpoint/tailscale.md - Inbound: - configuration/inbound/index.md - Direct: configuration/inbound/direct.md diff --git a/option/tailscale.go b/option/tailscale.go new file mode 100644 index 00000000..30579fc7 --- /dev/null +++ b/option/tailscale.go @@ -0,0 +1,24 @@ +package option + +import ( + "net/netip" +) + +type TailscaleEndpointOptions struct { + DialerOptions + StateDirectory string `json:"state_directory,omitempty"` + AuthKey string `json:"auth_key,omitempty"` + ControlURL string `json:"control_url,omitempty"` + Ephemeral bool `json:"ephemeral,omitempty"` + Hostname string `json:"hostname,omitempty"` + ExitNode string `json:"exit_node,omitempty"` + ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"` + AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"` + AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"` + UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"` +} + +type TailscaleDNSServerOptions struct { + Endpoint string `json:"endpoint,omitempty"` + AcceptDefaultResolvers bool `json:"accept_default_resolvers,omitempty"` +} diff --git a/protocol/tailscale/dns_transport.go b/protocol/tailscale/dns_transport.go new file mode 100644 index 00000000..0c83c698 --- /dev/null +++ b/protocol/tailscale/dns_transport.go @@ -0,0 +1,320 @@ +package tailscale + +import ( + "context" + "net" + "net/http" + "net/netip" + "net/url" + "os" + "reflect" + "strings" + "sync" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/sing-box/common/tls" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/dns/transport" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" + nDNS "github.com/sagernet/tailscale/net/dns" + "github.com/sagernet/tailscale/types/dnstype" + "github.com/sagernet/tailscale/wgengine/router" + "github.com/sagernet/tailscale/wgengine/wgcfg" + + mDNS "github.com/miekg/dns" + "go4.org/netipx" + "golang.org/x/net/http2" +) + +func RegistryTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.TailscaleDNSServerOptions](registry, C.DNSTypeTailscale, NewDNSTransport) +} + +type DNSTransport struct { + dns.TransportAdapter + ctx context.Context + logger logger.ContextLogger + endpointTag string + acceptDefaultResolvers bool + dnsRouter adapter.DNSRouter + endpointManager adapter.EndpointManager + cfg *wgcfg.Config + dnsCfg *nDNS.Config + endpoint *Endpoint + routePrefixes []netip.Prefix + routes map[string][]adapter.DNSTransport + hosts map[string][]netip.Addr + defaultResolvers []adapter.DNSTransport +} + +func NewDNSTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.TailscaleDNSServerOptions) (adapter.DNSTransport, error) { + if options.Endpoint == "" { + return nil, E.New("missing tailscale endpoint tag") + } + return &DNSTransport{ + TransportAdapter: dns.NewTransportAdapter(C.DNSTypeTailscale, tag, nil), + ctx: ctx, + logger: logger, + endpointTag: options.Endpoint, + acceptDefaultResolvers: options.AcceptDefaultResolvers, + dnsRouter: service.FromContext[adapter.DNSRouter](ctx), + endpointManager: service.FromContext[adapter.EndpointManager](ctx), + }, nil +} + +func (t *DNSTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateInitialize { + return nil + } + rawOutbound, loaded := t.endpointManager.Get(t.endpointTag) + if !loaded { + return E.New("endpoint not found: ", t.endpointTag) + } + ep, isTailscale := rawOutbound.(*Endpoint) + if !isTailscale { + return E.New("endpoint is not Tailscale: ", t.endpointTag) + } + if ep.onReconfig != nil { + return E.New("only one Tailscale DNS server is allowed for single endpoint") + } + ep.onReconfig = t.onReconfig + t.endpoint = ep + return nil +} + +func (t *DNSTransport) Reset() { +} + +func (t *DNSTransport) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *nDNS.Config) { + if cfg == nil || dnsCfg == nil { + return + } + if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) { + return + } + t.cfg = cfg + t.dnsCfg = dnsCfg + err := t.updateDNSServers(routerCfg, dnsCfg) + if err != nil { + t.logger.Error(E.Cause(err, "update DNS servers")) + } +} + +func (t *DNSTransport) updateDNSServers(routeConfig *router.Config, dnsConfig *nDNS.Config) error { + t.routePrefixes = buildRoutePrefixes(routeConfig) + directDialerOnce := sync.OnceValue(func() N.Dialer { + directDialer := common.Must1(dialer.NewDefault(t.ctx, option.DialerOptions{})) + return &DNSDialer{transport: t, fallbackDialer: directDialer} + }) + routes := make(map[string][]adapter.DNSTransport) + for domain, resolvers := range dnsConfig.Routes { + var myResolvers []adapter.DNSTransport + for _, resolver := range resolvers { + myResolver, err := t.createResolver(directDialerOnce, resolver) + if err != nil { + return err + } + myResolvers = append(myResolvers, myResolver) + } + routes[domain.WithTrailingDot()] = myResolvers + } + hosts := make(map[string][]netip.Addr) + for domain, addresses := range dnsConfig.Hosts { + hosts[domain.WithTrailingDot()] = addresses + } + var defaultResolvers []adapter.DNSTransport + for _, resolver := range dnsConfig.DefaultResolvers { + myResolver, err := t.createResolver(directDialerOnce, resolver) + if err != nil { + return err + } + defaultResolvers = append(defaultResolvers, myResolver) + } + t.routes = routes + t.hosts = hosts + t.defaultResolvers = defaultResolvers + if len(defaultResolvers) > 0 { + t.logger.Info("updated ", len(routes), " routes, ", len(hosts), " hosts, default resolvers: ", + strings.Join(common.Map(dnsConfig.DefaultResolvers, func(it *dnstype.Resolver) string { return it.Addr }), " ")) + } else { + t.logger.Info("updated ", len(routes), " routes, ", len(hosts), " hosts") + } + return nil +} + +func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dnstype.Resolver) (adapter.DNSTransport, error) { + serverURL, parseURLErr := url.Parse(resolver.Addr) + var myDialer N.Dialer + if parseURLErr == nil && serverURL.Scheme == "http" { + myDialer = t.endpoint + } else { + myDialer = directDialer() + } + if len(resolver.BootstrapResolution) > 0 { + bootstrapTransport := transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, M.SocksaddrFrom(resolver.BootstrapResolution[0], 53)) + myDialer = dialer.NewResolveDialer(t.ctx, myDialer, false, "", adapter.DNSQueryOptions{Transport: bootstrapTransport}, 0) + } + if serverAddr := M.ParseSocksaddr(resolver.Addr); serverAddr.IsValid() { + if serverAddr.Port == 0 { + serverAddr.Port = 53 + } + return transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, serverAddr), nil + } else if parseURLErr != nil { + return nil, E.Cause(parseURLErr, "parse resolver address") + } else { + switch serverURL.Scheme { + case "https": + serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port()) + if serverAddr.Port == 0 { + serverAddr.Port = 443 + } + tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.AddrString(), option.OutboundTLSOptions{ + ALPN: []string{http2.NextProtoTLS, "http/1.1"}, + })) + return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil + case "http": + serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port()) + if serverAddr.Port == 0 { + serverAddr.Port = 80 + } + return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, nil), nil + // case "tls": + default: + return nil, E.New("unknown resolver scheme: ", serverURL.Scheme) + } + } +} + +func buildRoutePrefixes(routeConfig *router.Config) []netip.Prefix { + var builder netipx.IPSetBuilder + for _, localAddr := range routeConfig.LocalAddrs { + builder.AddPrefix(localAddr) + } + for _, route := range routeConfig.Routes { + builder.AddPrefix(route) + } + for _, route := range routeConfig.LocalRoutes { + builder.AddPrefix(route) + } + for _, route := range routeConfig.SubnetRoutes { + builder.AddPrefix(route) + } + ipSet, err := builder.IPSet() + if err != nil { + return nil + } + return ipSet.Prefixes() +} + +func (t *DNSTransport) Close() error { + return nil +} + +func (t *DNSTransport) Raw() bool { + return true +} + +func (t *DNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + if len(message.Question) != 1 { + return nil, os.ErrInvalid + } + question := message.Question[0] + addresses, hostsLoaded := t.hosts[question.Name] + if hostsLoaded { + switch question.Qtype { + case mDNS.TypeA: + addresses4 := common.Filter(addresses, func(addr netip.Addr) bool { + return addr.Is4() + }) + if len(addresses4) > 0 { + return dns.FixedResponse(message.Id, question, addresses4, C.DefaultDNSTTL), nil + } + case mDNS.TypeAAAA: + addresses6 := common.Filter(addresses, func(addr netip.Addr) bool { + return addr.Is6() + }) + if len(addresses6) > 0 { + return dns.FixedResponse(message.Id, question, addresses6, C.DefaultDNSTTL), nil + } + } + } + for domainSuffix, transports := range t.routes { + if strings.HasSuffix(question.Name, domainSuffix) { + if len(transports) == 0 { + return &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: message.Id, + Rcode: mDNS.RcodeNameError, + Response: true, + }, + Question: []mDNS.Question{question}, + }, nil + } + var lastErr error + for _, dnsTransport := range transports { + response, err := dnsTransport.Exchange(ctx, message) + if err != nil { + lastErr = err + continue + } + return response, nil + } + return nil, lastErr + } + } + if t.acceptDefaultResolvers { + if len(t.defaultResolvers) > 0 { + var lastErr error + for _, resolver := range t.defaultResolvers { + response, err := resolver.Exchange(ctx, message) + if err != nil { + lastErr = err + continue + } + return response, nil + } + return nil, lastErr + } else { + return nil, E.New("missing default resolvers") + } + } + return nil, dns.RCodeNameError +} + +type DNSDialer struct { + transport *DNSTransport + fallbackDialer N.Dialer +} + +func (d *DNSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + if destination.IsFqdn() { + panic("invalid request here") + } + for _, prefix := range d.transport.routePrefixes { + if prefix.Contains(destination.Addr) { + return d.transport.endpoint.DialContext(ctx, network, destination) + } + } + return d.fallbackDialer.DialContext(ctx, network, destination) +} + +func (d *DNSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + if destination.IsFqdn() { + panic("invalid request here") + } + for _, prefix := range d.transport.routePrefixes { + if prefix.Contains(destination.Addr) { + return d.transport.endpoint.ListenPacket(ctx, destination) + } + } + return d.fallbackDialer.ListenPacket(ctx, destination) +} diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go new file mode 100644 index 00000000..6c2c111b --- /dev/null +++ b/protocol/tailscale/endpoint.go @@ -0,0 +1,473 @@ +package tailscale + +import ( + "context" + "fmt" + "net" + "net/netip" + "net/url" + "os" + "path/filepath" + "runtime" + "strings" + "sync/atomic" + "syscall" + "time" + + "github.com/sagernet/gvisor/pkg/tcpip" + "github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet" + "github.com/sagernet/gvisor/pkg/tcpip/header" + "github.com/sagernet/gvisor/pkg/tcpip/stack" + "github.com/sagernet/gvisor/pkg/tcpip/transport/tcp" + "github.com/sagernet/gvisor/pkg/tcpip/transport/udp" + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/endpoint" + "github.com/sagernet/sing-box/common/dialer" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/experimental/libbox/platform" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-tun" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/bufio" + "github.com/sagernet/sing/common/control" + E "github.com/sagernet/sing/common/exceptions" + F "github.com/sagernet/sing/common/format" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" + "github.com/sagernet/sing/service/filemanager" + "github.com/sagernet/tailscale/ipn" + "github.com/sagernet/tailscale/net/netmon" + "github.com/sagernet/tailscale/net/tsaddr" + "github.com/sagernet/tailscale/tsnet" + "github.com/sagernet/tailscale/types/ipproto" + "github.com/sagernet/tailscale/version" + "github.com/sagernet/tailscale/wgengine" + "github.com/sagernet/tailscale/wgengine/filter" +) + +func init() { + version.SetVersion("sing-box " + C.Version) +} + +func RegisterEndpoint(registry *endpoint.Registry) { + endpoint.Register[option.TailscaleEndpointOptions](registry, C.TypeTailscale, NewEndpoint) +} + +type Endpoint struct { + endpoint.Adapter + ctx context.Context + router adapter.Router + logger logger.ContextLogger + dnsRouter adapter.DNSRouter + network adapter.NetworkManager + platformInterface platform.Interface + server *tsnet.Server + stack *stack.Stack + filter *atomic.Pointer[filter.Filter] + onReconfig wgengine.ReconfigListener + + exitNode string + exitNodeAllowLANAccess bool + advertiseRoutes []netip.Prefix + advertiseExitNode bool + + udpTimeout time.Duration +} + +func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) { + stateDirectory := options.StateDirectory + if stateDirectory == "" { + stateDirectory = "tailscale" + } + hostname := options.Hostname + if hostname == "" { + osHostname, _ := os.Hostname() + osHostname = strings.TrimSpace(osHostname) + hostname = osHostname + } + if hostname == "" { + hostname = "sing-box" + } + stateDirectory = filemanager.BasePath(ctx, os.ExpandEnv(stateDirectory)) + stateDirectory, _ = filepath.Abs(stateDirectory) + for _, advertiseRoute := range options.AdvertiseRoutes { + if advertiseRoute.Addr().IsUnspecified() && advertiseRoute.Bits() == 0 { + return nil, E.New("`advertise_routes` cannot be default, use `advertise_exit_node` instead.") + } + } + if options.AdvertiseExitNode && options.ExitNode != "" { + return nil, E.New("cannot advertise an exit node and use an exit node at the same time.") + } + var udpTimeout time.Duration + if options.UDPTimeout != 0 { + udpTimeout = time.Duration(options.UDPTimeout) + } else { + udpTimeout = C.UDPTimeout + } + var remoteIsDomain bool + if options.ControlURL != "" { + controlURL, err := url.Parse(options.ControlURL) + if err != nil { + return nil, E.Cause(err, "parse control URL") + } + remoteIsDomain = M.IsDomainName(controlURL.Hostname()) + } else { + // controlplane.tailscale.com + remoteIsDomain = true + } + outboundDialer, err := dialer.NewWithOptions(dialer.Options{ + Context: ctx, + Options: options.DialerOptions, + RemoteIsDomain: remoteIsDomain, + ResolverOnDetour: true, + NewDialer: true, + }) + if err != nil { + return nil, err + } + dnsRouter := service.FromContext[adapter.DNSRouter](ctx) + server := &tsnet.Server{ + Dir: stateDirectory, + Hostname: hostname, + Logf: func(format string, args ...any) { + logger.Trace(fmt.Sprintf(format, args...)) + }, + UserLogf: func(format string, args ...any) { + logger.Debug(fmt.Sprintf(format, args...)) + }, + Ephemeral: options.Ephemeral, + AuthKey: options.AuthKey, + ControlURL: options.ControlURL, + Dialer: &endpointDialer{Dialer: outboundDialer, logger: logger}, + LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) { + return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions()) + }, + } + return &Endpoint{ + Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP}, nil), + ctx: ctx, + router: router, + logger: logger, + dnsRouter: dnsRouter, + network: service.FromContext[adapter.NetworkManager](ctx), + platformInterface: service.FromContext[platform.Interface](ctx), + server: server, + exitNode: options.ExitNode, + exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess, + advertiseRoutes: options.AdvertiseRoutes, + advertiseExitNode: options.AdvertiseExitNode, + udpTimeout: udpTimeout, + }, nil +} + +func (t *Endpoint) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + if t.platformInterface != nil { + err := t.network.UpdateInterfaces() + if err != nil { + return err + } + netmon.RegisterInterfaceGetter(func() ([]netmon.Interface, error) { + return common.Map(t.network.InterfaceFinder().Interfaces(), func(it control.Interface) netmon.Interface { + return netmon.Interface{ + Interface: &net.Interface{ + Index: it.Index, + MTU: it.MTU, + Name: it.Name, + HardwareAddr: it.HardwareAddr, + Flags: it.Flags, + }, + AltAddrs: common.Map(it.Addresses, func(it netip.Prefix) net.Addr { + return &net.IPNet{ + IP: it.Addr().AsSlice(), + Mask: net.CIDRMask(it.Bits(), it.Addr().BitLen()), + } + }), + } + }), nil + }) + if runtime.GOOS == "android" { + setAndroidProtectFunc(t.platformInterface) + } + } + err := t.server.Start() + if err != nil { + return err + } + if t.onReconfig != nil { + t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig) + } + + ipStack := t.server.ExportNetstack().ExportIPStack() + ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket) + udpForwarder := tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout) + ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket) + t.stack = ipStack + + localBackend := t.server.ExportLocalBackend() + perfs := &ipn.MaskedPrefs{ + ExitNodeIPSet: true, + AdvertiseRoutesSet: true, + } + if len(t.advertiseRoutes) > 0 { + perfs.AdvertiseRoutes = t.advertiseRoutes + } + if t.advertiseExitNode { + perfs.AdvertiseRoutes = append(perfs.AdvertiseRoutes, tsaddr.ExitRoutes()...) + } + _, err = localBackend.EditPrefs(perfs) + if err != nil { + return E.Cause(err, "update prefs") + } + t.filter = localBackend.ExportFilter() + + go t.watchState() + return nil +} + +func (t *Endpoint) watchState() { + localBackend := t.server.ExportLocalBackend() + localBackend.WatchNotifications(t.ctx, ipn.NotifyInitialState, nil, func(roNotify *ipn.Notify) (keepGoing bool) { + if roNotify.State != nil && *roNotify.State != ipn.NeedsLogin && *roNotify.State != ipn.NoState { + return false + } + authURL := localBackend.StatusWithoutPeers().AuthURL + if authURL != "" { + t.logger.Info("Waiting for authentication: ", authURL) + if t.platformInterface != nil { + err := t.platformInterface.SendNotification(&platform.Notification{ + Identifier: "tailscale-authentication", + TypeName: "Tailscale Authentication Notifications", + TypeID: 10, + Title: "Tailscale Authentication", + Body: F.ToString("Tailscale outbound[", t.Tag(), "] is waiting for authentication."), + OpenURL: authURL, + }) + if err != nil { + t.logger.Error("send authentication notification: ", err) + } + } + return false + } + return true + }) + if t.exitNode != "" { + localBackend.WatchNotifications(t.ctx, ipn.NotifyInitialState, nil, func(roNotify *ipn.Notify) (keepGoing bool) { + if roNotify.State == nil || *roNotify.State != ipn.Running { + return true + } + status, err := common.Must1(t.server.LocalClient()).Status(t.ctx) + if err != nil { + t.logger.Error("set exit node: ", err) + return + } + perfs := &ipn.MaskedPrefs{ + Prefs: ipn.Prefs{ + ExitNodeAllowLANAccess: t.exitNodeAllowLANAccess, + }, + ExitNodeIPSet: true, + ExitNodeAllowLANAccessSet: true, + } + err = perfs.SetExitNodeIP(t.exitNode, status) + if err != nil { + t.logger.Error("set exit node: ", err) + return true + } + _, err = localBackend.EditPrefs(perfs) + if err != nil { + t.logger.Error("set exit node: ", err) + return true + } + return false + }) + } +} + +func (t *Endpoint) Close() error { + netmon.RegisterInterfaceGetter(nil) + if runtime.GOOS == "android" { + setAndroidProtectFunc(nil) + } + return common.Close(common.PtrOrNil(t.server)) +} + +func (t *Endpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + switch network { + case N.NetworkTCP: + t.logger.InfoContext(ctx, "outbound connection to ", destination) + case N.NetworkUDP: + t.logger.InfoContext(ctx, "outbound packet connection to ", destination) + } + if destination.IsFqdn() { + destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) + if err != nil { + return nil, err + } + return N.DialSerial(ctx, t, network, destination, destinationAddresses) + } + addr := tcpip.FullAddress{ + NIC: 1, + Port: destination.Port, + Addr: addressFromAddr(destination.Addr), + } + var networkProtocol tcpip.NetworkProtocolNumber + if destination.IsIPv4() { + networkProtocol = header.IPv4ProtocolNumber + } else { + networkProtocol = header.IPv6ProtocolNumber + } + switch N.NetworkName(network) { + case N.NetworkTCP: + tcpConn, err := gonet.DialContextTCP(ctx, t.stack, addr, networkProtocol) + if err != nil { + return nil, err + } + return tcpConn, nil + case N.NetworkUDP: + udpConn, err := gonet.DialUDP(t.stack, nil, &addr, networkProtocol) + if err != nil { + return nil, err + } + return udpConn, nil + default: + return nil, E.Extend(N.ErrUnknownNetwork, network) + } +} + +func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + t.logger.InfoContext(ctx, "outbound packet connection to ", destination) + if destination.IsFqdn() { + destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) + if err != nil { + return nil, err + } + packetConn, _, err := N.ListenSerial(ctx, t, destination, destinationAddresses) + if err != nil { + return nil, err + } + return packetConn, err + } + addr4, addr6 := t.server.TailscaleIPs() + bind := tcpip.FullAddress{ + NIC: 1, + } + var networkProtocol tcpip.NetworkProtocolNumber + if destination.IsIPv4() { + if !addr4.IsValid() { + return nil, E.New("missing Tailscale IPv4 address") + } + networkProtocol = header.IPv4ProtocolNumber + bind.Addr = addressFromAddr(addr4) + } else { + if !addr6.IsValid() { + return nil, E.New("missing Tailscale IPv6 address") + } + networkProtocol = header.IPv6ProtocolNumber + bind.Addr = addressFromAddr(addr6) + } + udpConn, err := gonet.DialUDP(t.stack, &bind, nil, networkProtocol) + if err != nil { + return nil, err + } + return udpConn, nil +} + +func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error { + tsFilter := t.filter.Load() + if tsFilter != nil { + var ipProto ipproto.Proto + switch N.NetworkName(network) { + case N.NetworkTCP: + ipProto = ipproto.TCP + case N.NetworkUDP: + ipProto = ipproto.UDP + } + response := tsFilter.Check(source.Addr, destination.Addr, destination.Port, ipProto) + switch response { + case filter.Drop: + return syscall.ECONNRESET + case filter.DropSilently: + return tun.ErrDrop + } + } + return t.router.PreMatch(adapter.InboundContext{ + Inbound: t.Tag(), + InboundType: t.Type(), + Network: network, + Source: source, + Destination: destination, + }) +} + +func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + var metadata adapter.InboundContext + metadata.Inbound = t.Tag() + metadata.InboundType = t.Type() + metadata.Source = source + addr4, addr6 := t.server.TailscaleIPs() + switch destination.Addr { + case addr4: + destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1}) + case addr6: + destination.Addr = netip.IPv6Loopback() + } + metadata.Destination = destination + t.logger.InfoContext(ctx, "inbound connection from ", source) + t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) + t.router.RouteConnectionEx(ctx, conn, metadata, onClose) +} + +func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + var metadata adapter.InboundContext + metadata.Inbound = t.Tag() + metadata.InboundType = t.Type() + metadata.Source = source + metadata.Destination = destination + addr4, addr6 := t.server.TailscaleIPs() + switch destination.Addr { + case addr4: + metadata.OriginDestination = destination + destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1}) + conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination) + case addr6: + metadata.OriginDestination = destination + destination.Addr = netip.IPv6Loopback() + conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination) + } + t.logger.InfoContext(ctx, "inbound packet connection from ", source) + t.logger.InfoContext(ctx, "inbound packet connection to ", destination) + t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) +} + +func addressFromAddr(destination netip.Addr) tcpip.Address { + if destination.Is6() { + return tcpip.AddrFrom16(destination.As16()) + } else { + return tcpip.AddrFrom4(destination.As4()) + } +} + +type endpointDialer struct { + N.Dialer + logger logger.ContextLogger +} + +func (d *endpointDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + switch N.NetworkName(network) { + case N.NetworkTCP: + d.logger.InfoContext(ctx, "output connection to ", destination) + case N.NetworkUDP: + d.logger.InfoContext(ctx, "output packet connection to ", destination) + } + return d.Dialer.DialContext(ctx, network, destination) +} + +func (d *endpointDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + d.logger.InfoContext(ctx, "output packet connection") + return d.Dialer.ListenPacket(ctx, destination) +} diff --git a/protocol/tailscale/protect_android.go b/protocol/tailscale/protect_android.go new file mode 100644 index 00000000..90ab615a --- /dev/null +++ b/protocol/tailscale/protect_android.go @@ -0,0 +1,16 @@ +package tailscale + +import ( + "github.com/sagernet/sing-box/experimental/libbox/platform" + "github.com/sagernet/tailscale/net/netns" +) + +func setAndroidProtectFunc(platformInterface platform.Interface) { + if platformInterface != nil { + netns.SetAndroidProtectFunc(func(fd int) error { + return platformInterface.AutoDetectInterfaceControl(fd) + }) + } else { + netns.SetAndroidProtectFunc(nil) + } +} diff --git a/protocol/tailscale/protect_nonandroid.go b/protocol/tailscale/protect_nonandroid.go new file mode 100644 index 00000000..eeb56bf6 --- /dev/null +++ b/protocol/tailscale/protect_nonandroid.go @@ -0,0 +1,8 @@ +//go:build !android + +package tailscale + +import "github.com/sagernet/sing-box/experimental/libbox/platform" + +func setAndroidProtectFunc(platformInterface platform.Interface) { +} From d43767f9928c9396ed3df91cb4a6899ba2c7e5e7 Mon Sep 17 00:00:00 2001 From: xchacha20-poly1305 Date: Wed, 12 Feb 2025 20:56:41 +0800 Subject: [PATCH 28/94] Remove single quotes of raw Moziila certs --- cmd/internal/update_certificates/main.go | 5 +- common/certificate/mozilla.go | 785 ++++++++++++----------- 2 files changed, 414 insertions(+), 376 deletions(-) diff --git a/cmd/internal/update_certificates/main.go b/cmd/internal/update_certificates/main.go index cf597c86..744d7724 100644 --- a/cmd/internal/update_certificates/main.go +++ b/cmd/internal/update_certificates/main.go @@ -60,7 +60,10 @@ func init() { generated.WriteString(record[nameIndex]) generated.WriteString("\n") generated.WriteString(" mozillaIncluded.AppendCertsFromPEM([]byte(`") - generated.WriteString(record[certIndex]) + cert := record[certIndex] + // Remove single quotes + cert = cert[1 : len(cert)-1] + generated.WriteString(cert) generated.WriteString("`))\n") } generated.WriteString("}\n") diff --git a/common/certificate/mozilla.go b/common/certificate/mozilla.go index 3bf1d175..097091d0 100644 --- a/common/certificate/mozilla.go +++ b/common/certificate/mozilla.go @@ -10,7 +10,7 @@ func init() { mozillaIncluded = x509.NewCertPool() // Actalis Authentication Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 @@ -42,10 +42,10 @@ K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // TunTrust Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv @@ -77,10 +77,10 @@ nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Amazon Root CA 1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL @@ -99,10 +99,10 @@ N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy rqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Amazon Root CA 2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL @@ -132,10 +132,10 @@ n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE 76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H 9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT 4PsJYGw= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Amazon Root CA 3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG @@ -146,10 +146,10 @@ QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM YyRIHN8wfdVoOw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Amazon Root CA 4 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG @@ -161,10 +161,10 @@ M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW 1KyLa2tJElMzrdfkviT8tQp21KW8EA== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Starfield Services Root Certificate Authority - G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs @@ -187,10 +187,10 @@ qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn 0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN sSi6 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certum CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM @@ -208,10 +208,10 @@ xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs 6GAqm4VKQPNriiTsBhYscw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certum EC-384 CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT @@ -225,10 +225,10 @@ QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certum Trusted Network CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU @@ -249,10 +249,10 @@ I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI 03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certum Trusted Network CA 2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG @@ -285,10 +285,10 @@ b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P 5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi DrW5viSP ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certum Trusted Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV @@ -320,10 +320,10 @@ P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP 0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Autoridad de Certificacion Firmaprofesional CIF A62634068 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 @@ -357,10 +357,10 @@ CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // FIRMAPROFESIONAL CA ROOT-A WEB - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQsw CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB @@ -375,10 +375,10 @@ BBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO PQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgLcFBTApFw hVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dG XSaQpYXFuXqUPoeovQA= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // ANF Secure Server Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV @@ -411,10 +411,10 @@ OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Buypass Class 2 Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow @@ -444,10 +444,10 @@ I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h 3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Buypass Class 3 Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow @@ -477,10 +477,10 @@ kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq 4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certainly Root E1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ @@ -492,10 +492,10 @@ BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certainly Root R1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 @@ -525,10 +525,10 @@ Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 OV+KmalBWQewLK8= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certigna - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ @@ -549,10 +549,10 @@ fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Certigna Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x @@ -587,10 +587,10 @@ faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw 3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // certSIGN ROOT CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP @@ -609,10 +609,10 @@ MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN 9u6wWk5JRFRYX0KD ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // certSIGN ROOT CA G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ @@ -642,10 +642,10 @@ pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE 1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX QRBdJ3NghVdJIgc= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // ePKI Root Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe @@ -677,10 +677,10 @@ o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D hNQ+IIX3Sj0rnP0qCglN6oH4EZw= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // HiPKI Root CA - G1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa @@ -710,10 +710,10 @@ Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // CommScope Public Trust ECC Root-01 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMw TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNa @@ -726,10 +726,10 @@ VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg 2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uS Um9poIyNStDuiw7LR47QjRE= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // CommScope Public Trust ECC Root-02 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMw TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRa @@ -742,10 +742,10 @@ VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/n ich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AV mkzw5l4lIhVtwodZ0LKOag== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // CommScope Public Trust RSA Root-01 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQEL BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1 @@ -775,10 +775,10 @@ Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054A5U+1C0w lREQKC6/oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHn YfkUyq+Dj7+vsQpZXdxc1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVoc icCMb3SgazNNtQEo/a2tiRc7ppqEvOuM6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // CommScope Public Trust RSA Root-02 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQEL BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2 @@ -808,10 +808,10 @@ PjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2d lklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670 v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17O rg3bhzjlP1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SecureSign Root CA12 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQEL BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgw @@ -831,10 +831,10 @@ mBClnW8Zt7vPemVV2zfrPIpyMpcemik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA 8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPSvWKErI4cqc1avTc7bgoitPQV 55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhgaaaI5gdka9at/ yOPiZwud9AzqVN/Ssq+xIvEg37xEHA== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SecureSign Root CA14 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEM BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgw @@ -865,10 +865,10 @@ dB7h7sxaOgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtl Lor6CZpO2oYofaphNdgOpygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB 365jJ6UeTo3cKXhZ+PmhIIynJkBugnLNeLLIjzwec+fBH7/PzqUqm9tEZDKgu39c JRNItX+S ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SecureSign Root CA15 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMw UTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBM dGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMy @@ -881,10 +881,10 @@ Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT 9DAKBggqhkjOPQQDAwNoADBlAjEA2S6Jfl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp 4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJSwdLZrWeqrqgHkHZAXQ6 bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // D-TRUST BR Root CA 1 2020 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 @@ -901,10 +901,45 @@ c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV dWNbFJWcHwHP2NVypw87 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) + + // D-TRUST BR Root CA 2 2023 + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBI +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE +LVRSVVNUIEJSIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUw +OTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi +MCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCTcfKr +i3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNE +gXtRr90zsWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8 +k12b9py0i4a6Ibn08OhZWiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCT +Rphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl +2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LULQyReS2tNZ9/WtT5PeB+U +cSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIvx9gvdhFP +/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bS +uREVMweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+ +0bpwHJwh5Q8xaRfX/Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4N +DfTisl01gLmB1IRpkQLLddCNxbU9CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+ +XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZ5Dw1t61 +GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tI +FoE9c+CeJyrrd6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67n +riv6uvw8l5VAk1/DLQOj7aRvU9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTR +VFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4nj8+AybmTNudX0KEPUUDAxxZiMrc +LmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdijYQ6qgYF/6FKC0ULn +4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff/vtD +hQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsG +koHU6XCPpz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46 +ls/pdu4D58JDUjxqgejBWoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aS +Ecr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/5usWDiJFAbzdNpQ0qTUmiteXue4Icr80 +knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jtn/mtd+ArY0+ew+43u3gJ +hJ65bvspmZDogNOfJA== +-----END CERTIFICATE-----`)) // D-TRUST EV Root CA 1 2020 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 @@ -921,10 +956,45 @@ c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb gfM0agPnIjhQW+0ZT0MW ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) + + // D-TRUST EV Root CA 2 2023 + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBI +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE +LVRSVVNUIEVWIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUw +OTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi +MCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1sJkK +F8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE +7CUXFId/MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFe +EMbsh2aJgWi6zCudR3Mfvc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6 +lHPTGGkKSv/BAQP/eX+1SH977ugpbzZMlWGG2Pmic4ruri+W7mjNPU0oQvlFKzIb +RlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3YG14C8qKXO0elg6DpkiV +jTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq9107PncjLgc +jmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZx +TnXonMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+ +ARZZaBhDM7DS3LAaQzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nk +hbDhezGdpn9yo7nELC7MmVcOIQxFAZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knF +NXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqvyREBuH +kV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14 +QvBukEdHjqOSMo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4 +pZt+UPJ26oUFKidBK7GB0aL2QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q +3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xDUmPBEcrCRbH0O1P1aa4846XerOhU +t7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V4U/M5d40VxDJI3IX +cI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuodNv8 +ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT +2vFp4LJiTZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs +7dpn1mKmS00PaaLJvOwiS5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNP +gofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAst +Nl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L+KIkBI3Y4WNeApI02phh +XBxvWHZks/wCuPWdCg== +-----END CERTIFICATE-----`)) // D-TRUST Root CA 3 2013 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEDjCCAvagAwIBAgIDD92sMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkRF MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxHzAdBgNVBAMMFkQtVFJVU1QgUm9vdCBD QSAzIDIwMTMwHhcNMTMwOTIwMDgyNTUxWhcNMjgwOTIwMDgyNTUxWjBFMQswCQYD @@ -947,10 +1017,10 @@ t4RVLFzI9XRKEBmLo8ftNdYJSNMOwLo5qLBGArDbxohZwr78e7Erz35ih1WWzAFv m2chlTWL+BD8cRu3SzdppjvW7IvuwbDzJcmPkn2h6sPKRL8mpXSSnON065102ctN h9j8tGlsi6BDB2B4l+nZk3zCRrybN1Kj7Yo8E6l7U0tJmhEFLAtuVqwfLoJs4Gln tQ5tLdnkwBXxP/oYcuEVbSdbLTAoK59ImmQrme/ydUlfXA== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // D-TRUST Root Class 3 CA 2 2009 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha @@ -974,10 +1044,10 @@ o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y Johw1+qRzT65ysCQblrGXnRl11z+o+I= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // D-TRUST Root Class 3 CA 2 EV 2009 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw @@ -1001,10 +1071,10 @@ nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // D-Trust SBR Root CA 1 2022 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICXjCCAeOgAwIBAgIQUs/kjG2gSvc/gpcMgAmMlTAKBggqhkjOPQQDAzBJMQsw CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSMwIQYDVQQDExpELVRy dXN0IFNCUiBSb290IENBIDEgMjAyMjAeFw0yMjA3MDYxMTMwMDBaFw0zNzA3MDYx @@ -1018,10 +1088,10 @@ hjlodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Nicl9yb290X2Nh XzFfMjAyMi5jcmwwCgYIKoZIzj0EAwMDaQAwZgIxAJf53q5Lj5i1HkB/Mn1NVEPa ic3CqpI80YIec8/6TJIg+2MnxfVzPQk996dhhozzagIxAOcvfLj1JYw7OR82q431 hqIu4Xpk2mc5Av7+Mz/Zc7ZYWzr8sqTZYHh3zHmnpq5VvQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // D-Trust SBR Root CA 2 2022 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFrDCCA5SgAwIBAgIQVNWjlR49lbpyG5rQMSFKujANBgkqhkiG9w0BAQ0FADBJ MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSMwIQYDVQQDExpE LVRydXN0IFNCUiBSb290IENBIDIgMjAyMjAeFw0yMjA3MDcwNzMwMDBaFw0zNzA3 @@ -1053,10 +1123,10 @@ JpmgVDuhEm1wYs7T+bi9IvzUmtS74jgWL7d9OcKwqQPpnM9+GI123F8Ru+tC7FAJ PlzskDHYGnK6P2kH7pg0wjSk1toT1qmE8gCGwFS6HhGw4rnEB7SR56rmMVZvsUTE KmK8ybBlnDT8DBpT3yEXu8JtoQrm8bCqRAlQSTh6XXHiMS4ZsN+VQgR9hIjOCiNn azidFt4G/ihwOKVarvyD7Q== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // T-TeleSec GlobalRoot Class 2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl @@ -1078,10 +1148,10 @@ IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN 9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP BSeOE6Fuwg== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // T-TeleSec GlobalRoot Class 3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl @@ -1103,10 +1173,10 @@ WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ 91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p TpPDpFQUWw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Telekom Security SMIME ECC Root 2021 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICRzCCAc2gAwIBAgIQFSrdFMkY0aRWQIamJa8HXzAKBggqhkjOPQQDAzBlMQsw CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH bWJIMS0wKwYDVQQDDCRUZWxla29tIFNlY3VyaXR5IFNNSU1FIEVDQyBSb290IDIw @@ -1120,10 +1190,10 @@ vZA6SzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQD AwNoADBlAjEA1rxIkodHA8dwOyW2H65GZ3N0ACdL5KUEogPfXiitbl4DyN1onLa/ lBBIlS8P/xiLAjABQDOel5dNBfJ0VAzNOf1qawnBJD9hjjiht+jXRBURYv8OYTdH S0B/Sl+yZ1pzdcI= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Telekom Security SMIME RSA Root 2023 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFtzCCA5+gAwIBAgIQDH5i9XlzO51Djotj7ZGVuDANBgkqhkiG9w0BAQwFADBl MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 eSBHbWJIMS0wKwYDVQQDDCRUZWxla29tIFNlY3VyaXR5IFNNSU1FIFJTQSBSb290 @@ -1155,10 +1225,10 @@ NDnPpZo2qsjtebzP9s4EUwvaslAjfLw+Jq3wDkO7JsuuwkDeNx8KoFHNY522T9jG R3y82LTtnovzEeKotT7srnA+fiK7NUgXYGIUkTCjdj2mUTaLHw3dajEcpe3dlqNu cg8TTaqnqVx4+QMSGJM3RRKJPfi+yr3ZvgzZGGSnyEE+dYIhOH1l9KDUE0sHeCn5 nX7Mhz/E2i6I3eML3FpRWunZEk+eAtv3BSVR ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Telekom Security TLS ECC Root 2020 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw @@ -1172,10 +1242,10 @@ MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn 27iQ7t0l ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Telekom Security TLS RSA Root 2023 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy @@ -1207,10 +1277,10 @@ L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm dTdmQRCsu/WU48IxK63nI1bMNSWSs1A= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Baltimore CyberTrust Root - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX @@ -1230,10 +1300,10 @@ jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert Assured ID Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv @@ -1254,10 +1324,10 @@ fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert Assured ID Root G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv @@ -1278,10 +1348,10 @@ lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo IhNzbM8m9Yop5w== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert Assured ID Root G3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg @@ -1295,10 +1365,10 @@ BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv 6pZjamVFkpUBtA== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert Global Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD @@ -1319,10 +1389,10 @@ hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert Global Root G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH @@ -1343,10 +1413,10 @@ Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl MrY= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert Global Root G3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe @@ -1360,10 +1430,10 @@ BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 sycX ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert High Assurance EV Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j @@ -1385,10 +1455,10 @@ hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep +OkuE6N36B9K ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert SMIME ECC P384 Root G5 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICHDCCAaOgAwIBAgIQBT9uoAYBcn3tP8OjtqPW7zAKBggqhkjOPQQDAzBQMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xKDAmBgNVBAMTH0Rp Z2lDZXJ0IFNNSU1FIEVDQyBQMzg0IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN @@ -1401,10 +1471,10 @@ wmQyF/7gZ5AurTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggq hkjOPQQDAwNnADBkAjA3RPUygONx6/Rtz3zMkZrDbnHY0iNdkk2CQm1cYZX2kfWn CPZql+mclC2YcP0ztgkCMAc8L7lYgl4Po2Kok2fwIMNpvwMsO1CnO69BOMlSSJHW Dvu8YDB8ZD8SHkV/UT70pg== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert SMIME RSA4096 Root G5 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFajCCA1KgAwIBAgIQBfa6BCODRst9XOa5W7ocVTANBgkqhkiG9w0BAQwFADBP MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJzAlBgNVBAMT HkRpZ2lDZXJ0IFNNSU1FIFJTQTQwOTYgUm9vdCBHNTAeFw0yMTAxMTUwMDAwMDBa @@ -1434,10 +1504,10 @@ CQxy5BMjYEyjyhcue2cA29DN6nofOSZXiTB3y07llUVPX/s2XD35ILU6ECVPkzJa 7sGW6OlWBLBJYU3seKidGMH/2OovVu+VK3sEXmfjVUDtOQT5C3n1aoxcD4makMfN i97bJjWhbs2zQvKiDzsMjpP/FM/895P35EEIbhlSEQ9TGXN4DM/YhYH4rVXIsJ5G Y6+cUu5cv/DAWzceCSDSPiPGoRVKDjZ+MMV5arwiiNkMUkAf3U4PZyYW0q0XHA== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert TLS ECC P384 Root G5 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 @@ -1450,10 +1520,10 @@ B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 DXZDjC5Ty3zfDBeWUA== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert TLS RSA4096 Root G5 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN @@ -1483,10 +1553,10 @@ p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DigiCert Trusted Root G4 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg @@ -1517,10 +1587,10 @@ cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 /YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DIGITALSIGN GLOBAL ROOT ECDSA CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICajCCAfCgAwIBAgIUNi2PcoiiKCfkAP8kxi3k6/qdtuEwCgYIKoZIzj0EAwMw ZDELMAkGA1UEBhMCUFQxKjAoBgNVBAoMIURpZ2l0YWxTaWduIENlcnRpZmljYWRv cmEgRGlnaXRhbDEpMCcGA1UEAwwgRElHSVRBTFNJR04gR0xPQkFMIFJPT1QgRUNE @@ -1534,10 +1604,10 @@ GDAWgBTOr0qLGnXi8TjnAvAWrV7qZNV7tDAdBgNVHQ4EFgQUzq9Kixp14vE45wLw Fq1e6mTVe7QwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMAqIxHGc RANNjbTHvKiu2TAnNWprFmPX/OdZ4aeJG0wxmiNVRObzQyHVRydvbVcBqgIxAPuy 6uKXf1G1n0jrvG81iahkcKtXds3AxhRgyn/iggBz98w16o4km+UIWccEjHN4/g== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // DIGITALSIGN GLOBAL ROOT RSA CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFtTCCA52gAwIBAgIUXVnIyqsJV/XmtdoplARq/8XUlYcwDQYJKoZIhvcNAQEN BQAwYjELMAkGA1UEBhMCUFQxKjAoBgNVBAoMIURpZ2l0YWxTaWduIENlcnRpZmlj YWRvcmEgRGlnaXRhbDEnMCUGA1UEAwweRElHSVRBTFNJR04gR0xPQkFMIFJPT1Qg @@ -1569,10 +1639,10 @@ NrskGnkcUdH+E7v/eCzcpL4v9sVLU8+nTJlecKxZiASuZAS/e6Z6TdPod72hflAV 8+9JMIVNIVeq2yx1l62BAYeisXCdHgZaA2CxP6ZtgizUFLGBpeg9iB20cixYN4qO OJS4c92p4Lj2d6KzfFjermk6tYulGrvy2HQGnP1icyAhdrF+cJ4Z1OsXYhk4mc02 K0f+McvfueSsCNPYpuvUnn5LZKRVXSsXyQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // CA Disig Root R2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy @@ -1602,10 +1672,10 @@ gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GLOBALTRUST 2020 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx @@ -1636,10 +1706,10 @@ MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn 4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // emSign ECC Root CA - C3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw @@ -1652,10 +1722,10 @@ BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c 3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J 0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // emSign ECC Root CA - G3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g @@ -1669,10 +1739,10 @@ zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD +JbNR6iC8hZVdyR+EhCVBCyj ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // emSign Root CA - C1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw @@ -1692,10 +1762,10 @@ kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT +xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // emSign Root CA - G1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH @@ -1716,10 +1786,10 @@ GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH 6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx iN66zB+Afko= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // AffirmTrust Commercial - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL @@ -1738,10 +1808,10 @@ vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // AffirmTrust Networking - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL @@ -1760,10 +1830,10 @@ QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // AffirmTrust Premium - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG @@ -1793,10 +1863,10 @@ YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e KeC2uAloGRwYQw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // AffirmTrust Premium ECC - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ @@ -1808,10 +1878,10 @@ A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Entrust Root Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW @@ -1837,10 +1907,10 @@ AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP 9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m 0vdXcDazv/wor3ElhVsT/h5/WrQ8 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Entrust Root Certification Authority - EC1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu @@ -1857,10 +1927,10 @@ Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Entrust Root Certification Authority - G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs @@ -1884,10 +1954,10 @@ Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v 1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Entrust Root Certification Authority - G4 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg @@ -1922,10 +1992,10 @@ b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk 5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Entrust.net Certification Authority (2048) - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 @@ -1949,10 +2019,10 @@ zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er fF6adulZkMV8gzURZVE= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Atos TrustedRoot 2011 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM @@ -1972,10 +2042,10 @@ DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Atos TrustedRoot Root CA ECC G2 2020 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICMTCCAbagAwIBAgIMC3MoERh0MBzvbwiEMAoGCCqGSM49BAMDMEsxCzAJBgNV BAYTAkRFMQ0wCwYDVQQKDARBdG9zMS0wKwYDVQQDDCRBdG9zIFRydXN0ZWRSb290 IFJvb3QgQ0EgRUNDIEcyIDIwMjAwHhcNMjAxMjE1MDgzOTEwWhcNNDAxMjEwMDgz @@ -1988,10 +2058,10 @@ shufvlwfjP2ztvuzDgmHMB0GA1UdDgQWBBRbH8RxbLIbn75cH4z9s7b7sw4JhzAO BgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAOzgmf3d5FTByx/oPijX FVlKgspTMOzrNqW5yM6TR1bIYabhbZJTlY/241VT8N165wIxALCH1RuzYPyRjYDK ohtRSzhUy6oee9flRJUWLzxEeC4luuqQ5OxS7lfsA4TzXtsWDQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Atos TrustedRoot Root CA ECC TLS 2021 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0 @@ -2004,10 +2074,10 @@ KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo 9H1/IISpQuQo ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Atos TrustedRoot Root CA RSA G2 2020 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFfzCCA2egAwIBAgIMR7opRlU+FpKXsKtAMA0GCSqGSIb3DQEBDAUAMEsxCzAJ BgNVBAYTAkRFMQ0wCwYDVQQKDARBdG9zMS0wKwYDVQQDDCRBdG9zIFRydXN0ZWRS b290IFJvb3QgQ0EgUlNBIEcyIDIwMjAwHhcNMjAxMjE1MDg0MTIzWhcNNDAxMjEw @@ -2038,10 +2108,10 @@ VD190nZ12V4+MkinjPKecgz4uFi4FyOlFId1WHoAgQciOWpMlKC1otunLMGw8aOb wEz3bXDqMZ/xrn0+cyjZod/6k/CbsPDizSUgde/ifTIFyZt27su9MR75lJhLJFhW SMDeBky9pjRd7RZhY3P7GeL6W9iXddRtnmA5XpSLAizrmc5gKm4bjKdLvP025pgf ZfJ/8eOPTIBGNli2oWXLzhxEdQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Atos TrustedRoot Root CA RSA TLS 2021 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00 @@ -2071,10 +2141,10 @@ AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5 dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GlobalSign - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx @@ -2105,26 +2175,10 @@ pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R 8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GlobalSign - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- -MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk -MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH -bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX -DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD -QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc -8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke -hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI -KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg -515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO -xwy8p2Fp8fc74SrL+SvzZpA3 ------END CERTIFICATE-----'`)) - - // GlobalSign - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 @@ -2144,10 +2198,26 @@ jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH WD9f ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) + + // GlobalSign + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE-----`)) // GlobalSign Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw @@ -2167,10 +2237,10 @@ yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GlobalSign Root E46 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw @@ -2182,10 +2252,10 @@ yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ 7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 +RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GlobalSign Root R46 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy @@ -2215,10 +2285,10 @@ u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP 4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GlobalSign Secure Mail Root E45 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICITCCAaegAwIBAgIQdlP+qicdlUZd1vGe5biQCjAKBggqhkjOPQQDAzBSMQsw CQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEoMCYGA1UEAxMf R2xvYmFsU2lnbiBTZWN1cmUgTWFpbCBSb290IEU0NTAeFw0yMDAzMTgwMDAwMDBa @@ -2231,10 +2301,10 @@ DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU3xNei1/CQAL9VreUTLYe1aaxFJYw CgYIKoZIzj0EAwMDaAAwZQIwE7C+13EgPuSrnM42En1fTB8qtWlFM1/TLVqy5IjH 3go2QjJ5naZruuH5RCp7isMSAjEAoGYcToedh8ntmUwbCu4tYMM3xx3NtXKw2cbv vPL/P/BS3QjnqmR5w+RpV5EvpMt8 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GlobalSign Secure Mail Root R45 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFcDCCA1igAwIBAgIQdlP+qExQq5+NMrUdA49X3DANBgkqhkiG9w0BAQwFADBS MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEoMCYGA1UE AxMfR2xvYmFsU2lnbiBTZWN1cmUgTWFpbCBSb290IFI0NTAeFw0yMDAzMTgwMDAw @@ -2265,10 +2335,10 @@ l+6NJllXu3jwelAwCbBgqp9O3Mk+HjrcYpMzsDpUdG8sMUXRaxEyamh29j32ahNe JJjn6h2az3iCB2D3TRDTgZpFjZ6vm9yAx0OylWikww7oCkcVv1Qz3AHn1aYec9h6 sr8vreNVMJ7fDkG84BH1oQyoIuHjAKNOcHyS4wTRekKKdZBZ45vRTKJkvXN5m2/y s8H2PA== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Go Daddy Class 2 Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 @@ -2291,10 +2361,10 @@ TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf ReYNnyicsbkqWletNw+vHX/bvZ8= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Go Daddy Root Certificate Authority - G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp @@ -2316,10 +2386,10 @@ gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo 2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI 4uJEvlz36hz1 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Starfield Class 2 Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw @@ -2342,10 +2412,10 @@ eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Starfield Root Certificate Authority - G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs @@ -2367,10 +2437,10 @@ sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GlobalSign - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw @@ -2381,10 +2451,10 @@ uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ +wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GTS Root R1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw @@ -2414,10 +2484,10 @@ Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT 0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm 2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GTS Root R2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw @@ -2447,10 +2517,10 @@ TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV 7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl 6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GTS Root R3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw @@ -2462,10 +2532,10 @@ jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // GTS Root R4 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw @@ -2477,10 +2547,10 @@ HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D 9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Hongkong Post Root CA 3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n @@ -2513,10 +2583,10 @@ JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG mpv0 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // ACCVRAIZ1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ @@ -2559,10 +2629,10 @@ I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // AC RAIZ FNMT-RCM - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ @@ -2593,10 +2663,10 @@ fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp 9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // AC RAIZ FNMT-RCM SERVIDORES SEGUROS - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S @@ -2611,10 +2681,10 @@ VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy v+c= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Staat der Nederlanden Root CA - G3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX @@ -2645,10 +2715,10 @@ fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq 1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM 94B7IWcnMFk= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w @@ -2673,10 +2743,10 @@ IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c 8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // HARICA Client ECC Root CA 2021 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICWjCCAeGgAwIBAgIQMWjZ2OFiVx7SGUSI5hB98DAKBggqhkjOPQQDAzBvMQsw CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh cmNoIEluc3RpdHV0aW9ucyBDQTEnMCUGA1UEAwweSEFSSUNBIENsaWVudCBFQ0Mg @@ -2690,10 +2760,10 @@ AQH/BAUwAwEB/zAdBgNVHQ4EFgQUUgjSvjKBJf31GpfsTl8au1PNkK0wDgYDVR0P AQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMEwxRUZPqOa+w3eyGhhLLYh7WOar lGtEA7AX/9+Cc0RRLP2THQZ7FNKJ7EAM7yEBLgIwL8kuWmwsHdmV4J6wuVxSfPb4 OMou8dQd8qJJopX4wVheT/5zCu8xsKsjWBOMi947 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // HARICA Client RSA Root CA 2021 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFqjCCA5KgAwIBAgIQVVL4HtsbJCyeu5YYzQIoPjANBgkqhkiG9w0BAQsFADBv MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEnMCUGA1UEAwweSEFSSUNBIENsaWVudCBS @@ -2725,10 +2795,10 @@ ndSV9ZmqRuqurL/0FBkk6Izs4/W8BmiKKgwFXwqXdafcfsD913oY3zDROEsfsJhw v8x8c/BuxDGlpJcdrL/ObCFKvicjZ/MGVoEKkY624QMFMyzaNAhNTlAjrR+lxdR6 /uoJ7KcoYItGfLXqm91P+edrFcaIz0Pb5SfcBFZub0YV8VYt6FwMc8MjgTggy8kM ac8sqzuEYDMZUv1pFDM= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // HARICA TLS ECC Root CA 2021 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v @@ -2742,10 +2812,10 @@ AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // HARICA TLS RSA Root CA 2021 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg @@ -2777,10 +2847,10 @@ BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU 63ZTGI0RmLo= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Hellenic Academic and Research Institutions ECC RootCA 2015 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl @@ -2796,10 +2866,10 @@ MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Hellenic Academic and Research Institutions RootCA 2015 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT @@ -2833,10 +2903,10 @@ bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 vm9qp/UsQu0yrbYhnr68 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // IdenTrust Commercial Root CA 1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw @@ -2866,10 +2936,10 @@ l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG 4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A 7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // IdenTrust Public Sector Root CA 1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN @@ -2899,10 +2969,10 @@ WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv 8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // ISRG Root X1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 @@ -2932,10 +3002,10 @@ oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // ISRG Root X2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 @@ -2948,10 +3018,10 @@ AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 /q4AaOeMSQ+2b1tbFfLn ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Izenpe.com - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD @@ -2984,10 +3054,10 @@ UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SZAFIR ROOT CA2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw @@ -3007,10 +3077,10 @@ nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // LAWtrust Root CA2 (4096) - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFmDCCA4CgAwIBAgIEVRpusTANBgkqhkiG9w0BAQsFADBDMQswCQYDVQQGEwJa QTERMA8GA1UEChMITEFXdHJ1c3QxITAfBgNVBAMTGExBV3RydXN0IFJvb3QgQ0Ey ICg0MDk2KTAgFw0yMzAyMTQwOTE5MzhaGA8yMDUzMDIxNDA5NDkzOFowQzELMAkG @@ -3041,10 +3111,10 @@ NAdigkz0fseHdA6wIR4JIIDBsxU9Rm3T8QaSP++glYocbncxtut4KQx77oKlT36k KV6eqi34jsDz/A0GhZtO3PfiCXzQFFEeerMjr/rRYSpltQHZuOMHyiR20vBKvu+G BIBCFXARaH7Xx7v+506bnJWlHEqkydAJjKrOSNIekpfXEentZsw33PXXG3SbpupC rF0y4Fj0gUf/0hLifhzcSXaWwx2fS8pcKjdbPYrROJsh2uO/RUPT4Fh3Hyg= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // e-Szigno Root CA 2017 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv @@ -3058,10 +3128,10 @@ A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ +efcMQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Microsec e-Szigno Root CA 2009 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G @@ -3084,10 +3154,10 @@ ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c 2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Microsoft ECC Root Certificate Authority 2017 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw @@ -3101,10 +3171,10 @@ BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Microsoft RSA Root Certificate Authority 2017 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 @@ -3136,10 +3206,10 @@ GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB RA+GsCyRxj3qrg+E ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // NAVER Global Root Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 @@ -3171,10 +3241,10 @@ LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX 5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul 9XXeifdy ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // NetLock Arany (Class Gold) Főtanúsítvány - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl @@ -3197,10 +3267,10 @@ bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // OISTE WISeKey Global Root GA CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl @@ -3223,10 +3293,10 @@ vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ /L7fCg0= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // OISTE WISeKey Global Root GB CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i @@ -3247,10 +3317,10 @@ gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // OISTE WISeKey Global Root GC CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg @@ -3264,10 +3334,10 @@ BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV 57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // QuoVadis Root CA 1 G3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 @@ -3297,10 +3367,10 @@ NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // QuoVadis Root CA 2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV @@ -3332,10 +3402,10 @@ mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y 4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza 8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // QuoVadis Root CA 2 G3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 @@ -3365,10 +3435,10 @@ dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // QuoVadis Root CA 3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV @@ -3405,10 +3475,10 @@ zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK 4SVhM7JZG+Ju1zdXtg2pEto= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // QuoVadis Root CA 3 G3 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 @@ -3438,10 +3508,10 @@ u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Security Communication ECC RootCA1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx @@ -3454,10 +3524,10 @@ ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu 9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Security Communication RootCA2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX @@ -3477,10 +3547,10 @@ okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy 1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // AAA Certificate Services - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj @@ -3504,10 +3574,10 @@ Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // COMODO Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV @@ -3531,10 +3601,10 @@ RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB ZQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // COMODO ECC Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT @@ -3549,10 +3619,10 @@ BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // COMODO RSA Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV @@ -3585,10 +3655,10 @@ S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl 0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB NVOFBkpdn627G190 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Sectigo Public Email Protection Root E46 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICMTCCAbegAwIBAgIQbvXTp0GOoFlApzBr0kBlVjAKBggqhkjOPQQDAzBaMQsw CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTEwLwYDVQQDEyhT ZWN0aWdvIFB1YmxpYyBFbWFpbCBQcm90ZWN0aW9uIFJvb3QgRTQ2MB4XDTIxMDMy @@ -3601,10 +3671,10 @@ HQYDVR0OBBYEFC1OjKfCI7JXqQZrPmsrifPDXkfOMA4GA1UdDwEB/wQEAwIBhjAP BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQCSnRpZY0VYjhsW5H16 bDZIMB8rcueQMzT9JKLGBoxvOzJXWvj+xkkSU5rZELKZUXICMAUlKjMh/JPmIqLM cFUoNVaiB8QhhCMaTEyZUJmSFMtK3Fb79dOPaiz1cTr4izsDng== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Sectigo Public Email Protection Root R46 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFgDCCA2igAwIBAgIQHUSeuQ2DkXSu3fLriLemozANBgkqhkiG9w0BAQwFADBa MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTEwLwYDVQQD EyhTZWN0aWdvIFB1YmxpYyBFbWFpbCBQcm90ZWN0aW9uIFJvb3QgUjQ2MB4XDTIx @@ -3635,10 +3705,10 @@ IS+0AlmzLdBykLoLFHR4S8/BX1VyjlQrE876WAzTuyzZqZFh+PjxtnvevKnMkgTM S2tfc4C2Ie1QT9d2h27O39K3vWKhfVhiaEVStj/eEtvtBGmedoiqAW3ahsdgG8NS rDfsUHGAciohRQpTRzwZ643SWQTeJbDrHzVvYH3Xtca7CyeN4E1U5c8dJgFuOzXI IBKJg/DS7Vg7NJ27MfUy/THzVho= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Sectigo Public Server Authentication Root E46 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN @@ -3651,10 +3721,10 @@ WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q 4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Sectigo Public Server Authentication Root R46 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw @@ -3685,10 +3755,10 @@ dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp 0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // USERTrust ECC Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT @@ -3703,10 +3773,10 @@ A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // USERTrust RSA Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV @@ -3739,10 +3809,10 @@ qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG jjxDah2nGN59PRbxYvnKkKj9 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SSL.com Client ECC Root CA 2022 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICQDCCAcagAwIBAgIQdvhIHq7wPHAf4D8lVAGD1TAKBggqhkjOPQQDAzBRMQsw CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9T U0wuY29tIENsaWVudCBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzAzMloX @@ -3756,10 +3826,10 @@ U81SGi9dYKDDXfuyHBwwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUC ME0HES0R+7kmwyHdcuEX/MHPFOpJznGHjtZT3BHNXVSKr9kt9IxR6rxmR+J/lYNg ZQIxAIwhTE+75bBQ35BiSebMkdv4P11xkQiOT5LJf6Zc6hN+7W3E6MMqb1wR4aXz alqaTQ== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SSL.com Client RSA Root CA 2022 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFjzCCA3egAwIBAgIQdq/uiJMVRbZQU5uAnKTfmjANBgkqhkiG9w0BAQsFADBR MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSgwJgYDVQQD DB9TU0wuY29tIENsaWVudCBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzEw @@ -3790,10 +3860,10 @@ miZPPiDPT0PzEIx/EGF6NsqqC33Mn0dEWa6llcaZU+MHaz1JELAY/10OhUMUS+dr OdmsupJ13UPKugGVrRqBKzrw48UvDBhNEMauwO3+BVJ/GQXLqa81CAw4IuT+VuVT Pr/k1rPZCMM91TMygSTFqeFlEbgyMzBxGEkdGkXGmhSKWDkobvPLUblJJmR4A8eR EYOpuZA0tm+qBZ6FKFeZvn8nBkliTaH8CeErRglMFJtWj0U= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SSL.com EV Root Certification Authority ECC - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp @@ -3808,10 +3878,10 @@ MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX 5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SSL.com EV Root Certification Authority RSA R2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy @@ -3844,10 +3914,10 @@ QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SSL.com Root Certification Authority ECC - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 @@ -3862,10 +3932,10 @@ EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SSL.com Root Certification Authority RSA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp @@ -3898,10 +3968,10 @@ K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY Ic2wBlX7Jz9TkHCpBB5XJ7k= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SSL.com TLS ECC Root CA 2022 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 @@ -3914,10 +3984,10 @@ GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp 15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SSL.com TLS RSA Root CA 2022 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX @@ -3948,10 +4018,10 @@ AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU 98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SwissSign Gold CA - G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF @@ -3983,45 +4053,10 @@ hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ ------END CERTIFICATE-----'`)) - - // SwissSign Silver CA - G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- -MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE -BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu -IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow -RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY -U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv -Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br -YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF -nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH -6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt -eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ -c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ -MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH -HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf -jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 -5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB -rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU -F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c -wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 -cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB -AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp -WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 -xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ -2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ -IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 -aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X -em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR -dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ -OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ -hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy -tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // TWCA CYBER Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQ MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290 IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5 @@ -4052,10 +4087,10 @@ Qh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz NxKfKjLji7gh7MMrZQzvIt6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzX xeSDwWrruoBa3lwtcHb4yOWHh8qgnaHlIhInD0Q9HWzq1MKLL295q39QpsQZp6F6 t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // TWCA Global Root CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 @@ -4085,10 +4120,10 @@ nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy KwbQBM0= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // TWCA Global Root CA G2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFlTCCA32gAwIBAgIQQAE0jMIAAAAAAAAAAZdY9DANBgkqhkiG9w0BAQwFADBU MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290 IENBMR8wHQYDVQQDExZUV0NBIEdsb2JhbCBSb290IENBIEcyMB4XDTIyMTEyMjA2 @@ -4119,10 +4154,10 @@ UamsVsAhTqMUdAU6vOO/bT1OP16lpG0pv4RRdVOOhhr1UXAqDRxOQOH9o+OlK2eQ ksdsroW/OpsXFcqcKpPUTTkNvCAIo42IbAkNjK5EIU3JcezYJtcXni0RGDyjIn24 J1S/aMg7QsyPXk7n3MLF+mpED41WiHrfiYRsoLM+PfFlAAmI6irrQM6zXawyF67B m+nQwfVJlN2nznxaB+uuIJwXMJJpk3Lzmltxm/5q33owaY6zLtsPLN0= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // TWCA Root Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz @@ -4142,10 +4177,10 @@ XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Telia Root CA v2 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 @@ -4176,10 +4211,10 @@ Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA rBPuUBQemMc= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // TeliaSonera Root CA v1 - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD @@ -4208,10 +4243,10 @@ vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Secure Global CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx @@ -4232,10 +4267,10 @@ H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // SecureTrust CA - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz @@ -4256,10 +4291,10 @@ NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR 3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Trustwave Global Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 @@ -4292,10 +4327,10 @@ VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK yeC2nOnOcXHebD8WpHk= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Trustwave Global ECC P256 Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 @@ -4309,10 +4344,10 @@ FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // Trustwave Global ECC P384 Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 @@ -4328,10 +4363,10 @@ A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu Sw== ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) // XRamp Global Certification Authority - mozillaIncluded.AppendCertsFromPEM([]byte(`'-----BEGIN CERTIFICATE----- + mozillaIncluded.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE----- MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY @@ -4355,5 +4390,5 @@ qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ O+7ETPTsJ3xCwnR8gooJybQDJbw= ------END CERTIFICATE-----'`)) +-----END CERTIFICATE-----`)) } From 382ed8158a96a12a21477d7af8a4e427a2286b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 14 Feb 2025 13:43:07 +0800 Subject: [PATCH 29/94] Update dependencies --- .golangci.yml | 2 +- common/tls/acme.go | 2 +- experimental/v2rayapi/stats.pb.go | 246 +++++++------------------ experimental/v2rayapi/stats_grpc.pb.go | 29 ++- go.mod | 36 ++-- go.sum | 92 +++++---- transport/v2raygrpc/client.go | 2 +- transport/v2raygrpc/conn.go | 1 + transport/v2raygrpc/custom_name.go | 2 +- transport/v2raygrpc/stream.pb.go | 45 ++--- transport/v2raygrpc/stream_grpc.pb.go | 81 +++----- 11 files changed, 212 insertions(+), 326 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index cdd53eca..1117e075 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,7 +21,7 @@ linters-settings: - -SA1003 run: - go: "1.23" + go: "1.24" build-tags: - with_gvisor - with_quic diff --git a/common/tls/acme.go b/common/tls/acme.go index 08b24ed2..3f251473 100644 --- a/common/tls/acme.go +++ b/common/tls/acme.go @@ -16,7 +16,7 @@ import ( "github.com/caddyserver/certmagic" "github.com/libdns/alidns" "github.com/libdns/cloudflare" - "github.com/mholt/acmez/acme" + "github.com/mholt/acmez/v3/acme" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/experimental/v2rayapi/stats.pb.go b/experimental/v2rayapi/stats.pb.go index 45cde824..19a5f619 100644 --- a/experimental/v2rayapi/stats.pb.go +++ b/experimental/v2rayapi/stats.pb.go @@ -3,6 +3,7 @@ package v2rayapi import ( reflect "reflect" sync "sync" + unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -16,23 +17,20 @@ const ( ) type GetStatsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Name of the stat counter. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Whether or not to reset the counter to fetching its value. - Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"` + Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *GetStatsRequest) Reset() { *x = GetStatsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GetStatsRequest) String() string { @@ -43,7 +41,7 @@ func (*GetStatsRequest) ProtoMessage() {} func (x *GetStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -73,21 +71,18 @@ func (x *GetStatsRequest) GetReset_() bool { } type Stat struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Stat) Reset() { *x = Stat{} - if protoimpl.UnsafeEnabled { - mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Stat) String() string { @@ -98,7 +93,7 @@ func (*Stat) ProtoMessage() {} func (x *Stat) ProtoReflect() protoreflect.Message { mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -128,20 +123,17 @@ func (x *Stat) GetValue() int64 { } type GetStatsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Stat *Stat `protobuf:"bytes,1,opt,name=stat,proto3" json:"stat,omitempty"` unknownFields protoimpl.UnknownFields - - Stat *Stat `protobuf:"bytes,1,opt,name=stat,proto3" json:"stat,omitempty"` + sizeCache protoimpl.SizeCache } func (x *GetStatsResponse) Reset() { *x = GetStatsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GetStatsResponse) String() string { @@ -152,7 +144,7 @@ func (*GetStatsResponse) ProtoMessage() {} func (x *GetStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -175,24 +167,21 @@ func (x *GetStatsResponse) GetStat() *Stat { } type QueryStatsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Deprecated, use Patterns instead - Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` - Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"` - Patterns []string `protobuf:"bytes,3,rep,name=patterns,proto3" json:"patterns,omitempty"` - Regexp bool `protobuf:"varint,4,opt,name=regexp,proto3" json:"regexp,omitempty"` + Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` + Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"` + Patterns []string `protobuf:"bytes,3,rep,name=patterns,proto3" json:"patterns,omitempty"` + Regexp bool `protobuf:"varint,4,opt,name=regexp,proto3" json:"regexp,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *QueryStatsRequest) Reset() { *x = QueryStatsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *QueryStatsRequest) String() string { @@ -203,7 +192,7 @@ func (*QueryStatsRequest) ProtoMessage() {} func (x *QueryStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -247,20 +236,17 @@ func (x *QueryStatsRequest) GetRegexp() bool { } type QueryStatsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Stat []*Stat `protobuf:"bytes,1,rep,name=stat,proto3" json:"stat,omitempty"` unknownFields protoimpl.UnknownFields - - Stat []*Stat `protobuf:"bytes,1,rep,name=stat,proto3" json:"stat,omitempty"` + sizeCache protoimpl.SizeCache } func (x *QueryStatsResponse) Reset() { *x = QueryStatsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *QueryStatsResponse) String() string { @@ -271,7 +257,7 @@ func (*QueryStatsResponse) ProtoMessage() {} func (x *QueryStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -294,18 +280,16 @@ func (x *QueryStatsResponse) GetStat() []*Stat { } type SysStatsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SysStatsRequest) Reset() { *x = SysStatsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SysStatsRequest) String() string { @@ -316,7 +300,7 @@ func (*SysStatsRequest) ProtoMessage() {} func (x *SysStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -332,29 +316,26 @@ func (*SysStatsRequest) Descriptor() ([]byte, []int) { } type SysStatsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + NumGoroutine uint32 `protobuf:"varint,1,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"` + NumGC uint32 `protobuf:"varint,2,opt,name=NumGC,proto3" json:"NumGC,omitempty"` + Alloc uint64 `protobuf:"varint,3,opt,name=Alloc,proto3" json:"Alloc,omitempty"` + TotalAlloc uint64 `protobuf:"varint,4,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"` + Sys uint64 `protobuf:"varint,5,opt,name=Sys,proto3" json:"Sys,omitempty"` + Mallocs uint64 `protobuf:"varint,6,opt,name=Mallocs,proto3" json:"Mallocs,omitempty"` + Frees uint64 `protobuf:"varint,7,opt,name=Frees,proto3" json:"Frees,omitempty"` + LiveObjects uint64 `protobuf:"varint,8,opt,name=LiveObjects,proto3" json:"LiveObjects,omitempty"` + PauseTotalNs uint64 `protobuf:"varint,9,opt,name=PauseTotalNs,proto3" json:"PauseTotalNs,omitempty"` + Uptime uint32 `protobuf:"varint,10,opt,name=Uptime,proto3" json:"Uptime,omitempty"` unknownFields protoimpl.UnknownFields - - NumGoroutine uint32 `protobuf:"varint,1,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"` - NumGC uint32 `protobuf:"varint,2,opt,name=NumGC,proto3" json:"NumGC,omitempty"` - Alloc uint64 `protobuf:"varint,3,opt,name=Alloc,proto3" json:"Alloc,omitempty"` - TotalAlloc uint64 `protobuf:"varint,4,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"` - Sys uint64 `protobuf:"varint,5,opt,name=Sys,proto3" json:"Sys,omitempty"` - Mallocs uint64 `protobuf:"varint,6,opt,name=Mallocs,proto3" json:"Mallocs,omitempty"` - Frees uint64 `protobuf:"varint,7,opt,name=Frees,proto3" json:"Frees,omitempty"` - LiveObjects uint64 `protobuf:"varint,8,opt,name=LiveObjects,proto3" json:"LiveObjects,omitempty"` - PauseTotalNs uint64 `protobuf:"varint,9,opt,name=PauseTotalNs,proto3" json:"PauseTotalNs,omitempty"` - Uptime uint32 `protobuf:"varint,10,opt,name=Uptime,proto3" json:"Uptime,omitempty"` + sizeCache protoimpl.SizeCache } func (x *SysStatsResponse) Reset() { *x = SysStatsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SysStatsResponse) String() string { @@ -365,7 +346,7 @@ func (*SysStatsResponse) ProtoMessage() {} func (x *SysStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -452,7 +433,7 @@ func (x *SysStatsResponse) GetUptime() uint32 { var File_experimental_v2rayapi_stats_proto protoreflect.FileDescriptor -var file_experimental_v2rayapi_stats_proto_rawDesc = []byte{ +var file_experimental_v2rayapi_stats_proto_rawDesc = string([]byte{ 0x0a, 0x21, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, @@ -523,23 +504,23 @@ var file_experimental_v2rayapi_stats_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +}) var ( file_experimental_v2rayapi_stats_proto_rawDescOnce sync.Once - file_experimental_v2rayapi_stats_proto_rawDescData = file_experimental_v2rayapi_stats_proto_rawDesc + file_experimental_v2rayapi_stats_proto_rawDescData []byte ) func file_experimental_v2rayapi_stats_proto_rawDescGZIP() []byte { file_experimental_v2rayapi_stats_proto_rawDescOnce.Do(func() { - file_experimental_v2rayapi_stats_proto_rawDescData = protoimpl.X.CompressGZIP(file_experimental_v2rayapi_stats_proto_rawDescData) + file_experimental_v2rayapi_stats_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_experimental_v2rayapi_stats_proto_rawDesc), len(file_experimental_v2rayapi_stats_proto_rawDesc))) }) return file_experimental_v2rayapi_stats_proto_rawDescData } var ( file_experimental_v2rayapi_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 7) - file_experimental_v2rayapi_stats_proto_goTypes = []interface{}{ + file_experimental_v2rayapi_stats_proto_goTypes = []any{ (*GetStatsRequest)(nil), // 0: experimental.v2rayapi.GetStatsRequest (*Stat)(nil), // 1: experimental.v2rayapi.Stat (*GetStatsResponse)(nil), // 2: experimental.v2rayapi.GetStatsResponse @@ -571,97 +552,11 @@ func file_experimental_v2rayapi_stats_proto_init() { if File_experimental_v2rayapi_stats_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_experimental_v2rayapi_stats_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_experimental_v2rayapi_stats_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Stat); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_experimental_v2rayapi_stats_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_experimental_v2rayapi_stats_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryStatsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_experimental_v2rayapi_stats_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryStatsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_experimental_v2rayapi_stats_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SysStatsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_experimental_v2rayapi_stats_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SysStatsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_experimental_v2rayapi_stats_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_experimental_v2rayapi_stats_proto_rawDesc), len(file_experimental_v2rayapi_stats_proto_rawDesc)), NumEnums: 0, NumMessages: 7, NumExtensions: 0, @@ -672,7 +567,6 @@ func file_experimental_v2rayapi_stats_proto_init() { MessageInfos: file_experimental_v2rayapi_stats_proto_msgTypes, }.Build() File_experimental_v2rayapi_stats_proto = out.File - file_experimental_v2rayapi_stats_proto_rawDesc = nil file_experimental_v2rayapi_stats_proto_goTypes = nil file_experimental_v2rayapi_stats_proto_depIdxs = nil } diff --git a/experimental/v2rayapi/stats_grpc.pb.go b/experimental/v2rayapi/stats_grpc.pb.go index ee950287..3788f520 100644 --- a/experimental/v2rayapi/stats_grpc.pb.go +++ b/experimental/v2rayapi/stats_grpc.pb.go @@ -10,8 +10,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( StatsService_GetStats_FullMethodName = "/experimental.v2rayapi.StatsService/GetStats" @@ -37,8 +37,9 @@ func NewStatsServiceClient(cc grpc.ClientConnInterface) StatsServiceClient { } func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetStatsResponse) - err := c.cc.Invoke(ctx, StatsService_GetStats_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, StatsService_GetStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -46,8 +47,9 @@ func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, } func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(QueryStatsResponse) - err := c.cc.Invoke(ctx, StatsService_QueryStats_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, StatsService_QueryStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -55,8 +57,9 @@ func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsReque } func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SysStatsResponse) - err := c.cc.Invoke(ctx, StatsService_GetSysStats_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, StatsService_GetSysStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -65,7 +68,7 @@ func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsReques // StatsServiceServer is the server API for StatsService service. // All implementations must embed UnimplementedStatsServiceServer -// for forward compatibility +// for forward compatibility. type StatsServiceServer interface { GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) @@ -73,7 +76,11 @@ type StatsServiceServer interface { mustEmbedUnimplementedStatsServiceServer() } -// UnimplementedStatsServiceServer must be embedded to have forward compatible implementations. +// UnimplementedStatsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. type UnimplementedStatsServiceServer struct{} func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) { @@ -88,6 +95,7 @@ func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsReq return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented") } func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {} +func (UnimplementedStatsServiceServer) testEmbeddedByValue() {} // UnsafeStatsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to StatsServiceServer will @@ -97,6 +105,13 @@ type UnsafeStatsServiceServer interface { } func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) { + // If the following call pancis, it indicates UnimplementedStatsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&StatsService_ServiceDesc, srv) } diff --git a/go.mod b/go.mod index 0d38a4a1..8ae859f8 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.23.1 toolchain go1.24.0 require ( - github.com/caddyserver/certmagic v0.20.0 - github.com/cloudflare/circl v1.3.7 + github.com/caddyserver/certmagic v0.21.7 + github.com/cloudflare/circl v1.6.0 github.com/cretz/bine v0.2.0 github.com/go-chi/chi/v5 v5.2.1 github.com/go-chi/render v1.0.3 @@ -16,9 +16,9 @@ require ( github.com/libdns/cloudflare v0.1.1 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 - github.com/mholt/acmez v1.2.0 + github.com/mholt/acmez/v3 v3.0.1 github.com/miekg/dns v1.1.63 - github.com/oschwald/maxminddb-golang v1.12.0 + github.com/oschwald/maxminddb-golang v1.13.1 github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 @@ -45,14 +45,14 @@ require ( github.com/stretchr/testify v1.10.0 go.uber.org/zap v1.27.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.32.0 + golang.org/x/crypto v0.33.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/mod v0.20.0 - golang.org/x/net v0.34.0 + golang.org/x/mod v0.23.0 + golang.org/x/net v0.35.0 golang.org/x/sys v0.30.0 - golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 - google.golang.org/grpc v1.63.2 - google.golang.org/protobuf v1.33.0 + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 + google.golang.org/grpc v1.70.0 + google.golang.org/protobuf v1.36.5 howett.net/plist v1.0.1 ) @@ -65,6 +65,7 @@ require ( github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/caddyserver/zerossl v0.1.3 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/coder/websocket v1.8.12 // indirect github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect @@ -97,13 +98,13 @@ require ( github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect github.com/jsimonetti/rtnetlink v1.4.0 // indirect github.com/klauspost/compress v1.17.11 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/sdnotify v1.0.0 // indirect - github.com/mdlayher/socket v0.5.0 // indirect + github.com/mdlayher/socket v0.5.1 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect github.com/onsi/ginkgo/v2 v2.17.2 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect @@ -127,17 +128,18 @@ require ( github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect github.com/vishvananda/netns v0.0.4 // indirect github.com/x448/float16 v0.8.4 // indirect - github.com/zeebo/blake3 v0.2.3 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap/exp v0.3.0 // indirect go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/term v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 9ba48be3..9cc5214e 100644 --- a/go.sum +++ b/go.sum @@ -10,14 +10,16 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= -github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= +github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= +github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= +github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= +github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0= @@ -48,8 +50,10 @@ github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -64,6 +68,8 @@ github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0= github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -101,9 +107,8 @@ github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUF github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= @@ -121,12 +126,12 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= -github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= -github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI= +github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY= github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= -github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= -github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= +github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8= +github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= @@ -137,8 +142,8 @@ github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= -github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= -github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= +github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= +github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= @@ -240,36 +245,48 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= -github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= +go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -278,18 +295,17 @@ golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -300,16 +316,16 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= -golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= diff --git a/transport/v2raygrpc/client.go b/transport/v2raygrpc/client.go index af922b45..e649aa8f 100644 --- a/transport/v2raygrpc/client.go +++ b/transport/v2raygrpc/client.go @@ -62,6 +62,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt dialOptions = append(dialOptions, grpc.WithContextDialer(func(ctx context.Context, server string) (net.Conn, error) { return dialer.DialContext(ctx, N.NetworkTCP, M.ParseSocksaddr(server)) })) + //nolint:staticcheck dialOptions = append(dialOptions, grpc.WithReturnConnectionError()) return &Client{ ctx: ctx, @@ -84,7 +85,6 @@ func (c *Client) connect() (*grpc.ClientConn, error) { return conn, nil } //nolint:staticcheck - //goland:noinspection GoDeprecation conn, err := grpc.DialContext(c.ctx, c.serverAddr, c.dialOptions...) if err != nil { return nil, err diff --git a/transport/v2raygrpc/conn.go b/transport/v2raygrpc/conn.go index 0a0a627f..c29da4f9 100644 --- a/transport/v2raygrpc/conn.go +++ b/transport/v2raygrpc/conn.go @@ -18,6 +18,7 @@ type GRPCConn struct { } func NewGRPCConn(service GunService) *GRPCConn { + //nolint:staticcheck if client, isClient := service.(GunService_TunClient); isClient { service = &clientConnWrapper{client} } diff --git a/transport/v2raygrpc/custom_name.go b/transport/v2raygrpc/custom_name.go index 1fe7d8cc..ce970dc6 100644 --- a/transport/v2raygrpc/custom_name.go +++ b/transport/v2raygrpc/custom_name.go @@ -34,7 +34,7 @@ func (c *gunServiceClient) TunCustomName(ctx context.Context, name string, opts if err != nil { return nil, err } - x := &gunServiceTunClient{stream} + x := &grpc.GenericClientStream[Hunk, Hunk]{ClientStream: stream} return x, nil } diff --git a/transport/v2raygrpc/stream.pb.go b/transport/v2raygrpc/stream.pb.go index 26c091e1..fc2688b5 100644 --- a/transport/v2raygrpc/stream.pb.go +++ b/transport/v2raygrpc/stream.pb.go @@ -3,6 +3,7 @@ package v2raygrpc import ( reflect "reflect" sync "sync" + unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -16,20 +17,17 @@ const ( ) type Hunk struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` unknownFields protoimpl.UnknownFields - - Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Hunk) Reset() { *x = Hunk{} - if protoimpl.UnsafeEnabled { - mi := &file_transport_v2raygrpc_stream_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_transport_v2raygrpc_stream_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Hunk) String() string { @@ -40,7 +38,7 @@ func (*Hunk) ProtoMessage() {} func (x *Hunk) ProtoReflect() protoreflect.Message { mi := &file_transport_v2raygrpc_stream_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -64,7 +62,7 @@ func (x *Hunk) GetData() []byte { var File_transport_v2raygrpc_stream_proto protoreflect.FileDescriptor -var file_transport_v2raygrpc_stream_proto_rawDesc = []byte{ +var file_transport_v2raygrpc_stream_proto_rawDesc = string([]byte{ 0x0a, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x76, 0x32, @@ -79,23 +77,23 @@ var file_transport_v2raygrpc_stream_proto_rawDesc = []byte{ 0x2f, 0x73, 0x61, 0x67, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +}) var ( file_transport_v2raygrpc_stream_proto_rawDescOnce sync.Once - file_transport_v2raygrpc_stream_proto_rawDescData = file_transport_v2raygrpc_stream_proto_rawDesc + file_transport_v2raygrpc_stream_proto_rawDescData []byte ) func file_transport_v2raygrpc_stream_proto_rawDescGZIP() []byte { file_transport_v2raygrpc_stream_proto_rawDescOnce.Do(func() { - file_transport_v2raygrpc_stream_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_v2raygrpc_stream_proto_rawDescData) + file_transport_v2raygrpc_stream_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_v2raygrpc_stream_proto_rawDesc), len(file_transport_v2raygrpc_stream_proto_rawDesc))) }) return file_transport_v2raygrpc_stream_proto_rawDescData } var ( file_transport_v2raygrpc_stream_proto_msgTypes = make([]protoimpl.MessageInfo, 1) - file_transport_v2raygrpc_stream_proto_goTypes = []interface{}{ + file_transport_v2raygrpc_stream_proto_goTypes = []any{ (*Hunk)(nil), // 0: transport.v2raygrpc.Hunk } ) @@ -115,25 +113,11 @@ func file_transport_v2raygrpc_stream_proto_init() { if File_transport_v2raygrpc_stream_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_transport_v2raygrpc_stream_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Hunk); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_transport_v2raygrpc_stream_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_v2raygrpc_stream_proto_rawDesc), len(file_transport_v2raygrpc_stream_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, @@ -144,7 +128,6 @@ func file_transport_v2raygrpc_stream_proto_init() { MessageInfos: file_transport_v2raygrpc_stream_proto_msgTypes, }.Build() File_transport_v2raygrpc_stream_proto = out.File - file_transport_v2raygrpc_stream_proto_rawDesc = nil file_transport_v2raygrpc_stream_proto_goTypes = nil file_transport_v2raygrpc_stream_proto_depIdxs = nil } diff --git a/transport/v2raygrpc/stream_grpc.pb.go b/transport/v2raygrpc/stream_grpc.pb.go index ea634849..d602ec45 100644 --- a/transport/v2raygrpc/stream_grpc.pb.go +++ b/transport/v2raygrpc/stream_grpc.pb.go @@ -10,8 +10,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( GunService_Tun_FullMethodName = "/transport.v2raygrpc.GunService/Tun" @@ -21,7 +21,7 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type GunServiceClient interface { - Tun(ctx context.Context, opts ...grpc.CallOption) (GunService_TunClient, error) + Tun(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Hunk, Hunk], error) } type gunServiceClient struct { @@ -32,52 +32,39 @@ func NewGunServiceClient(cc grpc.ClientConnInterface) GunServiceClient { return &gunServiceClient{cc} } -func (c *gunServiceClient) Tun(ctx context.Context, opts ...grpc.CallOption) (GunService_TunClient, error) { - stream, err := c.cc.NewStream(ctx, &GunService_ServiceDesc.Streams[0], GunService_Tun_FullMethodName, opts...) +func (c *gunServiceClient) Tun(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Hunk, Hunk], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &GunService_ServiceDesc.Streams[0], GunService_Tun_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &gunServiceTunClient{stream} + x := &grpc.GenericClientStream[Hunk, Hunk]{ClientStream: stream} return x, nil } -type GunService_TunClient interface { - Send(*Hunk) error - Recv() (*Hunk, error) - grpc.ClientStream -} - -type gunServiceTunClient struct { - grpc.ClientStream -} - -func (x *gunServiceTunClient) Send(m *Hunk) error { - return x.ClientStream.SendMsg(m) -} - -func (x *gunServiceTunClient) Recv() (*Hunk, error) { - m := new(Hunk) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GunService_TunClient = grpc.BidiStreamingClient[Hunk, Hunk] // GunServiceServer is the server API for GunService service. // All implementations must embed UnimplementedGunServiceServer -// for forward compatibility +// for forward compatibility. type GunServiceServer interface { - Tun(GunService_TunServer) error + Tun(grpc.BidiStreamingServer[Hunk, Hunk]) error mustEmbedUnimplementedGunServiceServer() } -// UnimplementedGunServiceServer must be embedded to have forward compatible implementations. +// UnimplementedGunServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. type UnimplementedGunServiceServer struct{} -func (UnimplementedGunServiceServer) Tun(GunService_TunServer) error { +func (UnimplementedGunServiceServer) Tun(grpc.BidiStreamingServer[Hunk, Hunk]) error { return status.Errorf(codes.Unimplemented, "method Tun not implemented") } func (UnimplementedGunServiceServer) mustEmbedUnimplementedGunServiceServer() {} +func (UnimplementedGunServiceServer) testEmbeddedByValue() {} // UnsafeGunServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GunServiceServer will @@ -87,34 +74,22 @@ type UnsafeGunServiceServer interface { } func RegisterGunServiceServer(s grpc.ServiceRegistrar, srv GunServiceServer) { + // If the following call pancis, it indicates UnimplementedGunServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&GunService_ServiceDesc, srv) } func _GunService_Tun_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(GunServiceServer).Tun(&gunServiceTunServer{stream}) + return srv.(GunServiceServer).Tun(&grpc.GenericServerStream[Hunk, Hunk]{ServerStream: stream}) } -type GunService_TunServer interface { - Send(*Hunk) error - Recv() (*Hunk, error) - grpc.ServerStream -} - -type gunServiceTunServer struct { - grpc.ServerStream -} - -func (x *gunServiceTunServer) Send(m *Hunk) error { - return x.ServerStream.SendMsg(m) -} - -func (x *gunServiceTunServer) Recv() (*Hunk, error) { - m := new(Hunk) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GunService_TunServer = grpc.BidiStreamingServer[Hunk, Hunk] // GunService_ServiceDesc is the grpc.ServiceDesc for GunService service. // It's only intended for direct use with grpc.RegisterService, From 6cdf99dc45f67be88acbf8e9d1a3b15b55e92f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 16 Feb 2025 11:49:24 +0800 Subject: [PATCH 30/94] Add back port hopping to hysteria 1 --- docs/configuration/outbound/hysteria.md | 29 ++++++++++++++++++++++ docs/configuration/outbound/hysteria.zh.md | 29 ++++++++++++++++++++++ option/hysteria.go | 26 +++++++++++-------- protocol/hysteria/outbound.go | 24 ++++++++++-------- 4 files changed, 86 insertions(+), 22 deletions(-) diff --git a/docs/configuration/outbound/hysteria.md b/docs/configuration/outbound/hysteria.md index 90190e05..b326e06d 100644 --- a/docs/configuration/outbound/hysteria.md +++ b/docs/configuration/outbound/hysteria.md @@ -1,3 +1,12 @@ +--- +icon: material/new-box +--- + +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [server_ports](#server_ports) + :material-plus: [hop_interval](#hop_interval) + ### Structure ```json @@ -7,6 +16,10 @@ "server": "127.0.0.1", "server_port": 1080, + "server_ports": [ + "2080:3000" + ], + "hop_interval": "", "up": "100 Mbps", "up_mbps": 100, "down": "100 Mbps", @@ -38,6 +51,22 @@ The server address. The server port. +#### server_ports + +!!! question "Since sing-box 1.12.0" + +Server port range list. + +Conflicts with `server_port`. + +#### hop_interval + +!!! question "Since sing-box 1.12.0" + +Port hopping interval. + +`30s` is used by default. + #### up, down ==Required== diff --git a/docs/configuration/outbound/hysteria.zh.md b/docs/configuration/outbound/hysteria.zh.md index c6ee2313..4f4c00bb 100644 --- a/docs/configuration/outbound/hysteria.zh.md +++ b/docs/configuration/outbound/hysteria.zh.md @@ -1,3 +1,12 @@ +--- +icon: material/new-box +--- + +!!! quote "sing-box 1.12.0 中的更改" + + :material-plus: [server_ports](#server_ports) + :material-plus: [hop_interval](#hop_interval) + ### 结构 ```json @@ -7,6 +16,10 @@ "server": "127.0.0.1", "server_port": 1080, + "server_ports": [ + "2080:3000" + ], + "hop_interval": "", "up": "100 Mbps", "up_mbps": 100, "down": "100 Mbps", @@ -38,6 +51,22 @@ 服务器端口。 +#### server_ports + +!!! question "自 sing-box 1.12.0 起" + +服务器端口范围列表。 + +与 `server_port` 冲突。 + +#### hop_interval + +!!! question "自 sing-box 1.12.0 起" + +端口跳跃间隔。 + +默认使用 `30s`。 + #### up, down ==必填== diff --git a/option/hysteria.go b/option/hysteria.go index beb3685e..c3dc78ef 100644 --- a/option/hysteria.go +++ b/option/hysteria.go @@ -1,5 +1,7 @@ package option +import "github.com/sagernet/sing/common/json/badoption" + type HysteriaInboundOptions struct { ListenOptions Up string `json:"up,omitempty"` @@ -24,16 +26,18 @@ type HysteriaUser struct { type HysteriaOutboundOptions struct { DialerOptions ServerOptions - Up string `json:"up,omitempty"` - UpMbps int `json:"up_mbps,omitempty"` - Down string `json:"down,omitempty"` - DownMbps int `json:"down_mbps,omitempty"` - Obfs string `json:"obfs,omitempty"` - Auth []byte `json:"auth,omitempty"` - AuthString string `json:"auth_str,omitempty"` - ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"` - ReceiveWindow uint64 `json:"recv_window,omitempty"` - DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"` - Network NetworkList `json:"network,omitempty"` + ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"` + HopInterval badoption.Duration `json:"hop_interval,omitempty"` + Up string `json:"up,omitempty"` + UpMbps int `json:"up_mbps,omitempty"` + Down string `json:"down,omitempty"` + DownMbps int `json:"down_mbps,omitempty"` + Obfs string `json:"obfs,omitempty"` + Auth []byte `json:"auth,omitempty"` + AuthString string `json:"auth_str,omitempty"` + ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"` + ReceiveWindow uint64 `json:"recv_window,omitempty"` + DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"` + Network NetworkList `json:"network,omitempty"` OutboundTLSOptionsContainer } diff --git a/protocol/hysteria/outbound.go b/protocol/hysteria/outbound.go index 7746df13..d3035c71 100644 --- a/protocol/hysteria/outbound.go +++ b/protocol/hysteria/outbound.go @@ -4,6 +4,7 @@ import ( "context" "net" "os" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter/outbound" @@ -76,17 +77,18 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps } client, err := hysteria.NewClient(hysteria.ClientOptions{ - Context: ctx, - Dialer: outboundDialer, - Logger: logger, - ServerAddress: options.ServerOptions.Build(), - SendBPS: sendBps, - ReceiveBPS: receiveBps, - XPlusPassword: options.Obfs, - Password: password, - TLSConfig: tlsConfig, - UDPDisabled: !common.Contains(networkList, N.NetworkUDP), - + Context: ctx, + Dialer: outboundDialer, + Logger: logger, + ServerAddress: options.ServerOptions.Build(), + ServerPorts: options.ServerPorts, + HopInterval: time.Duration(options.HopInterval), + SendBPS: sendBps, + ReceiveBPS: receiveBps, + XPlusPassword: options.Obfs, + Password: password, + TLSConfig: tlsConfig, + UDPDisabled: !common.Contains(networkList, N.NetworkUDP), ConnReceiveWindow: options.ReceiveWindowConn, StreamReceiveWindow: options.ReceiveWindow, DisableMTUDiscovery: options.DisableMTUDiscovery, From dfc63984422acc2e280ea10f0d9f540a1a0959af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 18 Feb 2025 19:13:25 +0800 Subject: [PATCH 31/94] Fix toolchain version --- .goreleaser.yaml | 2 ++ Makefile | 7 +++++-- go.mod | 2 -- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 858f4ca0..86090bb7 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -24,6 +24,7 @@ builds: - with_tailscale env: - CGO_ENABLED=0 + - GOTOOLCHAIN=local targets: - linux_386 - linux_amd64_v1 @@ -62,6 +63,7 @@ builds: <<: *template env: - CGO_ENABLED=1 + - GOTOOLCHAIN=local overrides: - goos: android goarch: arm diff --git a/Makefile b/Makefile index b4a262b6..0a9e78b9 100644 --- a/Makefile +++ b/Makefile @@ -18,14 +18,17 @@ PREFIX ?= $(shell go env GOPATH) .PHONY: test release docs build build: + export GOTOOLCHAIN=local && \ go build $(MAIN_PARAMS) $(MAIN) ci_build_go120: - go build $(PARAMS) $(MAIN) + export GOTOOLCHAIN=local && \ + go build $(PARAMS) $(MAIN) && \ go build $(PARAMS) -tags "$(TAGS_GO120)" $(MAIN) ci_build: - go build $(PARAMS) $(MAIN) + export GOTOOLCHAIN=local && \ + go build $(PARAMS) $(MAIN) && \ go build $(MAIN_PARAMS) $(MAIN) generate_completions: diff --git a/go.mod b/go.mod index 8ae859f8..1b94f561 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,6 @@ module github.com/sagernet/sing-box go 1.23.1 -toolchain go1.24.0 - require ( github.com/caddyserver/certmagic v0.21.7 github.com/cloudflare/circl v1.6.0 From aa916045229649d9ec87516ee1b3972402ac00ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 20 Feb 2025 15:13:14 +0800 Subject: [PATCH 32/94] Improve resolve action --- docs/configuration/dns/rule_action.md | 4 +-- docs/configuration/dns/rule_action.zh.md | 4 +-- docs/configuration/route/rule_action.md | 36 ++++++++++++++++--- docs/configuration/route/rule_action.zh.md | 31 +++++++++++++++-- option/rule_action.go | 7 ++-- route/route.go | 7 ++-- route/rule/rule_action.go | 40 ++++++++++++++++------ 7 files changed, 103 insertions(+), 26 deletions(-) diff --git a/docs/configuration/dns/rule_action.md b/docs/configuration/dns/rule_action.md index c3f8c2cb..33c283fa 100644 --- a/docs/configuration/dns/rule_action.md +++ b/docs/configuration/dns/rule_action.md @@ -16,7 +16,7 @@ icon: material/new-box "server": "", "strategy": "", "disable_cache": false, - "rewrite_ttl": 0, + "rewrite_ttl": null, "client_subnet": null } ``` @@ -49,7 +49,7 @@ Append a `edns0-subnet` OPT extra record with the specified IP prefix to every q If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically. -Will overrides `dns.client_subnet` and `servers.[].client_subnet`. +Will overrides `dns.client_subnet`. ### route-options diff --git a/docs/configuration/dns/rule_action.zh.md b/docs/configuration/dns/rule_action.zh.md index 427f8a8d..03843ec5 100644 --- a/docs/configuration/dns/rule_action.zh.md +++ b/docs/configuration/dns/rule_action.zh.md @@ -17,7 +17,7 @@ icon: material/new-box "strategy": "", "disable_cache": false, - "rewrite_ttl": 0, + "rewrite_ttl": null, "client_subnet": null } ``` @@ -50,7 +50,7 @@ icon: material/new-box 如果值是 IP 地址而不是前缀,则会自动附加 `/32` 或 `/128`。 -将覆盖 `dns.client_subnet` 与 `servers.[].client_subnet`。 +将覆盖 `dns.client_subnet`. ### route-options diff --git a/docs/configuration/route/rule_action.md b/docs/configuration/route/rule_action.md index 567b9eb6..0f04b165 100644 --- a/docs/configuration/route/rule_action.md +++ b/docs/configuration/route/rule_action.md @@ -5,7 +5,10 @@ icon: material/new-box !!! quote "Changes in sing-box 1.12.0" :material-plus: [tls_fragment](#tls_fragment) - :material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay) + :material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay) + :material-plus: [resolve.disable_cache](#disable_cache) + :material-plus: [resolve.rewrite_ttl](#rewrite_ttl) + :material-plus: [resolve.client_subnet](#client_subnet) ## Final actions @@ -210,19 +213,44 @@ Timeout for sniffing. ```json { "action": "resolve", + "server": "", "strategy": "", - "server": "" + "disable_cache": false, + "rewrite_ttl": null, + "client_subnet": null } ``` `resolve` resolve request destination from domain to IP addresses. +#### server + +Specifies DNS server tag to use instead of selecting through DNS routing. + #### strategy DNS resolution strategy, available values are: `prefer_ipv4`, `prefer_ipv6`, `ipv4_only`, `ipv6_only`. `dns.strategy` will be used by default. -#### server +#### disable_cache -Specifies DNS server tag to use instead of selecting through DNS routing. +!!! question "Since sing-box 1.12.0" + +Disable cache and save cache in this query. + +#### rewrite_ttl + +!!! question "Since sing-box 1.12.0" + +Rewrite TTL in DNS responses. + +#### client_subnet + +!!! question "Since sing-box 1.12.0" + +Append a `edns0-subnet` OPT extra record with the specified IP prefix to every query by default. + +If value is an IP address instead of prefix, `/32` or `/128` will be appended automatically. + +Will overrides `dns.client_subnet`. diff --git a/docs/configuration/route/rule_action.zh.md b/docs/configuration/route/rule_action.zh.md index a8eca8a2..f0c91610 100644 --- a/docs/configuration/route/rule_action.zh.md +++ b/docs/configuration/route/rule_action.zh.md @@ -206,19 +206,44 @@ UDP 连接超时时间。 ```json { "action": "resolve", + "server": "", "strategy": "", - "server": "" + "disable_cache": false, + "rewrite_ttl": null, + "client_subnet": null } ``` `resolve` 将请求的目标从域名解析为 IP 地址。 +#### server + +指定要使用的 DNS 服务器的标签,而不是通过 DNS 路由进行选择。 + #### strategy DNS 解析策略,可用值有:`prefer_ipv4`、`prefer_ipv6`、`ipv4_only`、`ipv6_only`。 默认使用 `dns.strategy`。 -#### server +#### disable_cache -指定要使用的 DNS 服务器的标签,而不是通过 DNS 路由进行选择。 +!!! question "自 sing-box 1.12.0 起" + +在此查询中禁用缓存。 + +#### rewrite_ttl + +!!! question "自 sing-box 1.12.0 起" + +重写 DNS 回应中的 TTL。 + +#### client_subnet + +!!! question "自 sing-box 1.12.0 起" + +默认情况下,将带有指定 IP 前缀的 `edns0-subnet` OPT 附加记录附加到每个查询。 + +如果值是 IP 地址而不是前缀,则会自动附加 `/32` 或 `/128`。 + +将覆盖 `dns.client_subnet`. diff --git a/option/rule_action.go b/option/rule_action.go index f07d7298..00d3ae7a 100644 --- a/option/rule_action.go +++ b/option/rule_action.go @@ -288,6 +288,9 @@ type RouteActionSniff struct { } type RouteActionResolve struct { - Strategy DomainStrategy `json:"strategy,omitempty"` - Server string `json:"server,omitempty"` + Server string `json:"server,omitempty"` + Strategy DomainStrategy `json:"strategy,omitempty"` + DisableCache bool `json:"disable_cache,omitempty"` + RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` + ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` } diff --git a/route/route.go b/route/route.go index ae78fcec..81b90fee 100644 --- a/route/route.go +++ b/route/route.go @@ -657,8 +657,11 @@ func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundCon } } addresses, err := r.dns.Lookup(adapter.WithContext(ctx, metadata), metadata.Destination.Fqdn, adapter.DNSQueryOptions{ - Transport: transport, - Strategy: action.Strategy, + Transport: transport, + Strategy: action.Strategy, + DisableCache: action.DisableCache, + RewriteTTL: action.RewriteTTL, + ClientSubnet: action.ClientSubnet, }) if err != nil { return err diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index 1cb3089d..d76f0a90 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -88,8 +88,11 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti return sniffAction, sniffAction.build() case C.RuleActionTypeResolve: return &RuleActionResolve{ - Strategy: C.DomainStrategy(action.ResolveOptions.Strategy), - Server: action.ResolveOptions.Server, + Server: action.ResolveOptions.Server, + Strategy: C.DomainStrategy(action.ResolveOptions.Strategy), + DisableCache: action.ResolveOptions.DisableCache, + RewriteTTL: action.ResolveOptions.RewriteTTL, + ClientSubnet: action.ResolveOptions.ClientSubnet.Build(netip.Prefix{}), }, nil default: panic(F.ToString("unknown rule action: ", action.Action)) @@ -379,8 +382,11 @@ func (r *RuleActionSniff) String() string { } type RuleActionResolve struct { - Strategy C.DomainStrategy - Server string + Server string + Strategy C.DomainStrategy + DisableCache bool + RewriteTTL *uint32 + ClientSubnet netip.Prefix } func (r *RuleActionResolve) Type() string { @@ -388,13 +394,25 @@ func (r *RuleActionResolve) Type() string { } func (r *RuleActionResolve) String() string { - if r.Strategy == C.DomainStrategyAsIS && r.Server == "" { - return F.ToString("resolve") - } else if r.Strategy != C.DomainStrategyAsIS && r.Server == "" { - return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ")") - } else if r.Strategy == C.DomainStrategyAsIS && r.Server != "" { - return F.ToString("resolve(", r.Server, ")") + var options []string + if r.Server != "" { + options = append(options, r.Server) + } + if r.Strategy != C.DomainStrategyAsIS { + options = append(options, F.ToString(option.DomainStrategy(r.Strategy))) + } + if r.DisableCache { + options = append(options, "disable_cache") + } + if r.RewriteTTL != nil { + options = append(options, F.ToString("rewrite_ttl=", *r.RewriteTTL)) + } + if r.ClientSubnet.IsValid() { + options = append(options, F.ToString("client_subnet=", r.ClientSubnet)) + } + if len(options) == 0 { + return "resolve" } else { - return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ",", r.Server, ")") + return F.ToString("resolve(", strings.Join(options, ","), ")") } } From 42fd9b50cf6eab44eb4cec098ad4bd5bb49654c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 20 Feb 2025 16:40:53 +0800 Subject: [PATCH 33/94] Get darwin local DNS server from libresolv --- dns/transport/local/local.go | 1 + dns/transport/local/resolv_darwin_cgo.go | 53 ++++++++++++++++++++++++ dns/transport/local/resolv_default.go | 23 ++++++++++ dns/transport/local/resolv_unix.go | 30 +++----------- dns/transport_manager.go | 4 +- protocol/tun/hook.go | 3 ++ protocol/tun/inbound.go | 3 ++ 7 files changed, 90 insertions(+), 27 deletions(-) create mode 100644 dns/transport/local/resolv_darwin_cgo.go create mode 100644 dns/transport/local/resolv_default.go create mode 100644 protocol/tun/hook.go diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index e5e8aef9..7f02c608 100644 --- a/dns/transport/local/local.go +++ b/dns/transport/local/local.go @@ -27,6 +27,7 @@ var _ adapter.DNSTransport = (*Transport)(nil) type Transport struct { dns.TransportAdapter + ctx context.Context hosts *hosts.File dialer N.Dialer } diff --git a/dns/transport/local/resolv_darwin_cgo.go b/dns/transport/local/resolv_darwin_cgo.go new file mode 100644 index 00000000..dfcb8a8a --- /dev/null +++ b/dns/transport/local/resolv_darwin_cgo.go @@ -0,0 +1,53 @@ +//go:build darwin && cgo + +package local + +/* +#include +#include +#include +#include +*/ +import "C" + +import ( + "time" + + E "github.com/sagernet/sing/common/exceptions" + + "github.com/miekg/dns" +) + +func dnsReadConfig(_ string) *dnsConfig { + if C.res_init() != 0 { + return &dnsConfig{ + servers: defaultNS, + search: dnsDefaultSearch(), + ndots: 1, + timeout: 5 * time.Second, + attempts: 2, + err: E.New("libresolv initialization failed"), + } + } + conf := &dnsConfig{ + ndots: 1, + timeout: 5 * time.Second, + attempts: int(C._res.retry), + } + for i := 0; i < int(C._res.nscount); i++ { + ns := C._res.nsaddr_list[i] + addr := C.inet_ntoa(ns.sin_addr) + if addr == nil { + continue + } + conf.servers = append(conf.servers, C.GoString(addr)) + } + for i := 0; ; i++ { + search := C._res.dnsrch[i] + if search == nil { + break + } + conf.search = append(conf.search, dns.Fqdn(C.GoString(search))) + } + return conf +} diff --git a/dns/transport/local/resolv_default.go b/dns/transport/local/resolv_default.go new file mode 100644 index 00000000..0a7d8810 --- /dev/null +++ b/dns/transport/local/resolv_default.go @@ -0,0 +1,23 @@ +package local + +import ( + "os" + "strings" + _ "unsafe" + + "github.com/miekg/dns" +) + +//go:linkname defaultNS net.defaultNS +var defaultNS []string + +func dnsDefaultSearch() []string { + hn, err := os.Hostname() + if err != nil { + return nil + } + if i := strings.IndexRune(hn, '.'); i >= 0 && i < len(hn)-1 { + return []string{dns.Fqdn(hn[i+1:])} + } + return nil +} diff --git a/dns/transport/local/resolv_unix.go b/dns/transport/local/resolv_unix.go index 6594ae41..c3953145 100644 --- a/dns/transport/local/resolv_unix.go +++ b/dns/transport/local/resolv_unix.go @@ -1,4 +1,4 @@ -//go:build !windows +//go:build !windows && !(darwin && cgo) package local @@ -9,7 +9,8 @@ import ( "os" "strings" "time" - _ "unsafe" + + "github.com/miekg/dns" ) func dnsReadConfig(name string) *dnsConfig { @@ -69,13 +70,13 @@ func dnsReadConfig(name string) *dnsConfig { } case "domain": if len(f) > 1 { - conf.search = []string{ensureRooted(f[1])} + conf.search = []string{dns.Fqdn(f[1])} } case "search": conf.search = make([]string, 0, len(f)-1) for i := 1; i < len(f); i++ { - name := ensureRooted(f[i]) + name := dns.Fqdn(f[i]) if name == "." { continue } @@ -137,27 +138,6 @@ func dnsReadConfig(name string) *dnsConfig { return conf } -//go:linkname defaultNS net.defaultNS -var defaultNS []string - -func dnsDefaultSearch() []string { - hn, err := os.Hostname() - if err != nil { - return nil - } - if i := strings.IndexRune(hn, '.'); i >= 0 && i < len(hn)-1 { - return []string{ensureRooted(hn[i+1:])} - } - return nil -} - -func ensureRooted(s string) string { - if len(s) > 0 && s[len(s)-1] == '.' { - return s - } - return s + "." -} - const big = 0xFFFFFF func dtoi(s string) (n int, i int, ok bool) { diff --git a/dns/transport_manager.go b/dns/transport_manager.go index 4497923b..8666a1b9 100644 --- a/dns/transport_manager.go +++ b/dns/transport_manager.go @@ -56,12 +56,12 @@ func (m *TransportManager) Start(stage adapter.StartStage) error { } m.started = true m.stage = stage - outbounds := m.transports + transports := m.transports m.access.Unlock() if stage == adapter.StartStateStart { return m.startTransports(m.transports) } else { - for _, outbound := range outbounds { + for _, outbound := range transports { err := adapter.LegacyStart(outbound, stage) if err != nil { return E.Cause(err, stage, " dns/", outbound.Type(), "[", outbound.Tag(), "]") diff --git a/protocol/tun/hook.go b/protocol/tun/hook.go new file mode 100644 index 00000000..1afa643f --- /dev/null +++ b/protocol/tun/hook.go @@ -0,0 +1,3 @@ +package tun + +var HookBeforeCreatePlatformInterface func() diff --git a/protocol/tun/inbound.go b/protocol/tun/inbound.go index 70f78c0a..05172af1 100644 --- a/protocol/tun/inbound.go +++ b/protocol/tun/inbound.go @@ -361,6 +361,9 @@ func (t *Inbound) Start(stage adapter.StartStage) error { if t.platformInterface != nil { tunInterface, err = t.platformInterface.OpenTun(&tunOptions, t.platformOptions) } else { + if HookBeforeCreatePlatformInterface != nil { + HookBeforeCreatePlatformInterface() + } tunInterface, err = tun.New(tunOptions) } monitor.Finish() From ead0b3d99a2aaff4da601458f9823e5914806d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 20 Feb 2025 17:18:47 +0800 Subject: [PATCH 34/94] Add fallback local DNS server for iOS --- common/tls/ech_client.go | 2 +- dns/client.go | 78 +++++++++- dns/rcode.go | 38 ++--- dns/router.go | 4 +- dns/transport/local/local.go | 4 - dns/transport/local/local_fallback.go | 201 ++++++++++++++++++++++++++ dns/transport/local/resolv_windows.go | 3 - dns/transport/predefined.go | 2 +- experimental/libbox/dns.go | 2 +- protocol/dns/handle.go | 2 +- protocol/tailscale/dns_transport.go | 2 +- 11 files changed, 293 insertions(+), 45 deletions(-) create mode 100644 dns/transport/local/local_fallback.go diff --git a/common/tls/ech_client.go b/common/tls/ech_client.go index 80219350..9c3744b4 100644 --- a/common/tls/ech_client.go +++ b/common/tls/ech_client.go @@ -221,7 +221,7 @@ func fetchECHClientConfig(ctx context.Context) func(_ context.Context, serverNam return nil, err } if response.Rcode != mDNS.RcodeSuccess { - return nil, dns.RCodeError(response.Rcode) + return nil, dns.RcodeError(response.Rcode) } for _, rr := range response.Answer { switch resource := rr.(type) { diff --git a/dns/client.go b/dns/client.go index 79b6fce5..d44883b5 100644 --- a/dns/client.go +++ b/dns/client.go @@ -17,7 +17,7 @@ import ( "github.com/sagernet/sing/contrab/freelru" "github.com/sagernet/sing/contrab/maphash" - "github.com/miekg/dns" + dns "github.com/miekg/dns" ) var ( @@ -484,7 +484,7 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp func MessageToAddresses(response *dns.Msg) ([]netip.Addr, error) { if response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError { - return nil, RCodeError(response.Rcode) + return nil, RcodeError(response.Rcode) } addresses := make([]netip.Addr, 0, len(response.Answer)) for _, rawAnswer := range response.Answer { @@ -508,10 +508,10 @@ func wrapError(err error) error { switch dnsErr := err.(type) { case *net.DNSError: if dnsErr.IsNotFound { - return RCodeNameError + return RcodeNameError } case *net.AddrError: - return RCodeNameError + return RcodeNameError } return err } @@ -561,3 +561,73 @@ func FixedResponse(id uint16, question dns.Question, addresses []netip.Addr, tim } return &response } + +func FixedResponseCNAME(id uint16, question dns.Question, record string, timeToLive uint32) *dns.Msg { + response := dns.Msg{ + MsgHdr: dns.MsgHdr{ + Id: id, + Rcode: dns.RcodeSuccess, + Response: true, + }, + Question: []dns.Question{question}, + Answer: []dns.RR{ + &dns.CNAME{ + Hdr: dns.RR_Header{ + Name: question.Name, + Rrtype: dns.TypeCNAME, + Class: dns.ClassINET, + Ttl: timeToLive, + }, + Target: record, + }, + }, + } + return &response +} + +func FixedResponseTXT(id uint16, question dns.Question, records []string, timeToLive uint32) *dns.Msg { + response := dns.Msg{ + MsgHdr: dns.MsgHdr{ + Id: id, + Rcode: dns.RcodeSuccess, + Response: true, + }, + Question: []dns.Question{question}, + Answer: []dns.RR{ + &dns.TXT{ + Hdr: dns.RR_Header{ + Name: question.Name, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: timeToLive, + }, + Txt: records, + }, + }, + } + return &response +} + +func FixedResponseMX(id uint16, question dns.Question, records []*net.MX, timeToLive uint32) *dns.Msg { + response := dns.Msg{ + MsgHdr: dns.MsgHdr{ + Id: id, + Rcode: dns.RcodeSuccess, + Response: true, + }, + Question: []dns.Question{question}, + } + for _, record := range records { + response.Answer = append(response.Answer, &dns.MX{ + Hdr: dns.RR_Header{ + Name: question.Name, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: timeToLive, + }, + Preference: record.Pref, + Mx: record.Host, + }) + } + return &response +} diff --git a/dns/rcode.go b/dns/rcode.go index 5b7e52cc..08545474 100644 --- a/dns/rcode.go +++ b/dns/rcode.go @@ -1,33 +1,17 @@ package dns -import F "github.com/sagernet/sing/common/format" - -const ( - RCodeSuccess RCodeError = 0 // NoError - RCodeFormatError RCodeError = 1 // FormErr - RCodeServerFailure RCodeError = 2 // ServFail - RCodeNameError RCodeError = 3 // NXDomain - RCodeNotImplemented RCodeError = 4 // NotImp - RCodeRefused RCodeError = 5 // Refused +import ( + mDNS "github.com/miekg/dns" ) -type RCodeError uint16 +const ( + RcodeFormatError RcodeError = mDNS.RcodeFormatError + RcodeNameError RcodeError = mDNS.RcodeNameError + RcodeRefused RcodeError = mDNS.RcodeRefused +) -func (e RCodeError) Error() string { - switch e { - case RCodeSuccess: - return "success" - case RCodeFormatError: - return "format error" - case RCodeServerFailure: - return "server failure" - case RCodeNameError: - return "name error" - case RCodeNotImplemented: - return "not implemented" - case RCodeRefused: - return "refused" - default: - return F.ToString("unknown error: ", uint16(e)) - } +type RcodeError int + +func (e RcodeError) Error() string { + return mDNS.RcodeToString[int(e)] } diff --git a/dns/router.go b/dns/router.go index 4102128e..5db5429a 100644 --- a/dns/router.go +++ b/dns/router.go @@ -329,13 +329,13 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ } } else if len(responseAddrs) == 0 { r.logger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result") - err = RCodeNameError + err = RcodeNameError } } responseAddrs, cached = r.client.LookupCache(domain, options.Strategy) if cached { if len(responseAddrs) == 0 { - return nil, RCodeNameError + return nil, RcodeNameError } return responseAddrs, nil } diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index 7f02c608..e4236275 100644 --- a/dns/transport/local/local.go +++ b/dns/transport/local/local.go @@ -19,10 +19,6 @@ import ( mDNS "github.com/miekg/dns" ) -func RegisterTransport(registry *dns.TransportRegistry) { - dns.RegisterTransport[option.LocalDNSServerOptions](registry, C.DNSTypeLocal, NewTransport) -} - var _ adapter.DNSTransport = (*Transport)(nil) type Transport struct { diff --git a/dns/transport/local/local_fallback.go b/dns/transport/local/local_fallback.go new file mode 100644 index 00000000..0a7cd9f0 --- /dev/null +++ b/dns/transport/local/local_fallback.go @@ -0,0 +1,201 @@ +package local + +import ( + "context" + "errors" + "net" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/experimental/libbox/platform" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/service" + + mDNS "github.com/miekg/dns" +) + +func RegisterTransport(registry *dns.TransportRegistry) { + dns.RegisterTransport[option.LocalDNSServerOptions](registry, C.DNSTypeLocal, NewFallbackTransport) +} + +type FallbackTransport struct { + adapter.DNSTransport + ctx context.Context + fallback bool + resolver net.Resolver +} + +func NewFallbackTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.LocalDNSServerOptions) (adapter.DNSTransport, error) { + transport, err := NewTransport(ctx, logger, tag, options) + if err != nil { + return nil, err + } + return &FallbackTransport{ + DNSTransport: transport, + ctx: ctx, + }, nil +} + +func (f *FallbackTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + platformInterface := service.FromContext[platform.Interface](f.ctx) + if platformInterface == nil { + return nil + } + inboundManager := service.FromContext[adapter.InboundManager](f.ctx) + for _, inbound := range inboundManager.Inbounds() { + if inbound.Type() == C.TypeTun { + // platform tun hijacks DNS, so we can only use cgo resolver here + f.fallback = true + break + } + } + return nil +} + +func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { + if f.fallback { + return f.DNSTransport.Exchange(ctx, message) + } + question := message.Question[0] + domain := dns.FqdnToDomain(question.Name) + if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA { + var network string + if question.Qtype == mDNS.TypeA { + network = "ip4" + } else { + network = "ip6" + } + addresses, err := f.resolver.LookupNetIP(ctx, network, domain) + if err != nil { + var dnsError *net.DNSError + if errors.As(err, &dnsError) && dnsError.IsNotFound { + return nil, dns.RcodeRefused + } + return nil, err + } + return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil + } else if question.Qtype == mDNS.TypeNS { + records, err := f.resolver.LookupNS(ctx, domain) + if err != nil { + var dnsError *net.DNSError + if errors.As(err, &dnsError) && dnsError.IsNotFound { + return nil, dns.RcodeRefused + } + return nil, err + } + response := &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: message.Id, + Rcode: mDNS.RcodeSuccess, + Response: true, + }, + Question: []mDNS.Question{question}, + } + for _, record := range records { + response.Answer = append(response.Answer, &mDNS.NS{ + Hdr: mDNS.RR_Header{ + Name: question.Name, + Rrtype: mDNS.TypeNS, + Class: mDNS.ClassINET, + Ttl: C.DefaultDNSTTL, + }, + Ns: record.Host, + }) + } + return response, nil + } else if question.Qtype == mDNS.TypeCNAME { + cname, err := f.resolver.LookupCNAME(ctx, domain) + if err != nil { + var dnsError *net.DNSError + if errors.As(err, &dnsError) && dnsError.IsNotFound { + return nil, dns.RcodeRefused + } + return nil, err + } + return &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: message.Id, + Rcode: mDNS.RcodeSuccess, + Response: true, + }, + Question: []mDNS.Question{question}, + Answer: []mDNS.RR{ + &mDNS.CNAME{ + Hdr: mDNS.RR_Header{ + Name: question.Name, + Rrtype: mDNS.TypeCNAME, + Class: mDNS.ClassINET, + Ttl: C.DefaultDNSTTL, + }, + Target: cname, + }, + }, + }, nil + } else if question.Qtype == mDNS.TypeTXT { + records, err := f.resolver.LookupTXT(ctx, domain) + if err != nil { + var dnsError *net.DNSError + if errors.As(err, &dnsError) && dnsError.IsNotFound { + return nil, dns.RcodeRefused + } + return nil, err + } + return &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: message.Id, + Rcode: mDNS.RcodeSuccess, + Response: true, + }, + Question: []mDNS.Question{question}, + Answer: []mDNS.RR{ + &mDNS.TXT{ + Hdr: mDNS.RR_Header{ + Name: question.Name, + Rrtype: mDNS.TypeCNAME, + Class: mDNS.ClassINET, + Ttl: C.DefaultDNSTTL, + }, + Txt: records, + }, + }, + }, nil + } else if question.Qtype == mDNS.TypeMX { + records, err := f.resolver.LookupMX(ctx, domain) + if err != nil { + var dnsError *net.DNSError + if errors.As(err, &dnsError) && dnsError.IsNotFound { + return nil, dns.RcodeRefused + } + return nil, err + } + response := &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: message.Id, + Rcode: mDNS.RcodeSuccess, + Response: true, + }, + Question: []mDNS.Question{question}, + } + for _, record := range records { + response.Answer = append(response.Answer, &mDNS.MX{ + Hdr: mDNS.RR_Header{ + Name: question.Name, + Rrtype: mDNS.TypeA, + Class: mDNS.ClassINET, + Ttl: C.DefaultDNSTTL, + }, + Preference: record.Pref, + Mx: record.Host, + }) + } + return response, nil + } else { + return nil, E.New("only A, AAAA, NS, CNAME, TXT, MX queries are supported on current platform when using TUN, please switch to a fixed DNS server.") + } +} diff --git a/dns/transport/local/resolv_windows.go b/dns/transport/local/resolv_windows.go index 577e7a12..f6b81090 100644 --- a/dns/transport/local/resolv_windows.go +++ b/dns/transport/local/resolv_windows.go @@ -69,9 +69,6 @@ func dnsReadConfig(_ string) *dnsConfig { return conf } -//go:linkname defaultNS net.defaultNS -var defaultNS []string - func adapterAddresses() ([]*windows.IpAdapterAddresses, error) { var b []byte l := uint32(15000) // recommended initial size diff --git a/dns/transport/predefined.go b/dns/transport/predefined.go index 3f112886..dbb78e5c 100644 --- a/dns/transport/predefined.go +++ b/dns/transport/predefined.go @@ -79,5 +79,5 @@ func (t *PredefinedTransport) Exchange(ctx context.Context, message *mDNS.Msg) ( } } } - return nil, dns.RCodeNameError + return nil, dns.RcodeNameError } diff --git a/experimental/libbox/dns.go b/experimental/libbox/dns.go index a7ccd2a2..7e143442 100644 --- a/experimental/libbox/dns.go +++ b/experimental/libbox/dns.go @@ -134,7 +134,7 @@ func (c *ExchangeContext) RawSuccess(result []byte) { } func (c *ExchangeContext) ErrorCode(code int32) { - c.error = dns.RCodeError(code) + c.error = dns.RcodeError(code) } func (c *ExchangeContext) ErrnoCode(code int32) { diff --git a/protocol/dns/handle.go b/protocol/dns/handle.go index c4ad79d9..765e5051 100644 --- a/protocol/dns/handle.go +++ b/protocol/dns/handle.go @@ -26,7 +26,7 @@ func HandleStreamDNSRequest(ctx context.Context, router adapter.DNSRouter, conn return err } if queryLength == 0 { - return dns.RCodeFormatError + return dns.RcodeFormatError } buffer := buf.NewSize(int(queryLength)) defer buffer.Release() diff --git a/protocol/tailscale/dns_transport.go b/protocol/tailscale/dns_transport.go index 0c83c698..3447b6b2 100644 --- a/protocol/tailscale/dns_transport.go +++ b/protocol/tailscale/dns_transport.go @@ -287,7 +287,7 @@ func (t *DNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M return nil, E.New("missing default resolvers") } } - return nil, dns.RCodeNameError + return nil, dns.RcodeNameError } type DNSDialer struct { From cb048be805535075a1ab3ab95c44c43cca1de4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 22 Feb 2025 08:00:59 +0800 Subject: [PATCH 35/94] Migrate to stdlib ECH support --- .golangci.yml | 1 - .goreleaser.fury.yaml | 1 - .goreleaser.yaml | 1 - Dockerfile | 2 +- Makefile | 5 +- cmd/internal/build_libbox/main.go | 2 +- cmd/sing-box/cmd_generate_ech.go | 5 +- common/badtls/read_wait_ech.go | 31 --- common/tls/client.go | 7 +- common/tls/ech.go | 174 +++++++++++++ common/tls/ech_client.go | 244 ----------------- common/tls/ech_keygen.go | 19 +- common/tls/ech_quic.go | 55 ---- common/tls/ech_server.go | 278 -------------------- common/tls/ech_stub.go | 18 +- common/tls/mkcert.go | 6 +- common/tls/server.go | 7 +- common/tls/std_client.go | 3 + common/tls/std_server.go | 50 ++-- docs/configuration/shared/tls.md | 38 ++- docs/configuration/shared/tls.zh.md | 56 ++-- docs/deprecated.md | 11 + docs/deprecated.zh.md | 9 + docs/installation/build-from-source.md | 36 ++- docs/installation/build-from-source.zh.md | 6 +- experimental/deprecated/constants.go | 10 + experimental/deprecated/stderr.go | 16 +- go.mod | 1 - go.sum | 2 - option/tls.go | 26 +- test/box_test.go | 2 +- test/ech_test.go | 6 +- test/go.mod | 133 ++++++---- test/go.sum | 303 ++++++++++++++-------- 34 files changed, 667 insertions(+), 897 deletions(-) delete mode 100644 common/badtls/read_wait_ech.go create mode 100644 common/tls/ech.go delete mode 100644 common/tls/ech_client.go delete mode 100644 common/tls/ech_quic.go delete mode 100644 common/tls/ech_server.go diff --git a/.golangci.yml b/.golangci.yml index 1117e075..d212ebb2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -27,7 +27,6 @@ run: - with_quic - with_dhcp - with_wireguard - - with_ech - with_utls - with_reality_server - with_acme diff --git a/.goreleaser.fury.yaml b/.goreleaser.fury.yaml index 8f3835f4..c149b1f4 100644 --- a/.goreleaser.fury.yaml +++ b/.goreleaser.fury.yaml @@ -14,7 +14,6 @@ builds: - with_quic - with_dhcp - with_wireguard - - with_ech - with_utls - with_reality_server - with_acme diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 86090bb7..23e0771f 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -16,7 +16,6 @@ builds: - with_quic - with_dhcp - with_wireguard - - with_ech - with_utls - with_reality_server - with_acme diff --git a/Dockerfile b/Dockerfile index 6c3cfd39..c9b32b8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN set -ex \ && export COMMIT=$(git rev-parse --short HEAD) \ && export VERSION=$(go run ./cmd/internal/read_tag) \ && go build -v -trimpath -tags \ - "with_gvisor,with_quic,with_dhcp,with_wireguard,with_ech,with_utls,with_reality_server,with_acme,with_clash_api" \ + "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api" \ -o /go/bin/sing-box \ -ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid=" \ ./cmd/sing-box diff --git a/Makefile b/Makefile index 0a9e78b9..68e6ffa1 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,9 @@ NAME = sing-box COMMIT = $(shell git rev-parse --short HEAD) TAGS_GO120 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api,with_quic,with_utls -TAGS_GO121 = with_ech TAGS_GO123 = with_tailscale -TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121),$(TAGS_GO123) -TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server +TAGS ?= $(TAGS_GO120),$(TAGS_GO123) +TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_utls,with_reality_server GOHOSTOS = $(shell go env GOHOSTOS) GOHOSTARCH = $(shell go env GOHOSTARCH) diff --git a/cmd/internal/build_libbox/main.go b/cmd/internal/build_libbox/main.go index 56cf8688..cad3662b 100644 --- a/cmd/internal/build_libbox/main.go +++ b/cmd/internal/build_libbox/main.go @@ -58,7 +58,7 @@ func init() { sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=") debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag) - sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api", "with_tailscale") + sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_tailscale") iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack") debugTags = append(debugTags, "debug") } diff --git a/cmd/sing-box/cmd_generate_ech.go b/cmd/sing-box/cmd_generate_ech.go index 4b5b7be3..7942d588 100644 --- a/cmd/sing-box/cmd_generate_ech.go +++ b/cmd/sing-box/cmd_generate_ech.go @@ -9,8 +9,6 @@ import ( "github.com/spf13/cobra" ) -var pqSignatureSchemesEnabled bool - var commandGenerateECHKeyPair = &cobra.Command{ Use: "ech-keypair ", Short: "Generate TLS ECH key pair", @@ -24,12 +22,11 @@ var commandGenerateECHKeyPair = &cobra.Command{ } func init() { - commandGenerateECHKeyPair.Flags().BoolVar(&pqSignatureSchemesEnabled, "pq-signature-schemes-enabled", false, "Enable PQ signature schemes") commandGenerate.AddCommand(commandGenerateECHKeyPair) } func generateECHKeyPair(serverName string) error { - configPem, keyPem, err := tls.ECHKeygenDefault(serverName, pqSignatureSchemesEnabled) + configPem, keyPem, err := tls.ECHKeygenDefault(serverName) if err != nil { return err } diff --git a/common/badtls/read_wait_ech.go b/common/badtls/read_wait_ech.go deleted file mode 100644 index 6a0d5b5f..00000000 --- a/common/badtls/read_wait_ech.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build go1.21 && !without_badtls && with_ech - -package badtls - -import ( - "net" - _ "unsafe" - - "github.com/sagernet/cloudflare-tls" - "github.com/sagernet/sing/common" -) - -func init() { - tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) { - tlsConn, loaded := common.Cast[*tls.Conn](conn) - if !loaded { - return - } - return true, func() error { - return echReadRecord(tlsConn) - }, func() error { - return echHandlePostHandshakeMessage(tlsConn) - } - }) -} - -//go:linkname echReadRecord github.com/sagernet/cloudflare-tls.(*Conn).readRecord -func echReadRecord(c *tls.Conn) error - -//go:linkname echHandlePostHandshakeMessage github.com/sagernet/cloudflare-tls.(*Conn).handlePostHandshakeMessage -func echHandlePostHandshakeMessage(c *tls.Conn) error diff --git a/common/tls/client.go b/common/tls/client.go index 4d6b0c54..5e05c990 100644 --- a/common/tls/client.go +++ b/common/tls/client.go @@ -29,15 +29,12 @@ func NewClient(ctx context.Context, serverAddress string, options option.Outboun if !options.Enabled { return nil, nil } - if options.ECH != nil && options.ECH.Enabled { - return NewECHClient(ctx, serverAddress, options) - } else if options.Reality != nil && options.Reality.Enabled { + if options.Reality != nil && options.Reality.Enabled { return NewRealityClient(ctx, serverAddress, options) } else if options.UTLS != nil && options.UTLS.Enabled { return NewUTLSClient(ctx, serverAddress, options) - } else { - return NewSTDClient(ctx, serverAddress, options) } + return NewSTDClient(ctx, serverAddress, options) } func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) { diff --git a/common/tls/ech.go b/common/tls/ech.go new file mode 100644 index 00000000..880ca27c --- /dev/null +++ b/common/tls/ech.go @@ -0,0 +1,174 @@ +//go:build go1.24 + +package tls + +import ( + "context" + "crypto/tls" + "encoding/base64" + "encoding/pem" + "net" + "os" + "strings" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/dns" + "github.com/sagernet/sing-box/experimental/deprecated" + "github.com/sagernet/sing-box/option" + E "github.com/sagernet/sing/common/exceptions" + aTLS "github.com/sagernet/sing/common/tls" + "github.com/sagernet/sing/service" + + mDNS "github.com/miekg/dns" + "golang.org/x/crypto/cryptobyte" +) + +func parseECHClientConfig(ctx context.Context, options option.OutboundTLSOptions, tlsConfig *tls.Config) (Config, error) { + var echConfig []byte + if len(options.ECH.Config) > 0 { + echConfig = []byte(strings.Join(options.ECH.Config, "\n")) + } else if options.ECH.ConfigPath != "" { + content, err := os.ReadFile(options.ECH.ConfigPath) + if err != nil { + return nil, E.Cause(err, "read ECH config") + } + echConfig = content + } + //nolint:staticcheck + if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled { + deprecated.Report(ctx, deprecated.OptionLegacyECHOptions) + } + if len(echConfig) > 0 { + block, rest := pem.Decode(echConfig) + if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 { + return nil, E.New("invalid ECH configs pem") + } + tlsConfig.EncryptedClientHelloConfigList = block.Bytes + return &STDClientConfig{tlsConfig}, nil + } else { + return &STDECHClientConfig{STDClientConfig{tlsConfig}, service.FromContext[adapter.DNSRouter](ctx)}, nil + } +} + +func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions, tlsConfig *tls.Config, echKeyPath *string) error { + var echKey []byte + if len(options.ECH.Key) > 0 { + echKey = []byte(strings.Join(options.ECH.Key, "\n")) + } else if options.ECH.KeyPath != "" { + content, err := os.ReadFile(options.ECH.KeyPath) + if err != nil { + return E.Cause(err, "read ECH keys") + } + echKey = content + *echKeyPath = options.ECH.KeyPath + } else { + return E.New("missing ECH keys") + } + block, rest := pem.Decode(echKey) + if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 { + return E.New("invalid ECH keys pem") + } + echKeys, err := UnmarshalECHKeys(block.Bytes) + if err != nil { + return E.Cause(err, "parse ECH keys") + } + tlsConfig.EncryptedClientHelloKeys = echKeys + //nolint:staticcheck + if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled { + deprecated.Report(ctx, deprecated.OptionLegacyECHOptions) + } + return nil +} + +func reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error { + echKey, err := os.ReadFile(echKeyPath) + if err != nil { + return E.Cause(err, "reload ECH keys from ", echKeyPath) + } + block, _ := pem.Decode(echKey) + if block == nil || block.Type != "ECH KEYS" { + return E.New("invalid ECH keys pem") + } + echKeys, err := UnmarshalECHKeys(block.Bytes) + if err != nil { + return E.Cause(err, "parse ECH keys") + } + tlsConfig.EncryptedClientHelloKeys = echKeys + return nil +} + +type STDECHClientConfig struct { + STDClientConfig + dnsRouter adapter.DNSRouter +} + +func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) { + if len(s.config.EncryptedClientHelloConfigList) == 0 { + message := &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + RecursionDesired: true, + }, + Question: []mDNS.Question{ + { + Name: mDNS.Fqdn(s.config.ServerName), + Qtype: mDNS.TypeHTTPS, + Qclass: mDNS.ClassINET, + }, + }, + } + response, err := s.dnsRouter.Exchange(ctx, message, adapter.DNSQueryOptions{}) + if err != nil { + return nil, E.Cause(err, "fetch ECH config list") + } + if response.Rcode != mDNS.RcodeSuccess { + return nil, E.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list") + } + for _, rr := range response.Answer { + switch resource := rr.(type) { + case *mDNS.HTTPS: + for _, value := range resource.Value { + if value.Key().String() == "ech" { + echConfigList, err := base64.StdEncoding.DecodeString(value.String()) + if err != nil { + return nil, E.Cause(err, "decode ECH config") + } + s.config.EncryptedClientHelloConfigList = echConfigList + } + } + } + } + return nil, E.New("no ECH config found in DNS records") + } + tlsConn, err := s.Client(conn) + if err != nil { + return nil, err + } + err = tlsConn.HandshakeContext(ctx) + if err != nil { + return nil, err + } + return tlsConn, nil +} + +func (s *STDECHClientConfig) Clone() Config { + return &STDECHClientConfig{STDClientConfig{s.config.Clone()}, s.dnsRouter} +} + +func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) { + var keys []tls.EncryptedClientHelloKey + rawString := cryptobyte.String(raw) + for !rawString.Empty() { + var key tls.EncryptedClientHelloKey + if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) { + return nil, E.New("error parsing private key") + } + if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.Config)) { + return nil, E.New("error parsing config") + } + keys = append(keys, key) + } + if len(keys) == 0 { + return nil, E.New("empty ECH keys") + } + return keys, nil +} diff --git a/common/tls/ech_client.go b/common/tls/ech_client.go deleted file mode 100644 index 9c3744b4..00000000 --- a/common/tls/ech_client.go +++ /dev/null @@ -1,244 +0,0 @@ -//go:build with_ech - -package tls - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "net" - "net/netip" - "os" - "strings" - - cftls "github.com/sagernet/cloudflare-tls" - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/dns" - "github.com/sagernet/sing-box/option" - E "github.com/sagernet/sing/common/exceptions" - "github.com/sagernet/sing/common/ntp" - "github.com/sagernet/sing/service" - - mDNS "github.com/miekg/dns" -) - -type echClientConfig struct { - config *cftls.Config -} - -func (c *echClientConfig) ServerName() string { - return c.config.ServerName -} - -func (c *echClientConfig) SetServerName(serverName string) { - c.config.ServerName = serverName -} - -func (c *echClientConfig) NextProtos() []string { - return c.config.NextProtos -} - -func (c *echClientConfig) SetNextProtos(nextProto []string) { - c.config.NextProtos = nextProto -} - -func (c *echClientConfig) Config() (*STDConfig, error) { - return nil, E.New("unsupported usage for ECH") -} - -func (c *echClientConfig) Client(conn net.Conn) (Conn, error) { - return &echConnWrapper{cftls.Client(conn, c.config)}, nil -} - -func (c *echClientConfig) Clone() Config { - return &echClientConfig{ - config: c.config.Clone(), - } -} - -type echConnWrapper struct { - *cftls.Conn -} - -func (c *echConnWrapper) ConnectionState() tls.ConnectionState { - state := c.Conn.ConnectionState() - //nolint:staticcheck - return tls.ConnectionState{ - Version: state.Version, - HandshakeComplete: state.HandshakeComplete, - DidResume: state.DidResume, - CipherSuite: state.CipherSuite, - NegotiatedProtocol: state.NegotiatedProtocol, - NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual, - ServerName: state.ServerName, - PeerCertificates: state.PeerCertificates, - VerifiedChains: state.VerifiedChains, - SignedCertificateTimestamps: state.SignedCertificateTimestamps, - OCSPResponse: state.OCSPResponse, - TLSUnique: state.TLSUnique, - } -} - -func (c *echConnWrapper) Upstream() any { - return c.Conn -} - -func NewECHClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) { - var serverName string - if options.ServerName != "" { - serverName = options.ServerName - } else if serverAddress != "" { - if _, err := netip.ParseAddr(serverName); err != nil { - serverName = serverAddress - } - } - if serverName == "" && !options.Insecure { - return nil, E.New("missing server_name or insecure=true") - } - - var tlsConfig cftls.Config - tlsConfig.Time = ntp.TimeFuncFromContext(ctx) - tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx) - if options.DisableSNI { - tlsConfig.ServerName = "127.0.0.1" - } else { - tlsConfig.ServerName = serverName - } - if options.Insecure { - tlsConfig.InsecureSkipVerify = options.Insecure - } else if options.DisableSNI { - tlsConfig.InsecureSkipVerify = true - tlsConfig.VerifyConnection = func(state cftls.ConnectionState) error { - verifyOptions := x509.VerifyOptions{ - DNSName: serverName, - Intermediates: x509.NewCertPool(), - } - for _, cert := range state.PeerCertificates[1:] { - verifyOptions.Intermediates.AddCert(cert) - } - _, err := state.PeerCertificates[0].Verify(verifyOptions) - return err - } - } - if len(options.ALPN) > 0 { - tlsConfig.NextProtos = options.ALPN - } - if options.MinVersion != "" { - minVersion, err := ParseTLSVersion(options.MinVersion) - if err != nil { - return nil, E.Cause(err, "parse min_version") - } - tlsConfig.MinVersion = minVersion - } - if options.MaxVersion != "" { - maxVersion, err := ParseTLSVersion(options.MaxVersion) - if err != nil { - return nil, E.Cause(err, "parse max_version") - } - tlsConfig.MaxVersion = maxVersion - } - if options.CipherSuites != nil { - find: - for _, cipherSuite := range options.CipherSuites { - for _, tlsCipherSuite := range cftls.CipherSuites() { - if cipherSuite == tlsCipherSuite.Name { - tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID) - continue find - } - } - return nil, E.New("unknown cipher_suite: ", cipherSuite) - } - } - var certificate []byte - if len(options.Certificate) > 0 { - certificate = []byte(strings.Join(options.Certificate, "\n")) - } else if options.CertificatePath != "" { - content, err := os.ReadFile(options.CertificatePath) - if err != nil { - return nil, E.Cause(err, "read certificate") - } - certificate = content - } - if len(certificate) > 0 { - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM(certificate) { - return nil, E.New("failed to parse certificate:\n\n", certificate) - } - tlsConfig.RootCAs = certPool - } - - // ECH Config - - tlsConfig.ECHEnabled = true - tlsConfig.PQSignatureSchemesEnabled = options.ECH.PQSignatureSchemesEnabled - tlsConfig.DynamicRecordSizingDisabled = options.ECH.DynamicRecordSizingDisabled - - var echConfig []byte - if len(options.ECH.Config) > 0 { - echConfig = []byte(strings.Join(options.ECH.Config, "\n")) - } else if options.ECH.ConfigPath != "" { - content, err := os.ReadFile(options.ECH.ConfigPath) - if err != nil { - return nil, E.Cause(err, "read ECH config") - } - echConfig = content - } - - if len(echConfig) > 0 { - block, rest := pem.Decode(echConfig) - if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 { - return nil, E.New("invalid ECH configs pem") - } - echConfigs, err := cftls.UnmarshalECHConfigs(block.Bytes) - if err != nil { - return nil, E.Cause(err, "parse ECH configs") - } - tlsConfig.ClientECHConfigs = echConfigs - } else { - tlsConfig.GetClientECHConfigs = fetchECHClientConfig(ctx) - } - return &echClientConfig{&tlsConfig}, nil -} - -func fetchECHClientConfig(ctx context.Context) func(_ context.Context, serverName string) ([]cftls.ECHConfig, error) { - return func(_ context.Context, serverName string) ([]cftls.ECHConfig, error) { - message := &mDNS.Msg{ - MsgHdr: mDNS.MsgHdr{ - RecursionDesired: true, - }, - Question: []mDNS.Question{ - { - Name: serverName + ".", - Qtype: mDNS.TypeHTTPS, - Qclass: mDNS.ClassINET, - }, - }, - } - response, err := service.FromContext[adapter.DNSRouter](ctx).Exchange(ctx, message, adapter.DNSQueryOptions{}) - if err != nil { - return nil, err - } - if response.Rcode != mDNS.RcodeSuccess { - return nil, dns.RcodeError(response.Rcode) - } - for _, rr := range response.Answer { - switch resource := rr.(type) { - case *mDNS.HTTPS: - for _, value := range resource.Value { - if value.Key().String() == "ech" { - echConfig, err := base64.StdEncoding.DecodeString(value.String()) - if err != nil { - return nil, E.Cause(err, "decode ECH config") - } - return cftls.UnmarshalECHConfigs(echConfig) - } - } - default: - return nil, E.New("unknown resource record type: ", resource.Header().Rrtype) - } - } - return nil, E.New("no ECH config found") - } -} diff --git a/common/tls/ech_keygen.go b/common/tls/ech_keygen.go index 5053981f..b6c353ac 100644 --- a/common/tls/ech_keygen.go +++ b/common/tls/ech_keygen.go @@ -1,5 +1,3 @@ -//go:build with_ech - package tls import ( @@ -7,14 +5,13 @@ import ( "encoding/binary" "encoding/pem" - cftls "github.com/sagernet/cloudflare-tls" E "github.com/sagernet/sing/common/exceptions" "github.com/cloudflare/circl/hpke" "github.com/cloudflare/circl/kem" ) -func ECHKeygenDefault(serverName string, pqSignatureSchemesEnabled bool) (configPem string, keyPem string, err error) { +func ECHKeygenDefault(serverName string) (configPem string, keyPem string, err error) { cipherSuites := []echCipherSuite{ { kdf: hpke.KDF_HKDF_SHA256, @@ -24,13 +21,9 @@ func ECHKeygenDefault(serverName string, pqSignatureSchemesEnabled bool) (config aead: hpke.AEAD_ChaCha20Poly1305, }, } - keyConfig := []myECHKeyConfig{ {id: 0, kem: hpke.KEM_X25519_HKDF_SHA256}, } - if pqSignatureSchemesEnabled { - keyConfig = append(keyConfig, myECHKeyConfig{id: 1, kem: hpke.KEM_X25519_KYBER768_DRAFT00}) - } keyPairs, err := echKeygen(0xfe0d, serverName, keyConfig, cipherSuites) if err != nil { @@ -59,7 +52,6 @@ func ECHKeygenDefault(serverName string, pqSignatureSchemesEnabled bool) (config type echKeyConfigPair struct { id uint8 - key cftls.EXP_ECHKey rawKey []byte conf myECHKeyConfig rawConf []byte @@ -155,15 +147,6 @@ func echKeygen(version uint16, serverName string, conf []myECHKeyConfig, suite [ sk = append(sk, secBuf...) sk = be.AppendUint16(sk, uint16(len(b))) sk = append(sk, b...) - - cfECHKeys, err := cftls.EXP_UnmarshalECHKeys(sk) - if err != nil { - return nil, E.Cause(err, "bug: can't parse generated ECH server key") - } - if len(cfECHKeys) != 1 { - return nil, E.New("bug: unexpected server key count") - } - pair.key = cfECHKeys[0] pair.rawKey = sk pairs = append(pairs, pair) diff --git a/common/tls/ech_quic.go b/common/tls/ech_quic.go deleted file mode 100644 index 4606090a..00000000 --- a/common/tls/ech_quic.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build with_quic && with_ech - -package tls - -import ( - "context" - "net" - "net/http" - - "github.com/sagernet/cloudflare-tls" - "github.com/sagernet/quic-go/ech" - "github.com/sagernet/quic-go/http3_ech" - "github.com/sagernet/sing-quic" - M "github.com/sagernet/sing/common/metadata" -) - -var ( - _ qtls.Config = (*echClientConfig)(nil) - _ qtls.ServerConfig = (*echServerConfig)(nil) -) - -func (c *echClientConfig) Dial(ctx context.Context, conn net.PacketConn, addr net.Addr, config *quic.Config) (quic.Connection, error) { - return quic.Dial(ctx, conn, addr, c.config, config) -} - -func (c *echClientConfig) DialEarly(ctx context.Context, conn net.PacketConn, addr net.Addr, config *quic.Config) (quic.EarlyConnection, error) { - return quic.DialEarly(ctx, conn, addr, c.config, config) -} - -func (c *echClientConfig) CreateTransport(conn net.PacketConn, quicConnPtr *quic.EarlyConnection, serverAddr M.Socksaddr, quicConfig *quic.Config) http.RoundTripper { - return &http3.Transport{ - TLSClientConfig: c.config, - QUICConfig: quicConfig, - Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { - quicConn, err := quic.DialEarly(ctx, conn, serverAddr.UDPAddr(), tlsCfg, cfg) - if err != nil { - return nil, err - } - *quicConnPtr = quicConn - return quicConn, nil - }, - } -} - -func (c *echServerConfig) Listen(conn net.PacketConn, config *quic.Config) (qtls.Listener, error) { - return quic.Listen(conn, c.config, config) -} - -func (c *echServerConfig) ListenEarly(conn net.PacketConn, config *quic.Config) (qtls.EarlyListener, error) { - return quic.ListenEarly(conn, c.config, config) -} - -func (c *echServerConfig) ConfigureHTTP3() { - http3.ConfigureTLSConfig(c.config) -} diff --git a/common/tls/ech_server.go b/common/tls/ech_server.go deleted file mode 100644 index 03f5d876..00000000 --- a/common/tls/ech_server.go +++ /dev/null @@ -1,278 +0,0 @@ -//go:build with_ech - -package tls - -import ( - "context" - "crypto/tls" - "encoding/pem" - "net" - "os" - "strings" - - cftls "github.com/sagernet/cloudflare-tls" - "github.com/sagernet/fswatch" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" - E "github.com/sagernet/sing/common/exceptions" - "github.com/sagernet/sing/common/ntp" -) - -type echServerConfig struct { - config *cftls.Config - logger log.Logger - certificate []byte - key []byte - certificatePath string - keyPath string - echKeyPath string - watcher *fswatch.Watcher -} - -func (c *echServerConfig) ServerName() string { - return c.config.ServerName -} - -func (c *echServerConfig) SetServerName(serverName string) { - c.config.ServerName = serverName -} - -func (c *echServerConfig) NextProtos() []string { - return c.config.NextProtos -} - -func (c *echServerConfig) SetNextProtos(nextProto []string) { - c.config.NextProtos = nextProto -} - -func (c *echServerConfig) Config() (*STDConfig, error) { - return nil, E.New("unsupported usage for ECH") -} - -func (c *echServerConfig) Client(conn net.Conn) (Conn, error) { - return &echConnWrapper{cftls.Client(conn, c.config)}, nil -} - -func (c *echServerConfig) Server(conn net.Conn) (Conn, error) { - return &echConnWrapper{cftls.Server(conn, c.config)}, nil -} - -func (c *echServerConfig) Clone() Config { - return &echServerConfig{ - config: c.config.Clone(), - } -} - -func (c *echServerConfig) Start() error { - err := c.startWatcher() - if err != nil { - c.logger.Warn("create credentials watcher: ", err) - } - return nil -} - -func (c *echServerConfig) startWatcher() error { - var watchPath []string - if c.certificatePath != "" { - watchPath = append(watchPath, c.certificatePath) - } - if c.keyPath != "" { - watchPath = append(watchPath, c.keyPath) - } - if c.echKeyPath != "" { - watchPath = append(watchPath, c.echKeyPath) - } - if len(watchPath) == 0 { - return nil - } - watcher, err := fswatch.NewWatcher(fswatch.Options{ - Path: watchPath, - Callback: func(path string) { - err := c.credentialsUpdated(path) - if err != nil { - c.logger.Error(E.Cause(err, "reload credentials")) - } - }, - }) - if err != nil { - return err - } - err = watcher.Start() - if err != nil { - return err - } - c.watcher = watcher - return nil -} - -func (c *echServerConfig) credentialsUpdated(path string) error { - if path == c.certificatePath || path == c.keyPath { - if path == c.certificatePath { - certificate, err := os.ReadFile(c.certificatePath) - if err != nil { - return err - } - c.certificate = certificate - } else { - key, err := os.ReadFile(c.keyPath) - if err != nil { - return err - } - c.key = key - } - keyPair, err := cftls.X509KeyPair(c.certificate, c.key) - if err != nil { - return E.Cause(err, "parse key pair") - } - c.config.Certificates = []cftls.Certificate{keyPair} - c.logger.Info("reloaded TLS certificate") - } else { - echKeyContent, err := os.ReadFile(c.echKeyPath) - if err != nil { - return err - } - block, rest := pem.Decode(echKeyContent) - if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 { - return E.New("invalid ECH keys pem") - } - echKeys, err := cftls.EXP_UnmarshalECHKeys(block.Bytes) - if err != nil { - return E.Cause(err, "parse ECH keys") - } - echKeySet, err := cftls.EXP_NewECHKeySet(echKeys) - if err != nil { - return E.Cause(err, "create ECH key set") - } - c.config.ServerECHProvider = echKeySet - c.logger.Info("reloaded ECH keys") - } - return nil -} - -func (c *echServerConfig) Close() error { - var err error - if c.watcher != nil { - err = E.Append(err, c.watcher.Close(), func(err error) error { - return E.Cause(err, "close credentials watcher") - }) - } - return err -} - -func NewECHServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) { - if !options.Enabled { - return nil, nil - } - var tlsConfig cftls.Config - if options.ACME != nil && len(options.ACME.Domain) > 0 { - return nil, E.New("acme is unavailable in ech") - } - tlsConfig.Time = ntp.TimeFuncFromContext(ctx) - if options.ServerName != "" { - tlsConfig.ServerName = options.ServerName - } - if len(options.ALPN) > 0 { - tlsConfig.NextProtos = append(options.ALPN, tlsConfig.NextProtos...) - } - if options.MinVersion != "" { - minVersion, err := ParseTLSVersion(options.MinVersion) - if err != nil { - return nil, E.Cause(err, "parse min_version") - } - tlsConfig.MinVersion = minVersion - } - if options.MaxVersion != "" { - maxVersion, err := ParseTLSVersion(options.MaxVersion) - if err != nil { - return nil, E.Cause(err, "parse max_version") - } - tlsConfig.MaxVersion = maxVersion - } - 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) - } - } - var certificate []byte - var key []byte - if len(options.Certificate) > 0 { - certificate = []byte(strings.Join(options.Certificate, "\n")) - } else if options.CertificatePath != "" { - content, err := os.ReadFile(options.CertificatePath) - if err != nil { - return nil, E.Cause(err, "read certificate") - } - certificate = content - } - if len(options.Key) > 0 { - key = []byte(strings.Join(options.Key, "\n")) - } else if options.KeyPath != "" { - content, err := os.ReadFile(options.KeyPath) - if err != nil { - return nil, E.Cause(err, "read key") - } - key = content - } - - if certificate == nil { - return nil, E.New("missing certificate") - } else if key == nil { - return nil, E.New("missing key") - } - - keyPair, err := cftls.X509KeyPair(certificate, key) - if err != nil { - return nil, E.Cause(err, "parse x509 key pair") - } - tlsConfig.Certificates = []cftls.Certificate{keyPair} - - var echKey []byte - if len(options.ECH.Key) > 0 { - echKey = []byte(strings.Join(options.ECH.Key, "\n")) - } else if options.ECH.KeyPath != "" { - content, err := os.ReadFile(options.ECH.KeyPath) - if err != nil { - return nil, E.Cause(err, "read ECH key") - } - echKey = content - } else { - return nil, E.New("missing ECH key") - } - - block, rest := pem.Decode(echKey) - if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 { - return nil, E.New("invalid ECH keys pem") - } - - echKeys, err := cftls.EXP_UnmarshalECHKeys(block.Bytes) - if err != nil { - return nil, E.Cause(err, "parse ECH keys") - } - - echKeySet, err := cftls.EXP_NewECHKeySet(echKeys) - if err != nil { - return nil, E.Cause(err, "create ECH key set") - } - - tlsConfig.ECHEnabled = true - tlsConfig.PQSignatureSchemesEnabled = options.ECH.PQSignatureSchemesEnabled - tlsConfig.DynamicRecordSizingDisabled = options.ECH.DynamicRecordSizingDisabled - tlsConfig.ServerECHProvider = echKeySet - - return &echServerConfig{ - config: &tlsConfig, - logger: logger, - certificate: certificate, - key: key, - certificatePath: options.CertificatePath, - keyPath: options.KeyPath, - echKeyPath: options.ECH.KeyPath, - }, nil -} diff --git a/common/tls/ech_stub.go b/common/tls/ech_stub.go index 4ee4272c..8c93690c 100644 --- a/common/tls/ech_stub.go +++ b/common/tls/ech_stub.go @@ -1,25 +1,23 @@ -//go:build !with_ech +//go:build !go1.24 package tls import ( "context" + "crypto/tls" - "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" ) -var errECHNotIncluded = E.New(`ECH is not included in this build, rebuild with -tags with_ech`) - -func NewECHServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) { - return nil, errECHNotIncluded +func parseECHClientConfig(ctx context.Context, options option.OutboundTLSOptions, tlsConfig *tls.Config) (Config, error) { + return nil, E.New("ECH requires go1.24, please recompile your binary.") } -func NewECHClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) { - return nil, errECHNotIncluded +func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions, tlsConfig *tls.Config, echKeyPath *string) error { + return E.New("ECH requires go1.24, please recompile your binary.") } -func ECHKeygenDefault(host string, pqSignatureSchemesEnabled bool) (configPem string, keyPem string, err error) { - return "", "", errECHNotIncluded +func reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error { + return E.New("ECH requires go1.24, please recompile your binary.") } diff --git a/common/tls/mkcert.go b/common/tls/mkcert.go index 12680c48..4e0ed102 100644 --- a/common/tls/mkcert.go +++ b/common/tls/mkcert.go @@ -12,6 +12,9 @@ import ( ) func GenerateKeyPair(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string) (*tls.Certificate, error) { + if timeFunc == nil { + timeFunc = time.Now + } privateKeyPem, publicKeyPem, err := GenerateCertificate(parent, parentKey, timeFunc, serverName, timeFunc().Add(time.Hour)) if err != nil { return nil, err @@ -24,9 +27,6 @@ func GenerateKeyPair(parent *x509.Certificate, parentKey any, timeFunc func() ti } func GenerateCertificate(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string, expire time.Time) (privateKeyPem []byte, publicKeyPem []byte, err error) { - if timeFunc == nil { - timeFunc = time.Now - } key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return diff --git a/common/tls/server.go b/common/tls/server.go index 6afd89d6..bcc5ddfa 100644 --- a/common/tls/server.go +++ b/common/tls/server.go @@ -16,13 +16,10 @@ func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLS if !options.Enabled { return nil, nil } - if options.ECH != nil && options.ECH.Enabled { - return NewECHServer(ctx, logger, options) - } else if options.Reality != nil && options.Reality.Enabled { + if options.Reality != nil && options.Reality.Enabled { return NewRealityServer(ctx, logger, options) - } else { - return NewSTDServer(ctx, logger, options) } + return NewSTDServer(ctx, logger, options) } func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) { diff --git a/common/tls/std_client.go b/common/tls/std_client.go index 78f4e5cf..8e9327bf 100644 --- a/common/tls/std_client.go +++ b/common/tls/std_client.go @@ -127,5 +127,8 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb } tlsConfig.RootCAs = certPool } + if options.ECH != nil && options.ECH.Enabled { + return parseECHClientConfig(ctx, options, &tlsConfig) + } return &STDClientConfig{&tlsConfig}, nil } diff --git a/common/tls/std_server.go b/common/tls/std_server.go index f62f3d12..326d19ca 100644 --- a/common/tls/std_server.go +++ b/common/tls/std_server.go @@ -27,6 +27,7 @@ type STDServerConfig struct { key []byte certificatePath string keyPath string + echKeyPath string watcher *fswatch.Watcher } @@ -95,6 +96,9 @@ func (c *STDServerConfig) startWatcher() error { if c.keyPath != "" { watchPath = append(watchPath, c.keyPath) } + if c.echKeyPath != "" { + watchPath = append(watchPath, c.echKeyPath) + } watcher, err := fswatch.NewWatcher(fswatch.Options{ Path: watchPath, Callback: func(path string) { @@ -116,25 +120,33 @@ func (c *STDServerConfig) startWatcher() error { } func (c *STDServerConfig) certificateUpdated(path string) error { - if path == c.certificatePath { - certificate, err := os.ReadFile(c.certificatePath) - if err != nil { - return E.Cause(err, "reload certificate from ", c.certificatePath) + if path == c.certificatePath || path == c.keyPath { + 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 + } else if path == c.keyPath { + key, err := os.ReadFile(c.keyPath) + if err != nil { + return E.Cause(err, "reload key from ", c.keyPath) + } + c.key = key } - c.certificate = certificate - } else if path == c.keyPath { - key, err := os.ReadFile(c.keyPath) + keyPair, err := tls.X509KeyPair(c.certificate, c.key) if err != nil { - return E.Cause(err, "reload key from ", c.keyPath) + return E.Cause(err, "reload key pair") } - c.key = key + c.config.Certificates = []tls.Certificate{keyPair} + c.logger.Info("reloaded TLS certificate") + } else if path == c.echKeyPath { + err := reloadECHKeys(c.echKeyPath, c.config) + if err != nil { + return err + } + c.logger.Info("reloaded ECH keys") } - keyPair, err := tls.X509KeyPair(c.certificate, c.key) - if err != nil { - return E.Cause(err, "reload key pair") - } - c.config.Certificates = []tls.Certificate{keyPair} - c.logger.Info("reloaded TLS certificate") return nil } @@ -243,6 +255,13 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound tlsConfig.Certificates = []tls.Certificate{keyPair} } } + var echKeyPath string + if options.ECH != nil && options.ECH.Enabled { + err = parseECHServerConfig(ctx, options, tlsConfig, &echKeyPath) + if err != nil { + return nil, err + } + } return &STDServerConfig{ config: tlsConfig, logger: logger, @@ -251,5 +270,6 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound key: key, certificatePath: options.CertificatePath, keyPath: options.KeyPath, + echKeyPath: echKeyPath, }, nil } diff --git a/docs/configuration/shared/tls.md b/docs/configuration/shared/tls.md index f2a716a7..e4d77718 100644 --- a/docs/configuration/shared/tls.md +++ b/docs/configuration/shared/tls.md @@ -1,3 +1,12 @@ +--- +icon: material/alert-decagram +--- + +!!! quote "Changes in sing-box 1.12.0" + + :material-delete-clock: [ech.pq_signature_schemes_enabled](#pq_signature_schemes_enabled) + :material-delete-clock: [ech.dynamic_record_sizing_disabled](#dynamic_record_sizing_disabled) + !!! quote "Changes in sing-box 1.10.0" :material-alert-decagram: [utls](#utls) @@ -34,10 +43,13 @@ }, "ech": { "enabled": false, - "pq_signature_schemes_enabled": false, - "dynamic_record_sizing_disabled": false, "key": [], - "key_path": "" + "key_path": "", + + // Deprecated + + "pq_signature_schemes_enabled": false, + "dynamic_record_sizing_disabled": false }, "reality": { "enabled": false, @@ -72,10 +84,12 @@ "certificate_path": "", "ech": { "enabled": false, - "pq_signature_schemes_enabled": false, - "dynamic_record_sizing_disabled": false, "config": [], - "config_path": "" + "config_path": "", + + // Deprecated + "pq_signature_schemes_enabled": false, + "dynamic_record_sizing_disabled": false }, "utls": { "enabled": false, @@ -246,16 +260,22 @@ Chrome fingerprint will be used if empty. ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello message. -The ECH key and configuration can be generated by `sing-box generate ech-keypair [--pq-signature-schemes-enabled]`. +The ECH key and configuration can be generated by `sing-box generate ech-keypair`. #### pq_signature_schemes_enabled +!!! failure "Deprecated in sing-box 1.12.0" + + ECH support has been migrated to use stdlib in sing-box 1.12.0, which does not come with support for PQ signature schemes, so `pq_signature_schemes_enabled` has been deprecated and no longer works. + Enable support for post-quantum peer certificate signature schemes. -It is recommended to match the parameters of `sing-box generate ech-keypair`. - #### dynamic_record_sizing_disabled +!!! failure "Deprecated in sing-box 1.12.0" + + `dynamic_record_sizing_disabled` has nothing to do with ECH, was added by mistake, has been deprecated and no longer works. + Disables adaptive sizing of TLS records. When true, the largest possible TLS record size is always used. diff --git a/docs/configuration/shared/tls.zh.md b/docs/configuration/shared/tls.zh.md index 6cfc5f36..1418dd59 100644 --- a/docs/configuration/shared/tls.zh.md +++ b/docs/configuration/shared/tls.zh.md @@ -1,3 +1,12 @@ +--- +icon: material/alert-decagram +--- + +!!! quote "sing-box 1.12.0 中的更改" + + :material-delete-clock: [ech.pq_signature_schemes_enabled](#pq_signature_schemes_enabled) + :material-delete-clock: [ech.dynamic_record_sizing_disabled](#dynamic_record_sizing_disabled) + !!! quote "sing-box 1.10.0 中的更改" :material-alert-decagram: [utls](#utls) @@ -34,18 +43,21 @@ }, "ech": { "enabled": false, - "pq_signature_schemes_enabled": false, - "dynamic_record_sizing_disabled": false, "key": [], - "key_path": "" + "key_path": "", + + // 废弃的 + + "pq_signature_schemes_enabled": false, + "dynamic_record_sizing_disabled": false }, "reality": { "enabled": false, "handshake": { "server": "google.com", "server_port": 443, - ... - // 拨号字段 + + ... // 拨号字段 }, "private_key": "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc", "short_id": [ @@ -240,19 +252,6 @@ ECH (Encrypted Client Hello) 是一个 TLS 扩展,它允许客户端加密其 ECH 配置和密钥可以通过 `sing-box generate ech-keypair [--pq-signature-schemes-enabled]` 生成。 -#### pq_signature_schemes_enabled - -启用对后量子对等证书签名方案的支持。 - -建议匹配 `sing-box generate ech-keypair` 的参数。 - -#### dynamic_record_sizing_disabled - -禁用 TLS 记录的自适应大小调整。 - -如果为 true,则始终使用最大可能的 TLS 记录大小。 -如果为 false,则可能会调整 TLS 记录的大小以尝试改善延迟。 - #### key ==仅服务器== @@ -285,6 +284,27 @@ ECH PEM 配置路径 如果为空,将尝试从 DNS 加载。 +#### pq_signature_schemes_enabled + +!!! failure "已在 sing-box 1.12.0 废弃" + + ECH 支持已在 sing-box 1.12.0 迁移至使用标准库,但标准库不支持后量子对等证书签名方案,因此 `pq_signature_schemes_enabled` 已被弃用且不再工作。 + +启用对后量子对等证书签名方案的支持。 + +建议匹配 `sing-box generate ech-keypair` 的参数。 + +#### dynamic_record_sizing_disabled + +!!! failure "已在 sing-box 1.12.0 废弃" + + `dynamic_record_sizing_disabled` 与 ECH 无关,是错误添加的,现已弃用且不再工作。 + +禁用 TLS 记录的自适应大小调整。 + +如果为 true,则始终使用最大可能的 TLS 记录大小。 +如果为 false,则可能会调整 TLS 记录的大小以尝试改善延迟。 + ### ACME 字段 #### domain diff --git a/docs/deprecated.md b/docs/deprecated.md index 128c0709..8e53bda6 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -19,6 +19,17 @@ Legacy `outbound` DNS rules are deprecated and can be replaced by dial fields, check [Migration](../migration/#migrate-outbound-dns-rule-items-to-domain-resolver). +#### Legacy ECH fields + +ECH support has been migrated to use stdlib in sing-box 1.12.0, +which does not come with support for PQ signature schemes, +so `pq_signature_schemes_enabled` has been deprecated and no longer works. + +Also, `dynamic_record_sizing_disabled` has nothing to do with ECH, +was added by mistake, has been deprecated and no longer works. + +These fields will be removed in sing-box 1.13.0. + ## 1.11.0 #### Legacy special outbounds diff --git a/docs/deprecated.zh.md b/docs/deprecated.zh.md index 2c0f5578..c090432b 100644 --- a/docs/deprecated.zh.md +++ b/docs/deprecated.zh.md @@ -17,6 +17,15 @@ DNS 服务器已重构, 且可被拨号字段代替, 参阅 [迁移指南](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver). +#### 旧的 ECH 字段 + +ECH 支持已在 sing-box 1.12.0 迁移至使用标准库,但标准库不支持后量子对等证书签名方案, +因此 `pq_signature_schemes_enabled` 已被弃用且不再工作。 + +另外,`dynamic_record_sizing_disabled` 与 ECH 无关,是错误添加的,现已弃用且不再工作。 + +相关字段将在 sing-box 1.13.0 中被移除。 + ## 1.11.0 #### 旧的特殊出站 diff --git a/docs/installation/build-from-source.md b/docs/installation/build-from-source.md index 8c4d4923..c286c366 100644 --- a/docs/installation/build-from-source.md +++ b/docs/installation/build-from-source.md @@ -6,19 +6,18 @@ icon: material/file-code ## :material-graph: Requirements +### sing-box 1.11 + +* Go 1.23.1 - ~ + ### sing-box 1.10 * Go 1.20.0 - ~ -* Go 1.20.0 - ~ with tag `with_quic`, or `with_utls` enabled -* Go 1.21.0 - ~ with tag `with_ech` enabled ### sing-box 1.9 * Go 1.18.5 - 1.22.x * Go 1.20.0 - 1.22.x with tag `with_quic`, or `with_utls` enabled -* Go 1.21.0 - 1.22.x with tag `with_ech` enabled - -You can download and install Go from: https://go.dev/doc/install, latest version is recommended. ## :material-fast-forward: Simple Build @@ -46,20 +45,19 @@ go build -tags "tag_a tag_b" ./cmd/sing-box ## :material-folder-settings: Build Tags -| Build Tag | Enabled by default | Description | -|------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `with_quic` | :material-check: | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server/), [Naive inbound](/configuration/inbound/naive/), [Hysteria Inbound](/configuration/inbound/hysteria/), [Hysteria Outbound](/configuration/outbound/hysteria/) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | -| `with_grpc` | :material-close:️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | -| `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). | -| `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). | -| `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | -| `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | -| `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). | -| `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). | -| `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). | -| `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | -| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | -| `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). | +| Build Tag | Enabled by default | Description | +|------------------------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `with_quic` | :material-check: | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server/), [Naive inbound](/configuration/inbound/naive/), [Hysteria Inbound](/configuration/inbound/hysteria/), [Hysteria Outbound](/configuration/outbound/hysteria/) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | +| `with_grpc` | :material-close:️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | +| `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). | +| `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). | +| `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | +| `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). | +| `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). | +| `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). | +| `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | +| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | +| `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). | | `with_tailscale` | :material-check: | Build with Tailscale support, see [Tailscale endpoint](/configuration/endpoint/tailscale) | It is not recommended to change the default build tag list unless you really know what you are adding. diff --git a/docs/installation/build-from-source.zh.md b/docs/installation/build-from-source.zh.md index 7de7fcf4..c0222929 100644 --- a/docs/installation/build-from-source.zh.md +++ b/docs/installation/build-from-source.zh.md @@ -6,10 +6,13 @@ icon: material/file-code ## :material-graph: 要求 +### sing-box 1.11 + +* Go 1.23.1 - ~ + ### sing-box 1.10 * Go 1.20.0 - ~ -* Go 1.20.0 - ~ with tag `with_quic`, or `with_utls` enabled * Go 1.21.0 - ~ with tag `with_ech` enabled ### sing-box 1.9 @@ -52,7 +55,6 @@ go build -tags "tag_a tag_b" ./cmd/sing-box | `with_grpc` | :material-close:️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | | `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). | | `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). | -| `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | | `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | | `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). | | `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). | diff --git a/experimental/deprecated/constants.go b/experimental/deprecated/constants.go index d5c3ca48..16216716 100644 --- a/experimental/deprecated/constants.go +++ b/experimental/deprecated/constants.go @@ -144,6 +144,7 @@ var OptionTUNGSO = Note{ DeprecatedVersion: "1.11.0", ScheduledVersion: "1.12.0", EnvName: "TUN_GSO", + MigrationLink: "https://sing-box.sagernet.org/deprecated/#gso-option-in-tun", } var OptionLegacyDNSTransport = Note{ @@ -179,6 +180,14 @@ var OptionMissingDomainResolver = Note{ MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-outbound-dns-rule-items-to-domain-resolver", } +var OptionLegacyECHOptions = Note{ + Name: "legacy-ech-options", + Description: "legacy ECH options", + DeprecatedVersion: "1.12.0", + ScheduledVersion: "1.13.0", + MigrationLink: "https://sing-box.sagernet.org/deprecated/#legacy-ech-fields", +} + var Options = []Note{ OptionBadMatchSource, OptionGEOIP, @@ -194,4 +203,5 @@ var Options = []Note{ OptionLegacyDNSFakeIPOptions, OptionOutboundDNSRuleItem, OptionMissingDomainResolver, + OptionLegacyECHOptions, } diff --git a/experimental/deprecated/stderr.go b/experimental/deprecated/stderr.go index f29b4754..0dfb9354 100644 --- a/experimental/deprecated/stderr.go +++ b/experimental/deprecated/stderr.go @@ -28,11 +28,15 @@ func (f *stderrManager) ReportDeprecated(feature Note) { f.logger.Warn(feature.MessageWithLink()) return } - enable, enableErr := strconv.ParseBool(os.Getenv("ENABLE_DEPRECATED_" + feature.EnvName)) - if enableErr == nil && enable { - f.logger.Warn(feature.MessageWithLink()) - return + if feature.EnvName != "" { + enable, enableErr := strconv.ParseBool(os.Getenv("ENABLE_DEPRECATED_" + feature.EnvName)) + if enableErr == nil && enable { + f.logger.Warn(feature.MessageWithLink()) + return + } + f.logger.Error(feature.MessageWithLink()) + f.logger.Fatal("to continuing using this feature, set environment variable ENABLE_DEPRECATED_" + feature.EnvName + "=true") + } else { + f.logger.Error(feature.MessageWithLink()) } - f.logger.Error(feature.MessageWithLink()) - f.logger.Fatal("to continuing using this feature, set environment variable ENABLE_DEPRECATED_" + feature.EnvName + "=true") } diff --git a/go.mod b/go.mod index 1b94f561..ba3d61cd 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/oschwald/maxminddb-golang v1.13.1 github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a - github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/gomobile v0.1.4 diff --git a/go.sum b/go.sum index 9cc5214e..a74cff5d 100644 --- a/go.sum +++ b/go.sum @@ -163,8 +163,6 @@ github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= -github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY= -github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k= github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ= github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= diff --git a/option/tls.go b/option/tls.go index 72aaaef1..13e75306 100644 --- a/option/tls.go +++ b/option/tls.go @@ -83,19 +83,25 @@ type InboundRealityHandshakeOptions struct { } type InboundECHOptions struct { - Enabled bool `json:"enabled,omitempty"` - PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` - DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` - Key badoption.Listable[string] `json:"key,omitempty"` - KeyPath string `json:"key_path,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Key badoption.Listable[string] `json:"key,omitempty"` + KeyPath string `json:"key_path,omitempty"` + + // Deprecated: not supported by stdlib + PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` + // Deprecated: added by fault + DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` } type OutboundECHOptions struct { - Enabled bool `json:"enabled,omitempty"` - PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` - DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` - Config badoption.Listable[string] `json:"config,omitempty"` - ConfigPath string `json:"config_path,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Config badoption.Listable[string] `json:"config,omitempty"` + ConfigPath string `json:"config_path,omitempty"` + + // Deprecated: not supported by stdlib + PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` + // Deprecated: added by fault + DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` } type OutboundUTLSOptions struct { diff --git a/test/box_test.go b/test/box_test.go index 08b50b64..bfffaa03 100644 --- a/test/box_test.go +++ b/test/box_test.go @@ -32,7 +32,7 @@ func TestMain(m *testing.M) { var globalCtx context.Context func init() { - globalCtx = box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry()) + globalCtx = box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), include.DNSTransportRegistry()) } func startInstance(t *testing.T, options option.Options) *box.Box { diff --git a/test/ech_test.go b/test/ech_test.go index 865ffba0..91d13d3a 100644 --- a/test/ech_test.go +++ b/test/ech_test.go @@ -15,7 +15,7 @@ import ( func TestECH(t *testing.T) { _, certPem, keyPem := createSelfSignedCertificate(t, "example.org") - echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) + echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org")) startInstance(t, option.Options{ Inbounds: []option.Inbound{ { @@ -108,7 +108,7 @@ func TestECH(t *testing.T) { func TestECHQUIC(t *testing.T) { _, certPem, keyPem := createSelfSignedCertificate(t, "example.org") - echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) + echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org")) startInstance(t, option.Options{ Inbounds: []option.Inbound{ { @@ -198,7 +198,7 @@ func TestECHQUIC(t *testing.T) { func TestECHHysteria2(t *testing.T) { _, certPem, keyPem := createSelfSignedCertificate(t, "example.org") - echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) + echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org")) startInstance(t, option.Options{ Inbounds: []option.Inbound{ { diff --git a/test/go.mod b/test/go.mod index 68fddc34..2baf3b41 100644 --- a/test/go.mod +++ b/test/go.mod @@ -1,8 +1,8 @@ module test -go 1.23 +go 1.23.1 -toolchain go1.23.2 +toolchain go1.24.0 require github.com/sagernet/sing-box v0.0.0 @@ -11,110 +11,149 @@ replace github.com/sagernet/sing-box => ../ require ( github.com/docker/docker v27.3.1+incompatible github.com/docker/go-connections v0.5.0 - github.com/gofrs/uuid/v5 v5.3.0 - github.com/sagernet/quic-go v0.48.2-beta.1 - github.com/sagernet/sing v0.6.0-beta.12 - github.com/sagernet/sing-dns v0.4.0-beta.2 - github.com/sagernet/sing-quic v0.4.0-beta.4 + github.com/gofrs/uuid/v5 v5.3.1 + github.com/sagernet/quic-go v0.49.0-beta.1 + github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff + github.com/sagernet/sing-quic v0.4.1-beta.1 github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks2 v0.2.0 github.com/spyzhov/ajson v0.9.4 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 go.uber.org/goleak v1.3.0 - golang.org/x/net v0.31.0 + golang.org/x/net v0.35.0 ) require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ajg/form v1.5.1 // indirect - github.com/andybalholm/brotli v1.0.6 // indirect - github.com/caddyserver/certmagic v0.20.0 // indirect - github.com/cloudflare/circl v1.3.7 // indirect + github.com/akutz/memconn v0.1.0 // indirect + github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/anytls/sing-anytls v0.0.2 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/caddyserver/certmagic v0.21.7 // indirect + github.com/caddyserver/zerossl v0.1.3 // indirect + github.com/cloudflare/circl v1.6.0 // indirect + github.com/coder/websocket v1.8.12 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect github.com/cretz/bine v0.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect + github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect github.com/distribution/reference v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-chi/chi/v5 v5.1.0 // indirect + github.com/fxamacker/cbor/v2 v2.6.0 // indirect + github.com/gaissmai/bart v0.11.1 // indirect + github.com/go-chi/chi/v5 v5.2.1 // indirect github.com/go-chi/render v1.0.3 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect + github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.3 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect + github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/csrf v1.7.2 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect github.com/hashicorp/yamux v0.1.2 // indirect - github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect - github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/hdevalence/ed25519consensus v0.2.0 // indirect + github.com/illarion/gonotify/v2 v2.0.3 // indirect + github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect + github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect + github.com/jsimonetti/rtnetlink v1.4.0 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect github.com/libdns/alidns v1.0.3 // indirect github.com/libdns/cloudflare v0.1.1 // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect + github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect - github.com/mdlayher/socket v0.4.1 // indirect - github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa // indirect - github.com/mholt/acmez v1.2.0 // indirect - github.com/miekg/dns v1.1.62 // indirect + github.com/mdlayher/sdnotify v1.0.0 // indirect + github.com/mdlayher/socket v0.5.1 // indirect + github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect + github.com/mholt/acmez/v3 v3.0.1 // indirect + github.com/miekg/dns v1.1.63 // indirect + github.com/mitchellh/go-ps v1.0.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect - github.com/onsi/ginkgo/v2 v2.9.7 // indirect + github.com/onsi/ginkgo/v2 v2.17.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/oschwald/maxminddb-golang v1.12.0 // indirect - github.com/pierrec/lz4/v4 v4.1.14 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus-community/pro-bing v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + github.com/safchain/ethtool v0.3.0 // indirect github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect - github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect github.com/sagernet/cors v1.2.1 // indirect github.com/sagernet/fswatch v0.1.1 // indirect github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect - github.com/sagernet/sing-mux v0.3.0-alpha.1 // indirect - github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 // indirect - github.com/sagernet/sing-tun v0.6.0-beta.8 // indirect - github.com/sagernet/sing-vmess v0.2.0-beta.2 // indirect + github.com/sagernet/sing-mux v0.3.1 // indirect + github.com/sagernet/sing-shadowtls v0.2.0 // indirect + github.com/sagernet/sing-tun v0.6.1 // indirect + github.com/sagernet/sing-vmess v0.2.0 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect + github.com/sagernet/tailscale v1.79.0-mod.1 // indirect github.com/sagernet/utls v1.6.7 // indirect github.com/sagernet/wireguard-go v0.0.1-beta.5 // indirect github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect - github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect + github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect + github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect + github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 // indirect + github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect + github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect + github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect + github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect + github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect + github.com/tcnksm/go-httpstat v0.2.0 // indirect + github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect github.com/vishvananda/netns v0.0.4 // indirect - github.com/zeebo/blake3 v0.2.3 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect - go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect - go.opentelemetry.io/otel/metric v1.31.0 // indirect - go.opentelemetry.io/otel/sdk v1.31.0 // indirect - go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap/exp v0.3.0 // indirect + go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.33.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect - google.golang.org/grpc v1.67.1 // indirect - google.golang.org/protobuf v1.35.1 // indirect + golang.zx2c4.com/wireguard/windows v0.5.3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect diff --git a/test/go.sum b/test/go.sum index 5266d18c..d63f3057 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,24 +1,46 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= -github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= -github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= +github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= +github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/anytls/sing-anytls v0.0.2 h1:25azSh0o/LMcIkhS4ZutgRTIGwh8O3wuOhsThVM9K9o= +github.com/anytls/sing-anytls v0.0.2/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= +github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= +github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= +github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= +github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= +github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0= +github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk= +github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ= +github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q= +github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= @@ -31,10 +53,18 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= -github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= +github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc= +github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= +github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= +github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= +github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= +github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -42,42 +72,63 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= -github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU= +github.com/gofrs/uuid/v5 v5.3.1 h1:aPx49MwJbekCzOyhZDjJVb0hx3A0KLjlbLx6p2gY0p0= +github.com/gofrs/uuid/v5 v5.3.1/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI= +github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= +github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= -github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA= -github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= +github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= +github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/illarion/gonotify/v2 v2.0.3 h1:B6+SKPo/0Sw8cRJh1aLzNEeNVFfzE3c6N+o+vyxM+9A= +github.com/illarion/gonotify/v2 v2.0.3/go.mod h1:38oIJTgFqupkEydkkClkbL6i5lXV/bxdH9do5TALPEE= +github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4= +github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk= +github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8= +github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= +github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= +github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= @@ -89,48 +140,56 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= +github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4= -github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw= -github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= -github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= -github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= -github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= +github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= +github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY= +github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= +github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8= +github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= +github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= -github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= -github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4= +github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0= +github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= -github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY= -github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k= github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ= github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= @@ -141,31 +200,31 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/quic-go v0.48.2-beta.1 h1:W0plrLWa1XtOWDTdX3CJwxmQuxkya12nN5BRGZ87kEg= -github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k= +github.com/sagernet/quic-go v0.49.0-beta.1 h1:3LdoCzVVfYRibZns1tYWSIoB65fpTmrwy+yfK8DQ8Jk= +github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8WHNsRs71b3Lt1+p/U= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing v0.6.0-beta.12 h1:2DnTJcvypK3/PM/8JjmgG8wVK48gdcpRwU98c4J/a7s= -github.com/sagernet/sing v0.6.0-beta.12/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing-dns v0.4.0-beta.2 h1:HW94bUEp7K/vf5DlYz646LTZevQtJ0250jZa/UZRlbY= -github.com/sagernet/sing-dns v0.4.0-beta.2/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8= -github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg= -github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE= -github.com/sagernet/sing-quic v0.4.0-beta.4 h1:kKiMLGaxvVLDCSvCMYo4PtWd1xU6FTL7xvUAQfXO09g= -github.com/sagernet/sing-quic v0.4.0-beta.4/go.mod h1:1UNObFodd8CnS3aCT53x9cigjPSCl3P//8dfBMCwBDM= +github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff h1:5UGghwx8cI14qFa0ienrLekAYfhdKAiWvJUkY7rHmsI= +github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI= +github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78= +github.com/sagernet/sing-quic v0.4.1-beta.1 h1:V2VfMckT3EQR3ZdfSzJgZZDsvfZZH42QAZpnOnHKa0s= +github.com/sagernet/sing-quic v0.4.1-beta.1/go.mod h1:c+CytOEyeN20KCTFIP8YQUkNDVFLSzjrEPqP7Hlnxys= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= -github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjSsZtRPxiyLV6jyKg0= -github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA= -github.com/sagernet/sing-tun v0.6.0-beta.8 h1:GFNt/w8r1v30zC/hfCytk8C9+N/f1DfvosFXJkyJlrw= -github.com/sagernet/sing-tun v0.6.0-beta.8/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= -github.com/sagernet/sing-vmess v0.2.0-beta.2 h1:obAkAL35X7ql4RnGzDg4dBYIRpGXRKqcN4LyLZpZGSs= -github.com/sagernet/sing-vmess v0.2.0-beta.2/go.mod h1:HGhf9XUdeE2iOWrX0hQNFgXPbKyGlzpeYFyX0c/pykk= +github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk= +github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo= +github.com/sagernet/sing-tun v0.6.1 h1:4l0+gnEKcGjlWfUVTD+W0BRApqIny/lU2ZliurE+VMo= +github.com/sagernet/sing-tun v0.6.1/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= +github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI= +github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= +github.com/sagernet/tailscale v1.79.0-mod.1 h1:wIuAH7VqBYJNk0h2+bTyk4F0OlSqHvyLDCBrD3i+XNI= +github.com/sagernet/tailscale v1.79.0-mod.1/go.mod h1:RKY5WjYLj3JJ7VO/8ZCw8eAFa4+kWU6A1Ftdk84uB14= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc= @@ -177,36 +236,60 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/spyzhov/ajson v0.9.4 h1:MVibcTCgO7DY4IlskdqIlCmDOsUOZ9P7oKj8ifdcf84= github.com/spyzhov/ajson v0.9.4/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= -github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ= +github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4= +github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= +github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= +github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 h1:rXZGgEa+k2vJM8xT0PoSKfVXwFGPQ3z3CJfmnHJkZZw= +github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ= +github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio= +github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= +github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw= +github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= +github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= +github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= +github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w= +github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU= +github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g= +github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= +github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= +github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= +github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= +github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= +github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw= +github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= -github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= -go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= -go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= -go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= -go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= -go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= -go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -215,53 +298,63 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= +go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= +go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= +go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -276,17 +369,19 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= -google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= +golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -294,3 +389,5 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= +software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= From 483fd413fb13710895ea552b0ee34cff16c75701 Mon Sep 17 00:00:00 2001 From: anytls Date: Thu, 20 Feb 2025 20:06:21 +0800 Subject: [PATCH 36/94] Add AnyTLS protocol --- constant/proxy.go | 1 + docs/configuration/inbound/anytls.md | 39 +++++++ docs/configuration/inbound/anytls.zh.md | 39 +++++++ docs/configuration/inbound/index.md | 1 + docs/configuration/inbound/index.zh.md | 1 + docs/configuration/outbound/anytls.md | 55 +++++++++ docs/configuration/outbound/anytls.zh.md | 55 +++++++++ docs/configuration/outbound/index.md | 1 + docs/configuration/outbound/index.zh.md | 1 + go.mod | 1 + go.sum | 4 + include/registry.go | 3 + option/anytls.go | 24 ++++ protocol/anytls/inbound.go | 135 +++++++++++++++++++++++ protocol/anytls/outbound.go | 129 ++++++++++++++++++++++ 15 files changed, 489 insertions(+) create mode 100644 docs/configuration/inbound/anytls.md create mode 100644 docs/configuration/inbound/anytls.zh.md create mode 100644 docs/configuration/outbound/anytls.md create mode 100644 docs/configuration/outbound/anytls.zh.md create mode 100644 option/anytls.go create mode 100644 protocol/anytls/inbound.go create mode 100644 protocol/anytls/outbound.go diff --git a/constant/proxy.go b/constant/proxy.go index 45e79f84..787a1243 100644 --- a/constant/proxy.go +++ b/constant/proxy.go @@ -19,6 +19,7 @@ const ( TypeTor = "tor" TypeSSH = "ssh" TypeShadowTLS = "shadowtls" + TypeAnyTLS = "anytls" TypeShadowsocksR = "shadowsocksr" TypeVLESS = "vless" TypeTUIC = "tuic" diff --git a/docs/configuration/inbound/anytls.md b/docs/configuration/inbound/anytls.md new file mode 100644 index 00000000..ce4eefe6 --- /dev/null +++ b/docs/configuration/inbound/anytls.md @@ -0,0 +1,39 @@ +### Structure + +```json +{ + "type": "anytls", + "tag": "anytls-in", + + ... // Listen Fields + + "users": [ + { + "name": "sekai", + "password": "8JCsPssfgS8tiRwiMlhARg==" + } + ], + "padding_scheme": [], + "tls": {} +} +``` + +### Listen Fields + +See [Listen Fields](/configuration/shared/listen/) for details. + +### Fields + +#### users + +==Required== + +AnyTLS users. + +#### padding_scheme + +AnyTLS padding scheme line array. + +#### tls + +TLS configuration, see [TLS](/configuration/shared/tls/#inbound). diff --git a/docs/configuration/inbound/anytls.zh.md b/docs/configuration/inbound/anytls.zh.md new file mode 100644 index 00000000..27e78ba4 --- /dev/null +++ b/docs/configuration/inbound/anytls.zh.md @@ -0,0 +1,39 @@ +### 结构 + +```json +{ + "type": "anytls", + "tag": "anytls-in", + + ... // 监听字段 + + "users": [ + { + "name": "sekai", + "password": "8JCsPssfgS8tiRwiMlhARg==" + } + ], + "padding_scheme": [], + "tls": {} +} +``` + +### 监听字段 + +参阅 [监听字段](/zh/configuration/shared/listen/)。 + +### 字段 + +#### users + +==必填== + +AnyTLS 用户。 + +#### padding_scheme + +AnyTLS 填充方案行数组。 + +#### tls + +TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。 diff --git a/docs/configuration/inbound/index.md b/docs/configuration/inbound/index.md index 9ff4a7cf..27cc9fdb 100644 --- a/docs/configuration/inbound/index.md +++ b/docs/configuration/inbound/index.md @@ -30,6 +30,7 @@ | `tuic` | [TUIC](./tuic/) | :material-close: | | `hysteria2` | [Hysteria2](./hysteria2/) | :material-close: | | `vless` | [VLESS](./vless/) | TCP | +| `anytls` | [AnyTLS](./anytls/) | TCP | | `tun` | [Tun](./tun/) | :material-close: | | `redirect` | [Redirect](./redirect/) | :material-close: | | `tproxy` | [TProxy](./tproxy/) | :material-close: | diff --git a/docs/configuration/inbound/index.zh.md b/docs/configuration/inbound/index.zh.md index 2c036340..1e0c0c4f 100644 --- a/docs/configuration/inbound/index.zh.md +++ b/docs/configuration/inbound/index.zh.md @@ -30,6 +30,7 @@ | `tuic` | [TUIC](./tuic/) | :material-close: | | `hysteria2` | [Hysteria2](./hysteria2/) | :material-close: | | `vless` | [VLESS](./vless/) | TCP | +| `anytls` | [AnyTLS](./anytls/) | TCP | | `tun` | [Tun](./tun/) | :material-close: | | `redirect` | [Redirect](./redirect/) | :material-close: | | `tproxy` | [TProxy](./tproxy/) | :material-close: | diff --git a/docs/configuration/outbound/anytls.md b/docs/configuration/outbound/anytls.md new file mode 100644 index 00000000..42ee51d9 --- /dev/null +++ b/docs/configuration/outbound/anytls.md @@ -0,0 +1,55 @@ +### Structure + +```json +{ + "type": "anytls", + "tag": "anytls-out", + + "server": "127.0.0.1", + "server_port": 1080, + "password": "8JCsPssfgS8tiRwiMlhARg==", + "idle_session_check_interval": "30s", + "idle_session_timeout": "30s", + "tls": {}, + + ... // Dial Fields +} +``` + +### Fields + +#### server + +==Required== + +The server address. + +#### server_port + +==Required== + +The server port. + +#### password + +==Required== + +The AnyTLS password. + +#### idle_session_check_interval + +Interval checking for idle sessions. Default: 30s. + +#### idle_session_timeout + +In the check, close sessions that have been idle for longer than this. Default: 30s. + +#### tls + +==Required== + +TLS configuration, see [TLS](/configuration/shared/tls/#outbound). + +### Dial Fields + +See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/outbound/anytls.zh.md b/docs/configuration/outbound/anytls.zh.md new file mode 100644 index 00000000..4f6421df --- /dev/null +++ b/docs/configuration/outbound/anytls.zh.md @@ -0,0 +1,55 @@ +### 结构 + +```json +{ + "type": "anytls", + "tag": "anytls-out", + + "server": "127.0.0.1", + "server_port": 1080, + "password": "8JCsPssfgS8tiRwiMlhARg==", + "idle_session_check_interval": "30s", + "idle_session_timeout": "30s", + "tls": {}, + + ... // 拨号字段 +} +``` + +### 字段 + +#### server + +==必填== + +服务器地址。 + +#### server_port + +==必填== + +服务器端口。 + +#### password + +==必填== + +AnyTLS 密码。 + +#### idle_session_check_interval + +检查空闲会话的时间间隔。默认值:30秒。 + +#### idle_session_timeout + +在检查中,关闭闲置时间超过此值的会话。默认值:30秒。 + +#### tls + +==必填== + +TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。 + +### 拨号字段 + +参阅 [拨号字段](/zh/configuration/shared/dial/)。 diff --git a/docs/configuration/outbound/index.md b/docs/configuration/outbound/index.md index c5dc2917..b6094a15 100644 --- a/docs/configuration/outbound/index.md +++ b/docs/configuration/outbound/index.md @@ -30,6 +30,7 @@ | `shadowtls` | [ShadowTLS](./shadowtls/) | | `tuic` | [TUIC](./tuic/) | | `hysteria2` | [Hysteria2](./hysteria2/) | +| `anytls` | [AnyTLS](./anytls/) | | `tor` | [Tor](./tor/) | | `ssh` | [SSH](./ssh/) | | `dns` | [DNS](./dns/) | diff --git a/docs/configuration/outbound/index.zh.md b/docs/configuration/outbound/index.zh.md index c7ee59e9..1b6066e7 100644 --- a/docs/configuration/outbound/index.zh.md +++ b/docs/configuration/outbound/index.zh.md @@ -30,6 +30,7 @@ | `shadowtls` | [ShadowTLS](./shadowtls/) | | `tuic` | [TUIC](./tuic/) | | `hysteria2` | [Hysteria2](./hysteria2/) | +| `anytls` | [AnyTLS](./anytls/) | | `tor` | [Tor](./tor/) | | `ssh` | [SSH](./ssh/) | | `dns` | [DNS](./dns/) | diff --git a/go.mod b/go.mod index ba3d61cd..09866a55 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/sagernet/sing-box go 1.23.1 require ( + github.com/anytls/sing-anytls v0.0.2 github.com/caddyserver/certmagic v0.21.7 github.com/cloudflare/circl v1.6.0 github.com/cretz/bine v0.2.0 diff --git a/go.sum b/go.sum index a74cff5d..93c45aa1 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,10 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/anytls/sing-anytls v0.0.1 h1:Hex6GFUcgATWMWL2E9YgH/7oPgwdokiIF09UQi5BEC0= +github.com/anytls/sing-anytls v0.0.1/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/anytls/sing-anytls v0.0.2 h1:25azSh0o/LMcIkhS4ZutgRTIGwh8O3wuOhsThVM9K9o= +github.com/anytls/sing-anytls v0.0.2/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= diff --git a/include/registry.go b/include/registry.go index 866c506a..87aea576 100644 --- a/include/registry.go +++ b/include/registry.go @@ -15,6 +15,7 @@ import ( "github.com/sagernet/sing-box/dns/transport/local" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-box/protocol/anytls" "github.com/sagernet/sing-box/protocol/block" "github.com/sagernet/sing-box/protocol/direct" protocolDNS "github.com/sagernet/sing-box/protocol/dns" @@ -53,6 +54,7 @@ func InboundRegistry() *inbound.Registry { naive.RegisterInbound(registry) shadowtls.RegisterInbound(registry) vless.RegisterInbound(registry) + anytls.RegisterInbound(registry) registerQUICInbounds(registry) registerStubForRemovedInbounds(registry) @@ -80,6 +82,7 @@ func OutboundRegistry() *outbound.Registry { ssh.RegisterOutbound(registry) shadowtls.RegisterOutbound(registry) vless.RegisterOutbound(registry) + anytls.RegisterOutbound(registry) registerQUICOutbounds(registry) registerWireGuardOutbound(registry) diff --git a/option/anytls.go b/option/anytls.go new file mode 100644 index 00000000..0ac19cd1 --- /dev/null +++ b/option/anytls.go @@ -0,0 +1,24 @@ +package option + +import "github.com/sagernet/sing/common/json/badoption" + +type AnyTLSInboundOptions struct { + ListenOptions + InboundTLSOptionsContainer + Users []AnyTLSUser `json:"users,omitempty"` + PaddingScheme badoption.Listable[string] `json:"padding_scheme,omitempty"` +} + +type AnyTLSUser struct { + Name string `json:"name,omitempty"` + Password string `json:"password,omitempty"` +} + +type AnyTLSOutboundOptions struct { + DialerOptions + ServerOptions + OutboundTLSOptionsContainer + Password string `json:"password,omitempty"` + IdleSessionCheckInterval badoption.Duration `json:"idle_session_check_interval,omitempty"` + IdleSessionTimeout badoption.Duration `json:"idle_session_timeout,omitempty"` +} diff --git a/protocol/anytls/inbound.go b/protocol/anytls/inbound.go new file mode 100644 index 00000000..89907f1d --- /dev/null +++ b/protocol/anytls/inbound.go @@ -0,0 +1,135 @@ +package anytls + +import ( + "context" + "net" + "strings" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/inbound" + "github.com/sagernet/sing-box/common/listener" + "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/common/uot" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/auth" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + + anytls "github.com/anytls/sing-anytls" + "github.com/anytls/sing-anytls/padding" +) + +func RegisterInbound(registry *inbound.Registry) { + inbound.Register[option.AnyTLSInboundOptions](registry, C.TypeAnyTLS, NewInbound) +} + +type Inbound struct { + inbound.Adapter + tlsConfig tls.ServerConfig + router adapter.ConnectionRouterEx + logger logger.ContextLogger + listener *listener.Listener + service *anytls.Service +} + +func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.AnyTLSInboundOptions) (adapter.Inbound, error) { + inbound := &Inbound{ + Adapter: inbound.NewAdapter(C.TypeAnyTLS, tag), + router: uot.NewRouter(router, logger), + logger: logger, + } + + if options.TLS != nil && options.TLS.Enabled { + tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS)) + if err != nil { + return nil, err + } + inbound.tlsConfig = tlsConfig + } + + paddingScheme := padding.DefaultPaddingScheme + if len(options.PaddingScheme) > 0 { + paddingScheme = []byte(strings.Join(options.PaddingScheme, "\n")) + } + + service, err := anytls.NewService(anytls.ServiceConfig{ + Users: common.Map(options.Users, func(it option.AnyTLSUser) anytls.User { + return (anytls.User)(it) + }), + PaddingScheme: paddingScheme, + Handler: (*inboundHandler)(inbound), + Logger: logger, + }) + if err != nil { + return nil, err + } + inbound.service = service + inbound.listener = listener.New(listener.Options{ + Context: ctx, + Logger: logger, + Network: []string{N.NetworkTCP}, + Listen: options.ListenOptions, + ConnectionHandler: inbound, + }) + return inbound, nil +} + +func (h *Inbound) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + if h.tlsConfig != nil { + err := h.tlsConfig.Start() + if err != nil { + return err + } + } + return h.listener.Start() +} + +func (h *Inbound) Close() error { + return common.Close(h.listener, h.tlsConfig) +} + +func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + if h.tlsConfig != nil { + tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig) + if err != nil { + N.CloseOnHandshakeFailure(conn, onClose, err) + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake")) + return + } + conn = tlsConn + } + err := h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose) + if err != nil { + N.CloseOnHandshakeFailure(conn, onClose, err) + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } +} + +type inboundHandler Inbound + +func (h *inboundHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + var metadata adapter.InboundContext + metadata.Inbound = h.Tag() + metadata.InboundType = h.Type() + //nolint:staticcheck + metadata.InboundDetour = h.listener.ListenOptions().Detour + //nolint:staticcheck + metadata.InboundOptions = h.listener.ListenOptions().InboundOptions + metadata.Source = source + metadata.Destination = destination + if userName, _ := auth.UserFromContext[string](ctx); userName != "" { + metadata.User = userName + h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination) + } else { + h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) + } + h.router.RouteConnectionEx(ctx, conn, metadata, onClose) +} diff --git a/protocol/anytls/outbound.go b/protocol/anytls/outbound.go new file mode 100644 index 00000000..3a016d2e --- /dev/null +++ b/protocol/anytls/outbound.go @@ -0,0 +1,129 @@ +package anytls + +import ( + "context" + "net" + "os" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" + "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/sing-box/common/tls" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/uot" + + anytls "github.com/anytls/sing-anytls" +) + +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.AnyTLSOutboundOptions](registry, C.TypeAnyTLS, NewOutbound) +} + +type Outbound struct { + outbound.Adapter + dialer N.Dialer + server M.Socksaddr + tlsConfig tls.Config + client *anytls.Client + uotClient *uot.Client + logger log.ContextLogger +} + +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.AnyTLSOutboundOptions) (adapter.Outbound, error) { + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeAnyTLS, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions), + server: options.ServerOptions.Build(), + logger: logger, + } + if options.TLS == nil || !options.TLS.Enabled { + return nil, C.ErrTLSRequired + } + + tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS)) + if err != nil { + return nil, err + } + outbound.tlsConfig = tlsConfig + + outboundDialer, err := dialer.NewWithOptions(dialer.Options{ + Context: ctx, + Options: options.DialerOptions, + }) + if err != nil { + return nil, err + } + outbound.dialer = outboundDialer + + client, err := anytls.NewClient(ctx, anytls.ClientConfig{ + Password: options.Password, + IdleSessionCheckInterval: options.IdleSessionCheckInterval.Build(), + IdleSessionTimeout: options.IdleSessionTimeout.Build(), + DialOut: outbound.dialOut, + Logger: logger, + }) + if err != nil { + return nil, err + } + outbound.client = client + + outbound.uotClient = &uot.Client{ + Dialer: (anytlsDialer)(client.CreateProxy), + Version: uot.Version, + } + return outbound, nil +} + +type anytlsDialer func(ctx context.Context, destination M.Socksaddr) (net.Conn, error) + +func (d anytlsDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + return d(ctx, destination) +} + +func (d anytlsDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + return nil, os.ErrInvalid +} + +func (h *Outbound) dialOut(ctx context.Context) (net.Conn, error) { + conn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.server) + if err != nil { + return nil, err + } + tlsConn, err := tls.ClientHandshake(ctx, conn, h.tlsConfig) + if err != nil { + common.Close(tlsConn, conn) + return nil, err + } + return tlsConn, nil +} + +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + ctx, metadata := adapter.ExtendContext(ctx) + metadata.Outbound = h.Tag() + metadata.Destination = destination + switch N.NetworkName(network) { + case N.NetworkTCP: + h.logger.InfoContext(ctx, "outbound connection to ", destination) + return h.client.CreateProxy(ctx, destination) + case N.NetworkUDP: + h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination) + return h.uotClient.DialContext(ctx, network, destination) + } + return nil, os.ErrInvalid +} + +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + ctx, metadata := adapter.ExtendContext(ctx) + metadata.Outbound = h.Tag() + metadata.Destination = destination + h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination) + return h.uotClient.ListenPacket(ctx, destination) +} + +func (h *Outbound) Close() error { + return common.Close(h.client) +} From 0614d85b017d2fc781d9a14e61ac8fa9ffa0e61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 22 Feb 2025 17:41:03 +0800 Subject: [PATCH 37/94] documentation: Fix AnyTLS doc --- docs/configuration/inbound/anytls.md | 20 ++++++++++++++++++++ docs/configuration/inbound/anytls.zh.md | 20 ++++++++++++++++++++ docs/configuration/outbound/anytls.md | 6 ++++++ docs/configuration/outbound/anytls.zh.md | 6 ++++++ mkdocs.yml | 2 ++ 5 files changed, 54 insertions(+) diff --git a/docs/configuration/inbound/anytls.md b/docs/configuration/inbound/anytls.md index ce4eefe6..7eca9434 100644 --- a/docs/configuration/inbound/anytls.md +++ b/docs/configuration/inbound/anytls.md @@ -1,3 +1,9 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + ### Structure ```json @@ -34,6 +40,20 @@ AnyTLS users. AnyTLS padding scheme line array. +Default padding scheme: + +``` +stop=8 +0=34-120 +1=100-400 +2=400-500,c,500-1000,c,400-500,c,500-1000,c,500-1000,c,400-500 +3=500-1000 +4=500-1000 +5=500-1000 +6=500-1000 +7=500-1000 +``` + #### tls TLS configuration, see [TLS](/configuration/shared/tls/#inbound). diff --git a/docs/configuration/inbound/anytls.zh.md b/docs/configuration/inbound/anytls.zh.md index 27e78ba4..5c119b72 100644 --- a/docs/configuration/inbound/anytls.zh.md +++ b/docs/configuration/inbound/anytls.zh.md @@ -1,3 +1,9 @@ +--- +icon: material/new-box +--- + +!!! question "自 sing-box 1.12.0 起" + ### 结构 ```json @@ -34,6 +40,20 @@ AnyTLS 用户。 AnyTLS 填充方案行数组。 +默认填充方案: + +``` +stop=8 +0=34-120 +1=100-400 +2=400-500,c,500-1000,c,400-500,c,500-1000,c,500-1000,c,400-500 +3=500-1000 +4=500-1000 +5=500-1000 +6=500-1000 +7=500-1000 +``` + #### tls TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。 diff --git a/docs/configuration/outbound/anytls.md b/docs/configuration/outbound/anytls.md index 42ee51d9..d5eb8fdf 100644 --- a/docs/configuration/outbound/anytls.md +++ b/docs/configuration/outbound/anytls.md @@ -1,3 +1,9 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + ### Structure ```json diff --git a/docs/configuration/outbound/anytls.zh.md b/docs/configuration/outbound/anytls.zh.md index 4f6421df..1d0828b1 100644 --- a/docs/configuration/outbound/anytls.zh.md +++ b/docs/configuration/outbound/anytls.zh.md @@ -1,3 +1,9 @@ +--- +icon: material/new-box +--- + +!!! question "自 sing-box 1.12.0 起" + ### 结构 ```json diff --git a/mkdocs.yml b/mkdocs.yml index e1a74fb0..f786ac84 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -144,6 +144,7 @@ nav: - VLESS: configuration/inbound/vless.md - TUIC: configuration/inbound/tuic.md - Hysteria2: configuration/inbound/hysteria2.md + - AnyTLS: configuration/inbound/anytls.md - Tun: configuration/inbound/tun.md - Redirect: configuration/inbound/redirect.md - TProxy: configuration/inbound/tproxy.md @@ -162,6 +163,7 @@ nav: - VLESS: configuration/outbound/vless.md - TUIC: configuration/outbound/tuic.md - Hysteria2: configuration/outbound/hysteria2.md + - AnyTLS: configuration/outbound/anytls.md - Tor: configuration/outbound/tor.md - SSH: configuration/outbound/ssh.md - DNS: configuration/outbound/dns.md From f56bd380602de7a90e5989946fbaa33286088af1 Mon Sep 17 00:00:00 2001 From: Alireza Ahmadi Date: Sun, 23 Feb 2025 01:23:37 +0100 Subject: [PATCH 38/94] Fix Outbound deadlock --- adapter/outbound/manager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/manager.go b/adapter/outbound/manager.go index 977fe4ca..44ac8bc5 100644 --- a/adapter/outbound/manager.go +++ b/adapter/outbound/manager.go @@ -246,8 +246,6 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log. if err != nil { return err } - m.access.Lock() - defer m.access.Unlock() if m.started { for _, stage := range adapter.ListStartStages { err = adapter.LegacyStart(outbound, stage) @@ -256,6 +254,8 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log. } } } + m.access.Lock() + defer m.access.Unlock() if existsOutbound, loaded := m.outboundByTag[tag]; loaded { if m.started { err = common.Close(existsOutbound) From df788c792566e4002e955a6c883e4e5885c5175b Mon Sep 17 00:00:00 2001 From: libtry486 <89328481+libtry486@users.noreply.github.com> Date: Sun, 23 Feb 2025 08:27:41 +0800 Subject: [PATCH 39/94] documentation: Fix typo fix typo Signed-off-by: libtry486 <89328481+libtry486@users.noreply.github.com> --- docs/configuration/dns/server/udp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/dns/server/udp.md b/docs/configuration/dns/server/udp.md index 4ffbf4d8..c551d3c9 100644 --- a/docs/configuration/dns/server/udp.md +++ b/docs/configuration/dns/server/udp.md @@ -4,7 +4,7 @@ icon: material/new-box !!! question "Since sing-box 1.12.0" -# TCP +# UDP ### Structure From 2c730e3ca615c38dbb690c6ca3027c87cbf7ad0b Mon Sep 17 00:00:00 2001 From: ReleTor <191429954+ReleTor@users.noreply.github.com> Date: Thu, 20 Feb 2025 17:39:39 +0800 Subject: [PATCH 40/94] documentation: Minor fixes --- docs/configuration/dns/server/http3.md | 2 +- docs/configuration/dns/server/https.md | 2 +- docs/configuration/dns/server/index.md | 26 ++++++------ docs/configuration/dns/server/index.zh.md | 46 +++++++++++++++++++++ docs/configuration/endpoint/index.md | 1 + docs/configuration/endpoint/index.zh.md | 5 ++- docs/configuration/endpoint/wireguard.zh.md | 2 +- docs/configuration/index.md | 2 + docs/configuration/index.zh.md | 2 + 9 files changed, 70 insertions(+), 18 deletions(-) create mode 100644 docs/configuration/dns/server/index.zh.md diff --git a/docs/configuration/dns/server/http3.md b/docs/configuration/dns/server/http3.md index 89f000e8..dd81ba2d 100644 --- a/docs/configuration/dns/server/http3.md +++ b/docs/configuration/dns/server/http3.md @@ -50,7 +50,7 @@ If domain name is used, `domain_resolver` must also be set to resolve IP address The port of the DNS server. -`853` will be used by default. +`443` will be used by default. #### path diff --git a/docs/configuration/dns/server/https.md b/docs/configuration/dns/server/https.md index 93dc71a4..46e69a55 100644 --- a/docs/configuration/dns/server/https.md +++ b/docs/configuration/dns/server/https.md @@ -50,7 +50,7 @@ If domain name is used, `domain_resolver` must also be set to resolve IP address The port of the DNS server. -`853` will be used by default. +`443` will be used by default. #### path diff --git a/docs/configuration/dns/server/index.md b/docs/configuration/dns/server/index.md index 5393b0bd..0dae05c3 100644 --- a/docs/configuration/dns/server/index.md +++ b/docs/configuration/dns/server/index.md @@ -27,19 +27,19 @@ icon: material/alert-decagram The type of the DNS server. -| Type | Format | -|-----------------|-----------------------------------------------------| -| empty (default) | [Legacy](/configuration/dns/server/legacy/) | -| `tcp` | [TCP](/configuration/dns/server/tcp/) | -| `udp` | [UDP](/configuration/dns/server/udp/) | -| `tls` | [TLS](/configuration/dns/server/tls/) | -| `https` | [HTTPS](/configuration/dns/server/https/) | -| `quic` | [QUIC](/configuration/dns/server/quic/) | -| `h3` | [HTTP/3](/configuration/dns/server/http3/) | -| `predefined` | [Predefined](/configuration/dns/server/predefined/) | -| `dhcp` | [DHCP](/configuration/dns/server/dhcp/) | -| `fakeip` | [Fake IP](/configuration/dns/server/fakeip/) | - +| Type | Format | +|-----------------|-----------------------------| +| empty (default) | [Legacy](./legacy/) | +| `tcp` | [TCP](./tcp/) | +| `udp` | [UDP](./udp/) | +| `tls` | [TLS](./tls/) | +| `https` | [HTTPS](./https/) | +| `quic` | [QUIC](./quic/) | +| `h3` | [HTTP/3](./http3/) | +| `predefined` | [Predefined](./predefined/) | +| `dhcp` | [DHCP](./dhcp/) | +| `fakeip` | [Fake IP](./fakeip/) | +| `tailscale` | [Tailscale](./tailscale/) | #### tag diff --git a/docs/configuration/dns/server/index.zh.md b/docs/configuration/dns/server/index.zh.md new file mode 100644 index 00000000..2ad99502 --- /dev/null +++ b/docs/configuration/dns/server/index.zh.md @@ -0,0 +1,46 @@ +--- +icon: material/alert-decagram +--- + +!!! quote "sing-box 1.12.0 中的更改" + + :material-plus: [type](#type) + +# DNS Server + +### 结构 + +```json +{ + "dns": { + "servers": [ + { + "type": "", + "tag": "" + } + ] + } +} +``` + +#### type + +DNS 服务器的类型。 + +| 类型 | 格式 | +|-----------------|-----------------------------| +| empty (default) | [Legacy](./legacy/) | +| `tcp` | [TCP](./tcp/) | +| `udp` | [UDP](./udp/) | +| `tls` | [TLS](./tls/) | +| `https` | [HTTPS](./https/) | +| `quic` | [QUIC](./quic/) | +| `h3` | [HTTP/3](./http3/) | +| `predefined` | [Predefined](./predefined/) | +| `dhcp` | [DHCP](./dhcp/) | +| `fakeip` | [Fake IP](./fakeip/) | +| `tailscale` | [Tailscale](./tailscale/) | + +#### tag + +DNS 服务器的标签。 diff --git a/docs/configuration/endpoint/index.md b/docs/configuration/endpoint/index.md index e40333db..5e98c4cc 100644 --- a/docs/configuration/endpoint/index.md +++ b/docs/configuration/endpoint/index.md @@ -26,6 +26,7 @@ Endpoint is protocols that has both inbound and outbound behavior. | Type | Format | |-------------|---------------------------| | `wireguard` | [WireGuard](./wireguard/) | +| `tailscale` | [Tailscale](./tailscale/) | #### tag diff --git a/docs/configuration/endpoint/index.zh.md b/docs/configuration/endpoint/index.zh.md index 69ba2d09..3060a6e6 100644 --- a/docs/configuration/endpoint/index.zh.md +++ b/docs/configuration/endpoint/index.zh.md @@ -23,9 +23,10 @@ icon: material/new-box ### 字段 -| 类型 | 格式 | +| 类型 | 格式 | |-------------|---------------------------| -| `wireguard` | [WireGuard](./wiregaurd/) | +| `wireguard` | [WireGuard](./wiregaurd/) | +| `tailscale` | [Tailscale](./tailscale/) | #### tag diff --git a/docs/configuration/endpoint/wireguard.zh.md b/docs/configuration/endpoint/wireguard.zh.md index 918e7cbf..f80c82b4 100644 --- a/docs/configuration/endpoint/wireguard.zh.md +++ b/docs/configuration/endpoint/wireguard.zh.md @@ -41,7 +41,7 @@ icon: material/new-box ### 字段 -#### system_interface +#### system 使用系统设备。 diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 05e6a87d..dc5d0637 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -9,6 +9,7 @@ sing-box uses JSON for configuration files. "log": {}, "dns": {}, "ntp": {}, + "certificate": {}, "endpoints": [], "inbounds": [], "outbounds": [], @@ -24,6 +25,7 @@ sing-box uses JSON for configuration files. | `log` | [Log](./log/) | | `dns` | [DNS](./dns/) | | `ntp` | [NTP](./ntp/) | +| `certificate` | [Certificate](./certificate/) | | `endpoints` | [Endpoint](./endpoint/) | | `inbounds` | [Inbound](./inbound/) | | `outbounds` | [Outbound](./outbound/) | diff --git a/docs/configuration/index.zh.md b/docs/configuration/index.zh.md index b639c72a..a18cefd8 100644 --- a/docs/configuration/index.zh.md +++ b/docs/configuration/index.zh.md @@ -9,6 +9,7 @@ sing-box 使用 JSON 作为配置文件格式。 "log": {}, "dns": {}, "ntp": {}, + "certificate": {}, "endpoints": [], "inbounds": [], "outbounds": [], @@ -24,6 +25,7 @@ sing-box 使用 JSON 作为配置文件格式。 | `log` | [日志](./log/) | | `dns` | [DNS](./dns/) | | `ntp` | [NTP](./ntp/) | +| `certificate` | [证书](./certificate/) | | `endpoints` | [端点](./endpoint/) | | `inbounds` | [入站](./inbound/) | | `outbounds` | [出站](./outbound/) | From c42598c5a3adffb1bc696c916e94b3dcdf80adb5 Mon Sep 17 00:00:00 2001 From: anytls <198425817+anytls@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:40:12 +0800 Subject: [PATCH 41/94] Add MinIdleSession option to AnyTLS outbound Co-authored-by: anytls --- docs/configuration/outbound/anytls.md | 5 +++++ docs/configuration/outbound/anytls.zh.md | 5 +++++ go.mod | 2 +- go.sum | 6 ++---- option/anytls.go | 1 + protocol/anytls/outbound.go | 1 + 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/configuration/outbound/anytls.md b/docs/configuration/outbound/anytls.md index d5eb8fdf..4b3e0371 100644 --- a/docs/configuration/outbound/anytls.md +++ b/docs/configuration/outbound/anytls.md @@ -16,6 +16,7 @@ icon: material/new-box "password": "8JCsPssfgS8tiRwiMlhARg==", "idle_session_check_interval": "30s", "idle_session_timeout": "30s", + "min_idle_session": 5, "tls": {}, ... // Dial Fields @@ -50,6 +51,10 @@ Interval checking for idle sessions. Default: 30s. In the check, close sessions that have been idle for longer than this. Default: 30s. +#### min_idle_session + +In the check, at least the first `n` idle sessions are kept open. Default value: `n`=0 + #### tls ==Required== diff --git a/docs/configuration/outbound/anytls.zh.md b/docs/configuration/outbound/anytls.zh.md index 1d0828b1..1c888cfd 100644 --- a/docs/configuration/outbound/anytls.zh.md +++ b/docs/configuration/outbound/anytls.zh.md @@ -16,6 +16,7 @@ icon: material/new-box "password": "8JCsPssfgS8tiRwiMlhARg==", "idle_session_check_interval": "30s", "idle_session_timeout": "30s", + "min_idle_session": 5, "tls": {}, ... // 拨号字段 @@ -50,6 +51,10 @@ AnyTLS 密码。 在检查中,关闭闲置时间超过此值的会话。默认值:30秒。 +#### min_idle_session + +在检查中,至少前 `n` 个空闲会话保持打开状态。默认值:`n`=0 + #### tls ==必填== diff --git a/go.mod b/go.mod index 09866a55..721ea5b8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/sagernet/sing-box go 1.23.1 require ( - github.com/anytls/sing-anytls v0.0.2 + github.com/anytls/sing-anytls v0.0.3 github.com/caddyserver/certmagic v0.21.7 github.com/cloudflare/circl v1.6.0 github.com/cretz/bine v0.2.0 diff --git a/go.sum b/go.sum index 93c45aa1..045ef40c 100644 --- a/go.sum +++ b/go.sum @@ -8,10 +8,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/anytls/sing-anytls v0.0.1 h1:Hex6GFUcgATWMWL2E9YgH/7oPgwdokiIF09UQi5BEC0= -github.com/anytls/sing-anytls v0.0.1/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= -github.com/anytls/sing-anytls v0.0.2 h1:25azSh0o/LMcIkhS4ZutgRTIGwh8O3wuOhsThVM9K9o= -github.com/anytls/sing-anytls v0.0.2/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/anytls/sing-anytls v0.0.3 h1:JAQpETCUiZ1LFAPnO8vyJsdxypvKB4zinXwy9KiqWeQ= +github.com/anytls/sing-anytls v0.0.3/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= diff --git a/option/anytls.go b/option/anytls.go index 0ac19cd1..0f785263 100644 --- a/option/anytls.go +++ b/option/anytls.go @@ -21,4 +21,5 @@ type AnyTLSOutboundOptions struct { Password string `json:"password,omitempty"` IdleSessionCheckInterval badoption.Duration `json:"idle_session_check_interval,omitempty"` IdleSessionTimeout badoption.Duration `json:"idle_session_timeout,omitempty"` + MinIdleSession int `json:"min_idle_session,omitempty"` } diff --git a/protocol/anytls/outbound.go b/protocol/anytls/outbound.go index 3a016d2e..ce143bbe 100644 --- a/protocol/anytls/outbound.go +++ b/protocol/anytls/outbound.go @@ -63,6 +63,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL Password: options.Password, IdleSessionCheckInterval: options.IdleSessionCheckInterval.Build(), IdleSessionTimeout: options.IdleSessionTimeout.Build(), + MinIdleSession: options.MinIdleSession, DialOut: outbound.dialOut, Logger: logger, }) From b953dad23f2353c731d81df241f3aaa62d2ac7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 24 Feb 2025 18:17:11 +0800 Subject: [PATCH 42/94] documentation: Fix missing hosts DNS server --- dns/transport/hosts/hosts.go | 4 +- docs/configuration/dns/server/hosts.md | 70 ++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 docs/configuration/dns/server/hosts.md diff --git a/dns/transport/hosts/hosts.go b/dns/transport/hosts/hosts.go index 29f6778a..773a1d2a 100644 --- a/dns/transport/hosts/hosts.go +++ b/dns/transport/hosts/hosts.go @@ -2,12 +2,14 @@ package hosts import ( "context" + "os" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/service/filemanager" mDNS "github.com/miekg/dns" ) @@ -29,7 +31,7 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt files = append(files, NewFile(DefaultPath)) } else { for _, path := range options.Path { - files = append(files, NewFile(path)) + files = append(files, NewFile(filemanager.BasePath(ctx, os.ExpandEnv(path)))) } } return &Transport{ diff --git a/docs/configuration/dns/server/hosts.md b/docs/configuration/dns/server/hosts.md new file mode 100644 index 00000000..25c490ed --- /dev/null +++ b/docs/configuration/dns/server/hosts.md @@ -0,0 +1,70 @@ +--- +icon: material/new-box +--- + +!!! question "Since sing-box 1.12.0" + +# Hosts + +### Structure + +```json +{ + "dns": { + "servers": [ + { + "type": "hosts", + "tag": "", + + "path": [], + "predefined": {} + } + ] + } +} +``` + +!!! note "" + + You can ignore the JSON Array [] tag when the content is only one item + +### Fields + +#### path + +List of paths to hosts files. + +`/etc/hosts` is used by default. + +`C:\Windows\System32\Drivers\etc\hosts` is used by default on Windows. + +Example: + +```json +{ + // "path": "/etc/hosts" + + "path": [ + "/etc/hosts", + "$HOME/.hosts" + ] +} +``` + +#### predefined + +Predefined hosts. + +Example: + +```json +{ + "predefined": { + "www.google.com": "127.0.0.1", + "localhost": [ + "127.0.0.1", + "::1" + ] + } +} +``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index f786ac84..1aaee8b1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -84,6 +84,7 @@ nav: - configuration/dns/server/index.md - Legacy: configuration/dns/server/legacy.md - Local: configuration/dns/server/local.md + - Hosts: configuration/dns/server/hosts.md - TCP: configuration/dns/server/tcp.md - UDP: configuration/dns/server/udp.md - TLS: configuration/dns/server/tls.md From 192f452e0f7b9f24b7710f14a29771e10af375de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 26 Feb 2025 07:13:18 +0800 Subject: [PATCH 43/94] Fix DNS fallback --- dns/transport/local/local.go | 3 +++ dns/transport/local/local_fallback.go | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index e4236275..4e05ff02 100644 --- a/dns/transport/local/local.go +++ b/dns/transport/local/local.go @@ -139,6 +139,9 @@ func (t *Transport) tryOneName(ctx context.Context, config *dnsConfig, fqdn stri } func (t *Transport) exchangeOne(ctx context.Context, server M.Socksaddr, question mDNS.Question, timeout time.Duration, useTCP, ad bool) (*mDNS.Msg, error) { + if server.Port == 0 { + server.Port = 53 + } var networks []string if useTCP { networks = []string{N.NetworkTCP} diff --git a/dns/transport/local/local_fallback.go b/dns/transport/local/local_fallback.go index 0a7cd9f0..cd2d198f 100644 --- a/dns/transport/local/local_fallback.go +++ b/dns/transport/local/local_fallback.go @@ -58,8 +58,12 @@ func (f *FallbackTransport) Start(stage adapter.StartStage) error { return nil } +func (f *FallbackTransport) Close() error { + return nil +} + func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { - if f.fallback { + if !f.fallback { return f.DNSTransport.Exchange(ctx, message) } question := message.Question[0] From 8e8211b3e8fa127341f77cf8457938993bc57924 Mon Sep 17 00:00:00 2001 From: TargetLocked <32962687+TargetLocked@users.noreply.github.com> Date: Wed, 26 Feb 2025 07:28:42 +0800 Subject: [PATCH 44/94] Fix parsing legacy DNS options --- option/dns.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/option/dns.go b/option/dns.go index f4418468..c4186240 100644 --- a/option/dns.go +++ b/option/dns.go @@ -87,7 +87,7 @@ func (o *NewDNSServerOptions) UnmarshalJSONContext(ctx context.Context, content } registry := service.FromContext[DNSTransportOptionsRegistry](ctx) if registry == nil { - return E.New("missing outbound options registry in context") + return E.New("missing DNS transport options registry in context") } var options any switch o.Type { @@ -102,7 +102,7 @@ func (o *NewDNSServerOptions) UnmarshalJSONContext(ctx context.Context, content return E.New("unknown transport type: ", o.Type) } } - err = badjson.UnmarshallExcludedContext(ctx, content, (*_Outbound)(o), options) + err = badjson.UnmarshallExcludedContext(ctx, content, (*_NewDNSServerOptions)(o), options) if err != nil { return err } @@ -178,12 +178,10 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { if !serverAddr.IsValid() { return E.New("invalid server address") } - remoteOptions.Server = serverAddr.Addr.String() + remoteOptions.Server = serverAddr.AddrString() if serverAddr.Port != 0 && serverAddr.Port != 53 { remoteOptions.ServerPort = serverAddr.Port } - remoteOptions.Server = serverAddr.AddrString() - remoteOptions.ServerPort = serverAddr.Port case C.DNSTypeTCP: o.Type = C.DNSTypeTCP o.Options = &remoteOptions @@ -191,19 +189,17 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { if !serverAddr.IsValid() { return E.New("invalid server address") } - remoteOptions.Server = serverAddr.Addr.String() + remoteOptions.Server = serverAddr.AddrString() if serverAddr.Port != 0 && serverAddr.Port != 53 { remoteOptions.ServerPort = serverAddr.Port } - remoteOptions.Server = serverAddr.AddrString() - remoteOptions.ServerPort = serverAddr.Port case C.DNSTypeTLS, C.DNSTypeQUIC: o.Type = serverType serverAddr := M.ParseSocksaddr(serverURL.Host) if !serverAddr.IsValid() { return E.New("invalid server address") } - remoteOptions.Server = serverAddr.Addr.String() + remoteOptions.Server = serverAddr.AddrString() if serverAddr.Port != 0 && serverAddr.Port != 853 { remoteOptions.ServerPort = serverAddr.Port } @@ -222,7 +218,7 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { if !serverAddr.IsValid() { return E.New("invalid server address") } - httpsOptions.Server = serverAddr.Addr.String() + httpsOptions.Server = serverAddr.AddrString() if serverAddr.Port != 0 && serverAddr.Port != 443 { httpsOptions.ServerPort = serverAddr.Port } From f670078ebfd0fed65141f8ef396c3a308fd6bcad Mon Sep 17 00:00:00 2001 From: Estel Date: Wed, 26 Feb 2025 07:29:17 +0800 Subject: [PATCH 45/94] documentation: Fix typo Signed-off-by: Estel --- docs/installation/package-manager.zh.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/installation/package-manager.zh.md b/docs/installation/package-manager.zh.md index 60905d1d..f942e924 100644 --- a/docs/installation/package-manager.zh.md +++ b/docs/installation/package-manager.zh.md @@ -63,7 +63,7 @@ curl -fsSL https://sing-box.app/install.sh | sh -s -- --version === ":material-linux: Linux" - | 类型 | 平台 | 链接 | 命令 | + | 类型 | 平台 | 命令 | 链接 | |----------|---------------|------------------------------|---------------------------------------------------------------------------------------------------------------| | AUR | Arch Linux | `? -S sing-box` | [![AUR package](https://repology.org/badge/version-for-repo/aur/sing-box.svg)][aur] | | nixpkgs | NixOS | `nix-env -iA nixos.sing-box` | [![nixpkgs unstable package](https://repology.org/badge/version-for-repo/nix_unstable/sing-box.svg)][nixpkgs] | @@ -73,13 +73,13 @@ curl -fsSL https://sing-box.app/install.sh | sh -s -- --version === ":material-apple: macOS" - | 类型 | 平台 | 链接 | 命令 | + | 类型 | 平台 | 命令 | 链接 | |----------|-------|-------------------------|------------------------------------------------------------------------------------------------| | Homebrew | macOS | `brew install sing-box` | [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/sing-box.svg)][brew] | === ":material-microsoft-windows: Windows" - | 类型 | 平台 | 链接 | 命令 | + | 类型 | 平台 | 命令 | 链接 | |------------|---------|---------------------------|-----------------------------------------------------------------------------------------------------| | Scoop | Windows | `scoop install sing-box` | [![Scoop package](https://repology.org/badge/version-for-repo/scoop/sing-box.svg)][scoop] | | Chocolatey | Windows | `choco install sing-box` | [![Chocolatey package](https://repology.org/badge/version-for-repo/chocolatey/sing-box.svg)][choco] | @@ -87,13 +87,13 @@ curl -fsSL https://sing-box.app/install.sh | sh -s -- --version === ":material-android: Android" - | 类型 | 平台 | 链接 | 命令 | + | 类型 | 平台 | 命令 | 链接 | |--------|---------|--------------------|----------------------------------------------------------------------------------------------| | Termux | Android | `pkg add sing-box` | [![Termux package](https://repology.org/badge/version-for-repo/termux/sing-box.svg)][termux] | === ":material-freebsd: FreeBSD" - | 类型 | 平台 | 链接 | 命令 | + | 类型 | 平台 | 命令 | 链接 | |------------|---------|------------------------|--------------------------------------------------------------------------------------------| | FreshPorts | FreeBSD | `pkg install sing-box` | [![FreeBSD port](https://repology.org/badge/version-for-repo/freebsd/sing-box.svg)][ports] | From 572285eef18c27324420e5a3838a8e1fe0111281 Mon Sep 17 00:00:00 2001 From: anytls <198425817+anytls@users.noreply.github.com> Date: Wed, 26 Feb 2025 07:29:43 +0800 Subject: [PATCH 46/94] Update sing-anytls Co-authored-by: anytls --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 721ea5b8..6d419dda 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/sagernet/sing-box go 1.23.1 require ( - github.com/anytls/sing-anytls v0.0.3 + github.com/anytls/sing-anytls v0.0.5 github.com/caddyserver/certmagic v0.21.7 github.com/cloudflare/circl v1.6.0 github.com/cretz/bine v0.2.0 diff --git a/go.sum b/go.sum index 045ef40c..09e6a264 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/anytls/sing-anytls v0.0.3 h1:JAQpETCUiZ1LFAPnO8vyJsdxypvKB4zinXwy9KiqWeQ= -github.com/anytls/sing-anytls v0.0.3/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/anytls/sing-anytls v0.0.5 h1:I1NIh3zKTSXThLG5UgjsOOT/x2DZJqjfBzjuP/wZlDk= +github.com/anytls/sing-anytls v0.0.5/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= From 0a6e6d773c865a019210cb484dcf7590cd16ed2b Mon Sep 17 00:00:00 2001 From: Zephyruso <176294927+Zephyruso@users.noreply.github.com> Date: Wed, 26 Feb 2025 07:30:01 +0800 Subject: [PATCH 47/94] Fix missing AnyTLS display name --- constant/proxy.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/constant/proxy.go b/constant/proxy.go index 787a1243..1044428b 100644 --- a/constant/proxy.go +++ b/constant/proxy.go @@ -78,6 +78,8 @@ func ProxyDisplayName(proxyType string) string { return "TUIC" case TypeHysteria2: return "Hysteria2" + case TypeAnyTLS: + return "AnyTLS" case TypeSelector: return "Selector" case TypeURLTest: From 13bffd4819b39651d428e7d4ae177fe47cf9e51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 26 Feb 2025 08:00:09 +0800 Subject: [PATCH 48/94] Fix domain resolver on direct outbound --- common/dialer/default.go | 2 ++ protocol/direct/outbound.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/dialer/default.go b/common/dialer/default.go index 0bc956ec..8877e9f3 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -210,6 +210,8 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) { if !address.IsValid() { return nil, E.New("invalid address") + } else if address.IsFqdn() { + return nil, E.New("domain not resolved") } if d.networkStrategy == nil { switch N.NetworkName(network) { diff --git a/protocol/direct/outbound.go b/protocol/direct/outbound.go index 9b454f58..7ad756f2 100644 --- a/protocol/direct/outbound.go +++ b/protocol/direct/outbound.go @@ -45,7 +45,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if options.Detour != "" { return nil, E.New("`detour` is not supported in direct context") } - outboundDialer, err := dialer.New(ctx, options.DialerOptions, false) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, true) if err != nil { return nil, err } From 55e1fa8a15b5f780dcb01348429c7d31ac7cd42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 26 Feb 2025 08:59:21 +0800 Subject: [PATCH 49/94] Move predefined DNS server to rule action --- common/dialer/resolve.go | 29 ++++--- constant/dns.go | 26 +++--- constant/rule.go | 1 + dns/router.go | 32 +++++++ dns/transport/predefined.go | 83 ------------------ docs/configuration/dns/rule_action.md | 60 ++++++++++++- docs/configuration/dns/rule_action.zh.md | 64 +++++++++++++- docs/configuration/dns/server/hosts.md | 28 ++++++- docs/configuration/dns/server/predefined.md | 93 --------------------- include/registry.go | 1 - mkdocs.yml | 1 - option/dns.go | 51 +++++++++-- option/dns_record.go | 61 +------------- option/rule_action.go | 12 +++ route/rule/rule_action.go | 29 +++++++ 15 files changed, 289 insertions(+), 282 deletions(-) delete mode 100644 dns/transport/predefined.go delete mode 100644 docs/configuration/dns/server/predefined.md diff --git a/common/dialer/resolve.go b/common/dialer/resolve.go index 66b74e3c..49ed0703 100644 --- a/common/dialer/resolve.go +++ b/common/dialer/resolve.go @@ -44,6 +44,20 @@ type resolveDialer struct { } func NewResolveDialer(ctx context.Context, dialer N.Dialer, parallel bool, server string, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ResolveDialer { + if parallelDialer, isParallel := dialer.(ParallelInterfaceDialer); isParallel { + return &resolveParallelNetworkDialer{ + resolveDialer{ + transport: service.FromContext[adapter.DNSTransportManager](ctx), + router: service.FromContext[adapter.DNSRouter](ctx), + dialer: dialer, + parallel: parallel, + server: server, + queryOptions: queryOptions, + fallbackDelay: fallbackDelay, + }, + parallelDialer, + } + } return &resolveDialer{ transport: service.FromContext[adapter.DNSTransportManager](ctx), router: service.FromContext[adapter.DNSRouter](ctx), @@ -60,21 +74,6 @@ type resolveParallelNetworkDialer struct { dialer ParallelInterfaceDialer } -func NewResolveParallelInterfaceDialer(ctx context.Context, dialer ParallelInterfaceDialer, parallel bool, server string, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ParallelInterfaceResolveDialer { - return &resolveParallelNetworkDialer{ - resolveDialer{ - transport: service.FromContext[adapter.DNSTransportManager](ctx), - router: service.FromContext[adapter.DNSRouter](ctx), - dialer: dialer, - parallel: parallel, - server: server, - queryOptions: queryOptions, - fallbackDelay: fallbackDelay, - }, - dialer, - } -} - func (d *resolveDialer) initialize() error { d.initOnce.Do(d.initServer) return d.initErr diff --git a/constant/dns.go b/constant/dns.go index 0a444ff0..99e1ec0e 100644 --- a/constant/dns.go +++ b/constant/dns.go @@ -15,19 +15,19 @@ const ( ) const ( - DNSTypeLegacy = "legacy" - DNSTypeUDP = "udp" - DNSTypeTCP = "tcp" - DNSTypeTLS = "tls" - DNSTypeHTTPS = "https" - DNSTypeQUIC = "quic" - DNSTypeHTTP3 = "h3" - DNSTypeHosts = "hosts" - DNSTypeLocal = "local" - DNSTypePreDefined = "predefined" - DNSTypeFakeIP = "fakeip" - DNSTypeDHCP = "dhcp" - DNSTypeTailscale = "tailscale" + DNSTypeLegacy = "legacy" + DNSTypeLegacyRcode = "legacy_rcode" + DNSTypeUDP = "udp" + DNSTypeTCP = "tcp" + DNSTypeTLS = "tls" + DNSTypeHTTPS = "https" + DNSTypeQUIC = "quic" + DNSTypeHTTP3 = "h3" + DNSTypeLocal = "local" + DNSTypeHosts = "hosts" + DNSTypeFakeIP = "fakeip" + DNSTypeDHCP = "dhcp" + DNSTypeTailscale = "tailscale" ) const ( diff --git a/constant/rule.go b/constant/rule.go index c4a77838..336c3b38 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -33,6 +33,7 @@ const ( RuleActionTypeHijackDNS = "hijack-dns" RuleActionTypeSniff = "sniff" RuleActionTypeResolve = "resolve" + RuleActionTypePredefined = "predefined" ) const ( diff --git a/dns/router.go b/dns/router.go index 5db5429a..cdff388e 100644 --- a/dns/router.go +++ b/dns/router.go @@ -190,6 +190,8 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, } case *R.RuleActionReject: return nil, currentRule, currentRuleIndex + case *R.RuleActionPredefined: + return nil, currentRule, currentRuleIndex } } } @@ -260,6 +262,21 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte case C.RuleActionRejectMethodDrop: return nil, tun.ErrDrop } + case *R.RuleActionPredefined: + return &mDNS.Msg{ + MsgHdr: mDNS.MsgHdr{ + Id: message.Id, + Response: true, + Authoritative: true, + RecursionDesired: true, + RecursionAvailable: true, + Rcode: action.Rcode, + }, + Question: message.Question, + Answer: action.Answer, + Ns: action.Ns, + Extra: action.Extra, + }, nil } } var responseCheck func(responseAddrs []netip.Addr) bool @@ -376,6 +393,20 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ case C.RuleActionRejectMethodDrop: return nil, tun.ErrDrop } + case *R.RuleActionPredefined: + if action.Rcode != mDNS.RcodeSuccess { + err = RcodeError(action.Rcode) + } else { + for _, answer := range action.Answer { + switch record := answer.(type) { + case *mDNS.A: + responseAddrs = append(responseAddrs, M.AddrFromIP(record.A)) + case *mDNS.AAAA: + responseAddrs = append(responseAddrs, M.AddrFromIP(record.AAAA)) + } + } + } + goto response } } var responseCheck func(responseAddrs []netip.Addr) bool @@ -395,6 +426,7 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ printResult() } } +response: printResult() if len(responseAddrs) > 0 { r.logger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(responseAddrs), " ")) diff --git a/dns/transport/predefined.go b/dns/transport/predefined.go deleted file mode 100644 index dbb78e5c..00000000 --- a/dns/transport/predefined.go +++ /dev/null @@ -1,83 +0,0 @@ -package transport - -import ( - "context" - - "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/dns" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing/common" - E "github.com/sagernet/sing/common/exceptions" - - mDNS "github.com/miekg/dns" -) - -var _ adapter.DNSTransport = (*PredefinedTransport)(nil) - -func RegisterPredefined(registry *dns.TransportRegistry) { - dns.RegisterTransport[option.PredefinedDNSServerOptions](registry, C.DNSTypePreDefined, NewPredefined) -} - -type PredefinedTransport struct { - dns.TransportAdapter - responses []*predefinedResponse -} - -type predefinedResponse struct { - questions []mDNS.Question - answer *mDNS.Msg -} - -func NewPredefined(ctx context.Context, logger log.ContextLogger, tag string, options option.PredefinedDNSServerOptions) (adapter.DNSTransport, error) { - var responses []*predefinedResponse - for _, response := range options.Responses { - questions, msg, err := response.Build() - if err != nil { - return nil, err - } - responses = append(responses, &predefinedResponse{ - questions: questions, - answer: msg, - }) - } - if len(responses) == 0 { - return nil, E.New("empty predefined responses") - } - return &PredefinedTransport{ - TransportAdapter: dns.NewTransportAdapter(C.DNSTypePreDefined, tag, nil), - responses: responses, - }, nil -} - -func (t *PredefinedTransport) Reset() { -} - -func (t *PredefinedTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { - for _, response := range t.responses { - for _, question := range response.questions { - if func() bool { - if question.Name == "" && question.Qtype == mDNS.TypeNone { - return true - } else if question.Name == "" { - return common.Any(message.Question, func(it mDNS.Question) bool { - return it.Qtype == question.Qtype - }) - } else if question.Qtype == mDNS.TypeNone { - return common.Any(message.Question, func(it mDNS.Question) bool { - return it.Name == question.Name - }) - } else { - return common.Contains(message.Question, question) - } - }() { - copyAnswer := *response.answer - copyAnswer.Id = message.Id - copyAnswer.Question = message.Question - return ©Answer, nil - } - } - } - return nil, dns.RcodeNameError -} diff --git a/docs/configuration/dns/rule_action.md b/docs/configuration/dns/rule_action.md index 33c283fa..62c81c32 100644 --- a/docs/configuration/dns/rule_action.md +++ b/docs/configuration/dns/rule_action.md @@ -4,7 +4,8 @@ icon: material/new-box !!! quote "Changes in sing-box 1.12.0" - :material-plus: [strategy](#strategy) + :material-plus: [strategy](#strategy) + :material-plus: [predefined](#predefined) !!! question "Since sing-box 1.11.0" @@ -31,6 +32,8 @@ Tag of target server. #### strategy +!!! question "Since sing-box 1.12.0" + Set domain strategy for this query. One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`. @@ -69,7 +72,7 @@ Will overrides `dns.client_subnet`. ```json { "action": "reject", - "method": "default", // default + "method": "", "no_drop": false } ``` @@ -81,8 +84,61 @@ Will overrides `dns.client_subnet`. - `default`: Reply with NXDOMAIN. - `drop`: Drop the request. +`default` will be used by default. + #### no_drop If not enabled, `method` will be temporarily overwritten to `drop` after 50 triggers in 30s. Not available when `method` is set to drop. + +### predefined + +!!! question "Since sing-box 1.12.0" + +```json +{ + "action": "predefined", + "rcode": "", + "answer": [], + "ns": [], + "extra": [] +} +``` + +`predefined` responds with predefined DNS records. + +#### rcode + +The response code. + +| Value | Value in the legacy rcode server | Description | +|------------|----------------------------------|-----------------| +| `NOERROR` | `success` | Ok | +| `FORMERR` | `format_error` | Bad request | +| `SERVFAIL` | `server_failure` | Server failure | +| `NXDOMAIN` | `name_error` | Not found | +| `NOTIMP` | `not_implemented` | Not implemented | +| `REFUSED` | `refused` | Refused | + +`NOERROR` will be used by default. + +#### answer + +List of text DNS record to respond as answers. + +Examples: + +| Record Type | Example | +|-------------|-------------------------------| +| `A` | `localhost. IN A 127.0.0.1` | +| `AAAA` | `localhost. IN AAAA ::1` | +| `TXT` | `localhost. IN TXT \"Hello\"` | + +#### ns + +List of text DNS record to respond as name servers. + +#### extra + +List of text DNS record to respond as extra records. diff --git a/docs/configuration/dns/rule_action.zh.md b/docs/configuration/dns/rule_action.zh.md index 03843ec5..7aa3da6d 100644 --- a/docs/configuration/dns/rule_action.zh.md +++ b/docs/configuration/dns/rule_action.zh.md @@ -4,7 +4,8 @@ icon: material/new-box !!! quote "sing-box 1.12.0 中的更改" - :material-plus: [strategy](#strategy) + :material-plus: [strategy](#strategy) + :material-plus: [predefined](#predefined) !!! question "自 sing-box 1.11.0 起" @@ -12,9 +13,9 @@ icon: material/new-box ```json { - "action": "route", // 默认 + "action": "route", + // 默认 "server": "", - "strategy": "", "disable_cache": false, "rewrite_ttl": null, @@ -32,6 +33,8 @@ icon: material/new-box #### strategy +!!! question "自 sing-box 1.12.0 起" + 为此查询设置域名策略。 可选项:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。 @@ -70,7 +73,7 @@ icon: material/new-box ```json { "action": "reject", - "method": "default", // default + "method": "", "no_drop": false } ``` @@ -82,8 +85,61 @@ icon: material/new-box - `default`: 返回 NXDOMAIN。 - `drop`: 丢弃请求。 +默认使用 `defualt`。 + #### no_drop 如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`。 当 `method` 设为 `drop` 时不可用。 + +### predefined + +!!! question "自 sing-box 1.12.0 起" + +```json +{ + "action": "predefined", + "rcode": "", + "answer": [], + "ns": [], + "extra": [] +} +``` + +`predefined` 以预定义的 DNS 记录响应。 + +#### rcode + +响应码。 + +| 值 | 旧 rcode DNS 服务器中的值 | 描述 | +|------------|--------------------|-----------------| +| `NOERROR` | `success` | Ok | +| `FORMERR` | `format_error` | Bad request | +| `SERVFAIL` | `server_failure` | Server failure | +| `NXDOMAIN` | `name_error` | Not found | +| `NOTIMP` | `not_implemented` | Not implemented | +| `REFUSED` | `refused` | Refused | + +默认使用 `NOERROR`。 + +#### answer + +用于作为回答响应的文本 DNS 记录列表。 + +例子: + +| 记录类型 | 例子 | +|--------|-------------------------------| +| `A` | `localhost. IN A 127.0.0.1` | +| `AAAA` | `localhost. IN AAAA ::1` | +| `TXT` | `localhost. IN TXT \"Hello\"` | + +#### ns + +用于作为名称服务器响应的文本 DNS 记录列表。 + +#### extra + +用于作为额外记录响应的文本 DNS 记录列表。 diff --git a/docs/configuration/dns/server/hosts.md b/docs/configuration/dns/server/hosts.md index 25c490ed..ce859cca 100644 --- a/docs/configuration/dns/server/hosts.md +++ b/docs/configuration/dns/server/hosts.md @@ -67,4 +67,30 @@ Example: ] } } -``` \ No newline at end of file +``` + +### Examples + +=== "Use hosts if available" + + ```json + { + "dns": { + "servers": [ + { + ... + }, + { + "type": "hosts", + "tag": "hosts" + } + ], + "rules": [ + { + "ip_accept_any": true, + "server": "hosts" + } + ] + } + } + ``` \ No newline at end of file diff --git a/docs/configuration/dns/server/predefined.md b/docs/configuration/dns/server/predefined.md deleted file mode 100644 index ac75d6bb..00000000 --- a/docs/configuration/dns/server/predefined.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -icon: material/new-box ---- - -!!! question "Since sing-box 1.12.0" - -# Predefined - -### Structure - -```json -{ - "dns": { - "servers": [ - { - "type": "predefined", - "tag": "", - "responses": [] - } - ] - } -} -``` - -### Fields - -#### responses - -==Required== - -List of [Response](#response-structure). - -### Response Structure - -```json -{ - "query": [], - "query_type": [], - "rcode": "", - "answer": [], - "ns": [], - "extra": [] -} -``` - -!!! note "" - - You can ignore the JSON Array [] tag when the content is only one item - -### Response Fields - -#### query - -List of domain name to match. - -#### query_type - -List of query type to match. - -#### rcode - -The response code. - -| Value | Value in the legacy rcode server | Description | -|------------|----------------------------------|-----------------| -| `NOERROR` | `success` | Ok | -| `FORMERR` | `format_error` | Bad request | -| `SERVFAIL` | `server_failure` | Server failure | -| `NXDOMAIN` | `name_error` | Not found | -| `NOTIMP` | `not_implemented` | Not implemented | -| `REFUSED` | `refused` | Refused | - -`NOERROR` will be used by default. - -#### answer - -List of text DNS record to respond as answers. - -Examples: - -| Record Type | Example | -|-------------|-------------------------------| -| `A` | `localhost. IN A 127.0.0.1` | -| `AAAA` | `localhost. IN AAAA ::1` | -| `TXT` | `localhost. IN TXT \"Hello\"` | - -#### ns - -List of text DNS record to respond as name servers. - -#### extra - -List of text DNS record to respond as extra records. diff --git a/include/registry.go b/include/registry.go index 87aea576..9be1f2b4 100644 --- a/include/registry.go +++ b/include/registry.go @@ -107,7 +107,6 @@ func DNSTransportRegistry() *dns.TransportRegistry { transport.RegisterUDP(registry) transport.RegisterTLS(registry) transport.RegisterHTTPS(registry) - transport.RegisterPredefined(registry) hosts.RegisterTransport(registry) local.RegisterTransport(registry) fakeip.RegisterTransport(registry) diff --git a/mkdocs.yml b/mkdocs.yml index 1aaee8b1..8ee45c94 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -91,7 +91,6 @@ nav: - QUIC: configuration/dns/server/quic.md - HTTPS: configuration/dns/server/https.md - HTTP3: configuration/dns/server/http3.md - - Predefined: configuration/dns/server/predefined.md - DHCP: configuration/dns/server/dhcp.md - FakeIP: configuration/dns/server/fakeip.md - Tailscale: configuration/dns/server/tailscale.md diff --git a/option/dns.go b/option/dns.go index c4186240..64dbea38 100644 --- a/option/dns.go +++ b/option/dns.go @@ -46,7 +46,46 @@ func (o *DNSOptions) UnmarshalJSONContext(ctx context.Context, content []byte) e } legacyOptions := o.LegacyDNSOptions o.LegacyDNSOptions = LegacyDNSOptions{} - return badjson.UnmarshallExcludedContext(ctx, content, legacyOptions, &o.RawDNSOptions) + err = badjson.UnmarshallExcludedContext(ctx, content, legacyOptions, &o.RawDNSOptions) + if err != nil { + return err + } + rcodeMap := make(map[string]int) + o.Servers = common.Filter(o.Servers, func(it NewDNSServerOptions) bool { + if it.Type == C.DNSTypeLegacyRcode { + rcodeMap[it.Tag] = it.Options.(int) + return false + } + return true + }) + if len(rcodeMap) > 0 { + for i := 0; i < len(o.Rules); i++ { + rewriteRcode(rcodeMap, &o.Rules[i]) + } + } + return nil +} + +func rewriteRcode(rcodeMap map[string]int, rule *DNSRule) { + switch rule.Type { + case C.RuleTypeDefault: + rewriteRcodeAction(rcodeMap, &rule.DefaultOptions.DNSRuleAction) + case C.RuleTypeLogical: + rewriteRcodeAction(rcodeMap, &rule.LogicalOptions.DNSRuleAction) + } +} + +func rewriteRcodeAction(rcodeMap map[string]int, ruleAction *DNSRuleAction) { + if ruleAction.Action != C.RuleActionTypeRoute { + return + } + rcode, loaded := rcodeMap[ruleAction.RouteOptions.Server] + if !loaded { + return + } + ruleAction.Action = C.RuleActionTypePredefined + ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode)) + return } type DNSClientOptions struct { @@ -243,14 +282,8 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { default: return E.New("unknown rcode: ", serverURL.Host) } - o.Type = C.DNSTypePreDefined - o.Options = &PredefinedDNSServerOptions{ - Responses: []DNSResponseOptions{ - { - RCode: common.Ptr(DNSRCode(rcode)), - }, - }, - } + o.Type = C.DNSTypeLegacyRcode + o.Options = rcode case C.DNSTypeDHCP: o.Type = C.DNSTypeDHCP dhcpOptions := DHCPDNSServerOptions{} diff --git a/option/dns_record.go b/option/dns_record.go index c76a76c6..fa72b61b 100644 --- a/option/dns_record.go +++ b/option/dns_record.go @@ -3,30 +3,14 @@ package option import ( "encoding/base64" - "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" - "github.com/sagernet/sing/common/json/badoption" M "github.com/sagernet/sing/common/metadata" "github.com/miekg/dns" ) -type PredefinedDNSServerOptions struct { - Responses []DNSResponseOptions `json:"responses,omitempty"` -} - -type DNSResponseOptions struct { - Query badoption.Listable[string] `json:"query,omitempty"` - QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"` - - RCode *DNSRCode `json:"rcode,omitempty"` - Answer badoption.Listable[DNSRecordOptions] `json:"answer,omitempty"` - Ns badoption.Listable[DNSRecordOptions] `json:"ns,omitempty"` - Extra badoption.Listable[DNSRecordOptions] `json:"extra,omitempty"` -} - type DNSRCode int func (r DNSRCode) MarshalJSON() ([]byte, error) { @@ -64,49 +48,6 @@ func (r *DNSRCode) Build() int { return int(*r) } -func (o DNSResponseOptions) Build() ([]dns.Question, *dns.Msg, error) { - var questions []dns.Question - if len(o.Query) == 0 && len(o.QueryType) == 0 { - questions = []dns.Question{{Qclass: dns.ClassINET}} - } else if len(o.Query) == 0 { - for _, queryType := range o.QueryType { - questions = append(questions, dns.Question{ - Qtype: uint16(queryType), - Qclass: dns.ClassINET, - }) - } - } else if len(o.QueryType) == 0 { - for _, domain := range o.Query { - questions = append(questions, dns.Question{ - Name: dns.Fqdn(domain), - Qclass: dns.ClassINET, - }) - } - } else { - for _, queryType := range o.QueryType { - for _, domain := range o.Query { - questions = append(questions, dns.Question{ - Name: dns.Fqdn(domain), - Qtype: uint16(queryType), - Qclass: dns.ClassINET, - }) - } - } - } - return questions, &dns.Msg{ - MsgHdr: dns.MsgHdr{ - Response: true, - Rcode: o.RCode.Build(), - Authoritative: true, - RecursionDesired: true, - RecursionAvailable: true, - }, - Answer: common.Map(o.Answer, DNSRecordOptions.build), - Ns: common.Map(o.Ns, DNSRecordOptions.build), - Extra: common.Map(o.Extra, DNSRecordOptions.build), - }, nil -} - type DNSRecordOptions struct { dns.RR fromBase64 bool @@ -156,6 +97,6 @@ func (o *DNSRecordOptions) unmarshalBase64(binary []byte) error { return nil } -func (o DNSRecordOptions) build() dns.RR { +func (o DNSRecordOptions) Build() dns.RR { return o.RR } diff --git a/option/rule_action.go b/option/rule_action.go index 00d3ae7a..7c05dce6 100644 --- a/option/rule_action.go +++ b/option/rule_action.go @@ -92,6 +92,7 @@ type _DNSRuleAction struct { RouteOptions DNSRouteActionOptions `json:"-"` RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"` RejectOptions RejectActionOptions `json:"-"` + PredefinedOptions DNSRouteActionPredefined `json:"-"` } type DNSRuleAction _DNSRuleAction @@ -109,6 +110,8 @@ func (r DNSRuleAction) MarshalJSON() ([]byte, error) { v = r.RouteOptionsOptions case C.RuleActionTypeReject: v = r.RejectOptions + case C.RuleActionTypePredefined: + v = r.PredefinedOptions default: return nil, E.New("unknown DNS rule action: " + r.Action) } @@ -129,6 +132,8 @@ func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) e v = &r.RouteOptionsOptions case C.RuleActionTypeReject: v = &r.RejectOptions + case C.RuleActionTypePredefined: + v = &r.PredefinedOptions default: return E.New("unknown DNS rule action: " + r.Action) } @@ -294,3 +299,10 @@ type RouteActionResolve struct { RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` } + +type DNSRouteActionPredefined struct { + Rcode *DNSRCode `json:"rcode,omitempty"` + Answer badoption.Listable[DNSRecordOptions] `json:"answer,omitempty"` + Ns badoption.Listable[DNSRecordOptions] `json:"ns,omitempty"` + Extra badoption.Listable[DNSRecordOptions] `json:"extra,omitempty"` +} diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index d76f0a90..f1a570d4 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -20,6 +20,8 @@ import ( "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + + "github.com/miekg/dns" ) func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action option.RuleAction) (adapter.RuleAction, error) { @@ -126,6 +128,13 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction) NoDrop: action.RejectOptions.NoDrop, logger: logger, } + case C.RuleActionTypePredefined: + return &RuleActionPredefined{ + Rcode: action.PredefinedOptions.Rcode.Build(), + Answer: common.Map(action.PredefinedOptions.Answer, option.DNSRecordOptions.Build), + Ns: common.Map(action.PredefinedOptions.Ns, option.DNSRecordOptions.Build), + Extra: common.Map(action.PredefinedOptions.Extra, option.DNSRecordOptions.Build), + } default: panic(F.ToString("unknown rule action: ", action.Action)) } @@ -416,3 +425,23 @@ func (r *RuleActionResolve) String() string { return F.ToString("resolve(", strings.Join(options, ","), ")") } } + +type RuleActionPredefined struct { + Rcode int + Answer []dns.RR + Ns []dns.RR + Extra []dns.RR +} + +func (r *RuleActionPredefined) Type() string { + return C.RuleActionTypePredefined +} + +func (r *RuleActionPredefined) String() string { + var options []string + options = append(options, dns.RcodeToString[r.Rcode]) + options = append(options, common.Map(r.Answer, dns.RR.String)...) + options = append(options, common.Map(r.Ns, dns.RR.String)...) + options = append(options, common.Map(r.Extra, dns.RR.String)...) + return F.ToString("predefined(", strings.Join(options, ","), ")") +} From 551db61d33b9554e7897826122d73bf09adb4beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 27 Feb 2025 09:40:20 +0800 Subject: [PATCH 50/94] Fix anytls dialer usage --- protocol/anytls/outbound.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocol/anytls/outbound.go b/protocol/anytls/outbound.go index ce143bbe..b026ed18 100644 --- a/protocol/anytls/outbound.go +++ b/protocol/anytls/outbound.go @@ -51,8 +51,9 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL outbound.tlsConfig = tlsConfig outboundDialer, err := dialer.NewWithOptions(dialer.Options{ - Context: ctx, - Options: options.DialerOptions, + Context: ctx, + Options: options.DialerOptions, + RemoteIsDomain: options.ServerIsDomain(), }) if err != nil { return nil, err From 74f6941c3cbb858e88463b7f48f474faa6ed2fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 6 Mar 2025 14:02:16 +0800 Subject: [PATCH 51/94] documentation: Fix missing `ip_accept_any` DNS rule option --- docs/configuration/dns/rule.md | 38 +++++++++++++------- docs/configuration/dns/rule.zh.md | 46 +++++++++++++++--------- docs/configuration/dns/rule_action.zh.md | 3 +- 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/docs/configuration/dns/rule.md b/docs/configuration/dns/rule.md index 3039bffc..5d5cb7c3 100644 --- a/docs/configuration/dns/rule.md +++ b/docs/configuration/dns/rule.md @@ -4,6 +4,7 @@ icon: material/alert-decagram !!! quote "Changes in sing-box 1.12.0" + :material-plus: [ip_accept_any](#ip_accept_any) :material-delete-clock: [outbound](#outbound) !!! quote "Changes in sing-box 1.11.0" @@ -77,15 +78,6 @@ icon: material/alert-decagram "domain_regex": [ "^stun\\..+" ], - "geosite": [ - "cn" - ], - "source_geoip": [ - "private" - ], - "geoip": [ - "cn" - ], "source_ip_cidr": [ "10.0.0.0/24", "192.168.0.1" @@ -96,6 +88,7 @@ icon: material/alert-decagram "192.168.0.1" ], "ip_is_private": false, + "ip_accept_any": false, "source_port": [ 12345 ], @@ -147,8 +140,6 @@ icon: material/alert-decagram "geoip-cn", "geosite-cn" ], - // deprecated - "rule_set_ipcidr_match_source": false, "rule_set_ip_cidr_match_source": false, "rule_set_ip_cidr_accept_empty": false, "invert": false, @@ -156,7 +147,20 @@ icon: material/alert-decagram "direct" ], "action": "route", - "server": "local" + "server": "local", + + // Deprecated + + "rule_set_ipcidr_match_source": false, + "geosite": [ + "cn" + ], + "source_geoip": [ + "private" + ], + "geoip": [ + "cn" + ] }, { "type": "logical", @@ -451,7 +455,9 @@ Only takes effect for address requests (A/AAAA/HTTPS). When the query results do #### geoip -!!! question "Since sing-box 1.9.0" +!!! failure "Removed in sing-box 1.12.0" + + GeoIP is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0, check [Migration](/migration/#migrate-geoip-to-rule-sets). Match GeoIP with query response. @@ -473,6 +479,12 @@ Match private IP with query response. Make `ip_cidr` rules in rule-sets accept empty query response. +#### ip_accept_any + +!!! question "Since sing-box 1.12.0" + +Match any IP with query response. + ### Logical Fields #### type diff --git a/docs/configuration/dns/rule.zh.md b/docs/configuration/dns/rule.zh.md index 94ca4535..8973eba2 100644 --- a/docs/configuration/dns/rule.zh.md +++ b/docs/configuration/dns/rule.zh.md @@ -4,6 +4,7 @@ icon: material/alert-decagram !!! quote "sing-box 1.12.0 中的更改" + :material-plus: [ip_accept_any](#ip_accept_any) :material-delete-clock: [outbound](#outbound) !!! quote "sing-box 1.11.0 中的更改" @@ -77,15 +78,6 @@ icon: material/alert-decagram "domain_regex": [ "^stun\\..+" ], - "geosite": [ - "cn" - ], - "source_geoip": [ - "private" - ], - "geoip": [ - "cn" - ], "source_ip_cidr": [ "10.0.0.0/24", "192.168.0.1" @@ -96,6 +88,7 @@ icon: material/alert-decagram "192.168.0.1" ], "ip_is_private": false, + "ip_accept_any": false, "source_port": [ 12345 ], @@ -147,8 +140,6 @@ icon: material/alert-decagram "geoip-cn", "geosite-cn" ], - // 已弃用 - "rule_set_ipcidr_match_source": false, "rule_set_ip_cidr_match_source": false, "rule_set_ip_cidr_accept_empty": false, "invert": false, @@ -156,7 +147,19 @@ icon: material/alert-decagram "direct" ], "action": "route", - "server": "local" + "server": "local", + + // 已弃用 + "rule_set_ipcidr_match_source": false, + "geosite": [ + "cn" + ], + "source_geoip": [ + "private" + ], + "geoip": [ + "cn" + ] }, { "type": "logical", @@ -232,17 +235,17 @@ DNS 查询类型。值可以为整数或者类型名称字符串。 #### geosite -!!! failure "已在 sing-box 1.8.0 废弃" +!!! failure "已在 sing-box 1.12.0 中被移除" - Geosite 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geosite)。 + GeoSite 已在 sing-box 1.8.0 废弃且在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geosite)。 匹配 Geosite。 #### source_geoip -!!! failure "已在 sing-box 1.8.0 废弃" +!!! failure "已在 sing-box 1.12.0 中被移除" - GeoIP 已废弃且可能在不久的将来移除,参阅 [迁移指南](/zh/migration/#geoip)。 + GeoIP 已在 sing-box 1.8.0 废弃且在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。 匹配源 GeoIP。 @@ -451,7 +454,10 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`. #### geoip -!!! question "自 sing-box 1.9.0 起" +!!! failure "已在 sing-box 1.12.0 中被移除" + + GeoIP 已在 sing-box 1.8.0 废弃且在 sing-box 1.12.0 中被移除,参阅 [迁移指南](/zh/migration/#geoip)。 + 与查询响应匹配 GeoIP。 @@ -467,6 +473,12 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`. 与查询响应匹配非公开 IP。 +#### ip_accept_any + +!!! question "自 sing-box 1.12.0 起" + +匹配任意 IP。 + #### rule_set_ip_cidr_accept_empty !!! question "自 sing-box 1.10.0 起" diff --git a/docs/configuration/dns/rule_action.zh.md b/docs/configuration/dns/rule_action.zh.md index 7aa3da6d..c2921ace 100644 --- a/docs/configuration/dns/rule_action.zh.md +++ b/docs/configuration/dns/rule_action.zh.md @@ -13,8 +13,7 @@ icon: material/new-box ```json { - "action": "route", - // 默认 + "action": "route", // 默认 "server": "", "strategy": "", "disable_cache": false, From 62a96910164dae801d2c2cceb9cfe491dadce458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 6 Mar 2025 14:06:55 +0800 Subject: [PATCH 52/94] Fix UDP DNS server crash --- dns/transport/udp.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dns/transport/udp.go b/dns/transport/udp.go index 5099c6f6..289b1fe2 100644 --- a/dns/transport/udp.go +++ b/dns/transport/udp.go @@ -110,13 +110,6 @@ func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M conn.access.Lock() delete(conn.callbacks, messageId) conn.access.Unlock() - callback.access.Lock() - select { - case <-callback.done: - default: - close(callback.done) - } - callback.access.Unlock() }() rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes()) if err != nil { From 2288a080de50ecc9953ee8f05e11bf1ac101cf8c Mon Sep 17 00:00:00 2001 From: k9982874 Date: Mon, 10 Mar 2025 13:57:59 +0800 Subject: [PATCH 53/94] Fix hosts DNS server --- dns/client.go | 4 ++-- dns/transport/hosts/hosts.go | 20 +++++++++++++++++--- dns/transport/hosts/hosts_file.go | 2 +- dns/transport/hosts/hosts_test.go | 4 ++-- option/dns.go | 4 ++-- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/dns/client.go b/dns/client.go index d44883b5..f456f878 100644 --- a/dns/client.go +++ b/dns/client.go @@ -537,7 +537,7 @@ func FixedResponse(id uint16, question dns.Question, addresses []netip.Addr, tim Question: []dns.Question{question}, } for _, address := range addresses { - if address.Is4() { + if address.Is4() && question.Qtype == dns.TypeA { response.Answer = append(response.Answer, &dns.A{ Hdr: dns.RR_Header{ Name: question.Name, @@ -547,7 +547,7 @@ func FixedResponse(id uint16, question dns.Question, addresses []netip.Addr, tim }, A: address.AsSlice(), }) - } else { + } else if address.Is6() && question.Qtype == dns.TypeAAAA { response.Answer = append(response.Answer, &dns.AAAA{ Hdr: dns.RR_Header{ Name: question.Name, diff --git a/dns/transport/hosts/hosts.go b/dns/transport/hosts/hosts.go index 773a1d2a..0a1dd395 100644 --- a/dns/transport/hosts/hosts.go +++ b/dns/transport/hosts/hosts.go @@ -2,6 +2,7 @@ package hosts import ( "context" + "net/netip" "os" "github.com/sagernet/sing-box/adapter" @@ -22,11 +23,15 @@ var _ adapter.DNSTransport = (*Transport)(nil) type Transport struct { dns.TransportAdapter - files []*File + files []*File + predefined map[string][]netip.Addr } func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.HostsDNSServerOptions) (adapter.DNSTransport, error) { - var files []*File + var ( + files []*File + predefined = make(map[string][]netip.Addr) + ) if len(options.Path) == 0 { files = append(files, NewFile(DefaultPath)) } else { @@ -34,9 +39,15 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt files = append(files, NewFile(filemanager.BasePath(ctx, os.ExpandEnv(path)))) } } + if options.Predefined != nil { + for _, entry := range options.Predefined.Entries() { + predefined[mDNS.CanonicalName(entry.Key)] = entry.Value + } + } return &Transport{ TransportAdapter: dns.NewTransportAdapter(C.DNSTypeHosts, tag, nil), files: files, + predefined: predefined, }, nil } @@ -45,8 +56,11 @@ func (t *Transport) Reset() { func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { question := message.Question[0] - domain := dns.FqdnToDomain(question.Name) + domain := mDNS.CanonicalName(question.Name) if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA { + if addresses, ok := t.predefined[domain]; ok { + return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil + } for _, file := range t.files { addresses := file.Lookup(domain) if len(addresses) > 0 { diff --git a/dns/transport/hosts/hosts_file.go b/dns/transport/hosts/hosts_file.go index 7ff34f69..ec384882 100644 --- a/dns/transport/hosts/hosts_file.go +++ b/dns/transport/hosts/hosts_file.go @@ -34,7 +34,7 @@ func (f *File) Lookup(name string) []netip.Addr { f.access.Lock() defer f.access.Unlock() f.update() - return f.byName[name] + return f.byName[dns.CanonicalName(name)] } func (f *File) update() { diff --git a/dns/transport/hosts/hosts_test.go b/dns/transport/hosts/hosts_test.go index 944aa437..3ae160b7 100644 --- a/dns/transport/hosts/hosts_test.go +++ b/dns/transport/hosts/hosts_test.go @@ -11,6 +11,6 @@ import ( func TestHosts(t *testing.T) { t.Parallel() - require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, hosts.NewFile("testdata/hosts").Lookup("localhost.")) - require.NotEmpty(t, hosts.NewFile(hosts.DefaultPath).Lookup("localhost.")) + require.Equal(t, []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1}), netip.IPv6Loopback()}, hosts.NewFile("testdata/hosts").Lookup("localhost")) + require.NotEmpty(t, hosts.NewFile(hosts.DefaultPath).Lookup("localhost")) } diff --git a/option/dns.go b/option/dns.go index 64dbea38..04eb442d 100644 --- a/option/dns.go +++ b/option/dns.go @@ -316,8 +316,8 @@ type LegacyDNSServerOptions struct { } type HostsDNSServerOptions struct { - Path badoption.Listable[string] `json:"path,omitempty"` - Predefined badjson.TypedMap[string, badoption.Listable[netip.Addr]] `json:"predefined,omitempty"` + Path badoption.Listable[string] `json:"path,omitempty"` + Predefined *badjson.TypedMap[string, badoption.Listable[netip.Addr]] `json:"predefined,omitempty"` } type LocalDNSServerOptions struct { From aa6a6fd002f79869f5adf4f5429f083cdf43e92d Mon Sep 17 00:00:00 2001 From: anytls <198425817+anytls@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:03:22 +0800 Subject: [PATCH 54/94] Update sing-anytls Co-authored-by: anytls --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6d419dda..4c9682ac 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/sagernet/sing-box go 1.23.1 require ( - github.com/anytls/sing-anytls v0.0.5 + github.com/anytls/sing-anytls v0.0.6 github.com/caddyserver/certmagic v0.21.7 github.com/cloudflare/circl v1.6.0 github.com/cretz/bine v0.2.0 diff --git a/go.sum b/go.sum index 09e6a264..9bada1f8 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/anytls/sing-anytls v0.0.5 h1:I1NIh3zKTSXThLG5UgjsOOT/x2DZJqjfBzjuP/wZlDk= -github.com/anytls/sing-anytls v0.0.5/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/anytls/sing-anytls v0.0.6 h1:UatIjl/OvzWQGXQ1I2bAIkabL9WtihW0fA7G+DXGBUg= +github.com/anytls/sing-anytls v0.0.6/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= From 8f1e2566daf5cae7b05f63981a5b999f97024475 Mon Sep 17 00:00:00 2001 From: Restia-Ashbell <107416976+Restia-Ashbell@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:17:06 +0800 Subject: [PATCH 55/94] documentation: Fix typo --- docs/migration.md | 4 ++-- docs/migration.zh.md | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/migration.md b/docs/migration.md index 62e09e71..895f48f1 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -567,7 +567,7 @@ The legacy outbound DNS rules are deprecated and can be replaced by new domain r "server_port": 2080, "domain_resolver": { "server": "local", - "rewrite_tll": 60, + "rewrite_ttl": 60, "client_subnet": "1.1.1.1" }, // or "domain_resolver": "local", @@ -579,7 +579,7 @@ The legacy outbound DNS rules are deprecated and can be replaced by new domain r "route": { "default_domain_resolver": { "server": "local", - "rewrite_tll": 60, + "rewrite_ttl": 60, "client_subnet": "1.1.1.1" } } diff --git a/docs/migration.zh.md b/docs/migration.zh.md index 12044669..f82f2521 100644 --- a/docs/migration.zh.md +++ b/docs/migration.zh.md @@ -565,13 +565,21 @@ DNS 服务器已经重构。 "type": "socks", "server": "example.org", "server_port": 2080, - "domain_resolver": "local", + "domain_resolver": { + "server": "local", + "rewrite_ttl": 60, + "client_subnet": "1.1.1.1" + }, + // 或 "domain_resolver": "local", } ], + + // 或 + "route": { "default_domain_resolver": { "server": "local", - "rewrite_tll": 60, + "rewrite_ttl": 60, "client_subnet": "1.1.1.1" } } From 9afdd2d0fbf7185b8e22683c78fee417d61a9312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 13 Mar 2025 19:47:29 +0800 Subject: [PATCH 56/94] Fix http3 DNS server connecting to wrong address --- dns/transport/quic/http3.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dns/transport/quic/http3.go b/dns/transport/quic/http3.go index a5181ae0..73ae7c13 100644 --- a/dns/transport/quic/http3.go +++ b/dns/transport/quic/http3.go @@ -23,7 +23,6 @@ import ( "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/logger" - M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" sHTTP "github.com/sagernet/sing/protocol/http" @@ -101,8 +100,7 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options headers: headers, transport: &http3.Transport{ Dial: func(ctx context.Context, addr string, tlsCfg *tls.STDConfig, cfg *quic.Config) (quic.EarlyConnection, error) { - destinationAddr := M.ParseSocksaddr(addr) - conn, dialErr := transportDialer.DialContext(ctx, N.NetworkUDP, destinationAddr) + conn, dialErr := transportDialer.DialContext(ctx, N.NetworkUDP, serverAddr) if dialErr != nil { return nil, dialErr } From 724d74e6ae2642fa9e2c6d0bd28f1f03437c7a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 13 Mar 2025 19:52:50 +0800 Subject: [PATCH 57/94] Fix DNS lookup context pollution --- dns/router.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dns/router.go b/dns/router.go index cdff388e..bd4eb3f1 100644 --- a/dns/router.go +++ b/dns/router.go @@ -383,7 +383,8 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ ruleIndex = -1 for { dnsCtx := adapter.OverrideContext(ctx) - transport, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true, &options) + dnsOptions := options + transport, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true, &dnsOptions) if rule != nil { switch action := rule.Action().(type) { case *R.RuleActionReject: @@ -416,10 +417,10 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ return rule.MatchAddressLimit(metadata) } } - if options.Strategy == C.DomainStrategyAsIS { - options.Strategy = r.defaultDomainStrategy + if dnsOptions.Strategy == C.DomainStrategyAsIS { + dnsOptions.Strategy = r.defaultDomainStrategy } - responseAddrs, err = r.client.Lookup(dnsCtx, transport, domain, options, responseCheck) + responseAddrs, err = r.client.Lookup(dnsCtx, transport, domain, dnsOptions, responseCheck) if responseCheck == nil || err == nil { break } From f40e9e73063c2892bba079998e16d544cc94e3cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 15 Mar 2025 10:12:10 +0800 Subject: [PATCH 58/94] Make `domain_resolver` optional when only one DNS server is configured --- common/dialer/dialer.go | 7 ++++++- docs/configuration/shared/dial.md | 4 ++++ docs/configuration/shared/dial.zh.md | 4 ++++ option/outbound.go | 9 ++++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index 0efc7e9d..00ebbeac 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -104,7 +104,12 @@ func NewWithOptions(options Options) (N.Dialer, error) { } else if options.NewDialer { return nil, E.New("missing domain resolver for domain server address") } else { - deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) + transports := dnsTransport.Transports() + if len(transports) < 2 { + dnsQueryOptions.Transport = dnsTransport.Default() + } else { + deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) + } } dialer = NewResolveDialer( options.Context, diff --git a/docs/configuration/shared/dial.md b/docs/configuration/shared/dial.md index e852d57b..7f0bf251 100644 --- a/docs/configuration/shared/dial.md +++ b/docs/configuration/shared/dial.md @@ -106,6 +106,10 @@ Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". `outbound` DNS rule items are deprecated and will be removed in sing-box 1.14.0, so this item will be required for outbound/endpoints using domain name in server address since sing-box 1.14.0. +!!! info "" + + `domain_resolver` or `route.default_domain_resolver` is optional when only one DNS server is configured. + Set domain resolver to use for resolving domain names. This option uses the same format as the [route DNS rule action](/configuration/dns/rule_action/#route) without the `action` field. diff --git a/docs/configuration/shared/dial.zh.md b/docs/configuration/shared/dial.zh.md index 66e1436e..4a7d9562 100644 --- a/docs/configuration/shared/dial.zh.md +++ b/docs/configuration/shared/dial.zh.md @@ -105,6 +105,10 @@ icon: material/new-box `outbound` DNS 规则项已弃用,且将在 sing-box 1.14.0 中被移除。因此,从 sing-box 1.14.0 版本开始,所有在服务器地址中使用域名的出站/端点均需配置此项。 +!!! info "" + + 当只有一个 DNS 服务器已配置时,`domain_resolver` 或 `route.default_domain_resolver` 是可选的。 + 用于设置解析域名的域名解析器。 此选项的格式与 [路由 DNS 规则动作](/configuration/dns/rule_action/#route) 相同,但不包含 `action` 字段。 diff --git a/option/outbound.go b/option/outbound.go index 99e3361a..30ed4e79 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -116,7 +116,14 @@ func (o *DomainResolveOptions) UnmarshalJSON(bytes []byte) error { o.Server = stringValue return nil } - return json.Unmarshal(bytes, (*_DomainResolveOptions)(o)) + err = json.Unmarshal(bytes, (*_DomainResolveOptions)(o)) + if err != nil { + return err + } + if o.Server == "" { + return E.New("empty domain_resolver.server") + } + return nil } func (o *DialerOptions) TakeDialerOptions() DialerOptions { From a6e9d900c8c772b458f4e831d1c9d207cc019052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 15 Mar 2025 15:58:12 +0800 Subject: [PATCH 59/94] option: Fix marshal legacy DNS options --- dns/transport/https.go | 2 +- dns/transport/quic/http3.go | 2 +- dns/transport/quic/quic.go | 2 +- dns/transport/tcp.go | 2 +- dns/transport/tls.go | 2 +- dns/transport/udp.go | 2 +- option/dns.go | 100 +++++++++++++++++++++++++----------- option/outbound.go | 4 +- 8 files changed, 79 insertions(+), 37 deletions(-) diff --git a/dns/transport/https.go b/dns/transport/https.go index 1cfb2574..bd150d5f 100644 --- a/dns/transport/https.go +++ b/dns/transport/https.go @@ -91,7 +91,7 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options if err != nil { return nil, err } - serverAddr := options.ServerOptions.Build() + serverAddr := options.DNSServerAddressOptions.Build() if serverAddr.Port == 0 { serverAddr.Port = 443 } diff --git a/dns/transport/quic/http3.go b/dns/transport/quic/http3.go index 73ae7c13..e2a75b50 100644 --- a/dns/transport/quic/http3.go +++ b/dns/transport/quic/http3.go @@ -88,7 +88,7 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options if err != nil { return nil, err } - serverAddr := options.ServerOptions.Build() + serverAddr := options.DNSServerAddressOptions.Build() if serverAddr.Port == 0 { serverAddr.Port = 443 } diff --git a/dns/transport/quic/quic.go b/dns/transport/quic/quic.go index d3844c2b..4ae9ac16 100644 --- a/dns/transport/quic/quic.go +++ b/dns/transport/quic/quic.go @@ -54,7 +54,7 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options if len(tlsConfig.NextProtos()) == 0 { tlsConfig.SetNextProtos([]string{"doq"}) } - serverAddr := options.ServerOptions.Build() + serverAddr := options.DNSServerAddressOptions.Build() if serverAddr.Port == 0 { serverAddr.Port = 853 } diff --git a/dns/transport/tcp.go b/dns/transport/tcp.go index 6061585e..4abeee2f 100644 --- a/dns/transport/tcp.go +++ b/dns/transport/tcp.go @@ -35,7 +35,7 @@ func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options o if err != nil { return nil, err } - serverAddr := options.ServerOptions.Build() + serverAddr := options.DNSServerAddressOptions.Build() if serverAddr.Port == 0 { serverAddr.Port = 53 } diff --git a/dns/transport/tls.go b/dns/transport/tls.go index 28fa885a..ce88d425 100644 --- a/dns/transport/tls.go +++ b/dns/transport/tls.go @@ -52,7 +52,7 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o if err != nil { return nil, err } - serverAddr := options.ServerOptions.Build() + serverAddr := options.DNSServerAddressOptions.Build() if serverAddr.Port == 0 { serverAddr.Port = 853 } diff --git a/dns/transport/udp.go b/dns/transport/udp.go index 289b1fe2..8c905c4c 100644 --- a/dns/transport/udp.go +++ b/dns/transport/udp.go @@ -42,7 +42,7 @@ func NewUDP(ctx context.Context, logger log.ContextLogger, tag string, options o if err != nil { return nil, err } - serverAddr := options.ServerOptions.Build() + serverAddr := options.DNSServerAddressOptions.Build() if serverAddr.Port == 0 { serverAddr.Port = 53 } diff --git a/option/dns.go b/option/dns.go index 04eb442d..1a42b0f4 100644 --- a/option/dns.go +++ b/option/dns.go @@ -19,10 +19,10 @@ import ( ) type RawDNSOptions struct { - Servers []NewDNSServerOptions `json:"servers,omitempty"` - Rules []DNSRule `json:"rules,omitempty"` - Final string `json:"final,omitempty"` - ReverseMapping bool `json:"reverse_mapping,omitempty"` + Servers []DNSServerOptions `json:"servers,omitempty"` + Rules []DNSRule `json:"rules,omitempty"` + Final string `json:"final,omitempty"` + ReverseMapping bool `json:"reverse_mapping,omitempty"` DNSClientOptions } @@ -35,32 +35,47 @@ type DNSOptions struct { LegacyDNSOptions } +type contextKeyDontUpgrade struct{} + +func ContextWithDontUpgrade(ctx context.Context) context.Context { + return context.WithValue(ctx, (*contextKeyDontUpgrade)(nil), true) +} + +func dontUpgradeFromContext(ctx context.Context) bool { + return ctx.Value((*contextKeyDontUpgrade)(nil)) == true +} + func (o *DNSOptions) UnmarshalJSONContext(ctx context.Context, content []byte) error { err := json.UnmarshalContext(ctx, content, &o.LegacyDNSOptions) if err != nil { return err } - if o.FakeIP != nil && o.FakeIP.Enabled { - deprecated.Report(ctx, deprecated.OptionLegacyDNSFakeIPOptions) - ctx = context.WithValue(ctx, (*LegacyDNSFakeIPOptions)(nil), o.FakeIP) - } + dontUpgrade := dontUpgradeFromContext(ctx) legacyOptions := o.LegacyDNSOptions - o.LegacyDNSOptions = LegacyDNSOptions{} + if !dontUpgrade { + if o.FakeIP != nil && o.FakeIP.Enabled { + deprecated.Report(ctx, deprecated.OptionLegacyDNSFakeIPOptions) + ctx = context.WithValue(ctx, (*LegacyDNSFakeIPOptions)(nil), o.FakeIP) + } + o.LegacyDNSOptions = LegacyDNSOptions{} + } err = badjson.UnmarshallExcludedContext(ctx, content, legacyOptions, &o.RawDNSOptions) if err != nil { return err } - rcodeMap := make(map[string]int) - o.Servers = common.Filter(o.Servers, func(it NewDNSServerOptions) bool { - if it.Type == C.DNSTypeLegacyRcode { - rcodeMap[it.Tag] = it.Options.(int) - return false - } - return true - }) - if len(rcodeMap) > 0 { - for i := 0; i < len(o.Rules); i++ { - rewriteRcode(rcodeMap, &o.Rules[i]) + if !dontUpgrade { + rcodeMap := make(map[string]int) + o.Servers = common.Filter(o.Servers, func(it DNSServerOptions) bool { + if it.Type == C.DNSTypeLegacyRcode { + rcodeMap[it.Tag] = it.Options.(int) + return false + } + return true + }) + if len(rcodeMap) > 0 { + for i := 0; i < len(o.Rules); i++ { + rewriteRcode(rcodeMap, &o.Rules[i]) + } } } return nil @@ -107,20 +122,24 @@ type DNSTransportOptionsRegistry interface { CreateOptions(transportType string) (any, bool) } -type _NewDNSServerOptions struct { +type _DNSServerOptions struct { Type string `json:"type,omitempty"` Tag string `json:"tag,omitempty"` Options any `json:"-"` } -type NewDNSServerOptions _NewDNSServerOptions +type DNSServerOptions _DNSServerOptions -func (o *NewDNSServerOptions) MarshalJSONContext(ctx context.Context) ([]byte, error) { - return badjson.MarshallObjectsContext(ctx, (*_NewDNSServerOptions)(o), o.Options) +func (o *DNSServerOptions) MarshalJSONContext(ctx context.Context) ([]byte, error) { + switch o.Type { + case C.DNSTypeLegacy: + o.Type = "" + } + return badjson.MarshallObjectsContext(ctx, (*_DNSServerOptions)(o), o.Options) } -func (o *NewDNSServerOptions) UnmarshalJSONContext(ctx context.Context, content []byte) error { - err := json.UnmarshalContext(ctx, content, (*_NewDNSServerOptions)(o)) +func (o *DNSServerOptions) UnmarshalJSONContext(ctx context.Context, content []byte) error { + err := json.UnmarshalContext(ctx, content, (*_DNSServerOptions)(o)) if err != nil { return err } @@ -141,12 +160,12 @@ func (o *NewDNSServerOptions) UnmarshalJSONContext(ctx context.Context, content return E.New("unknown transport type: ", o.Type) } } - err = badjson.UnmarshallExcludedContext(ctx, content, (*_NewDNSServerOptions)(o), options) + err = badjson.UnmarshallExcludedContext(ctx, content, (*_DNSServerOptions)(o), options) if err != nil { return err } o.Options = options - if o.Type == C.DNSTypeLegacy { + if o.Type == C.DNSTypeLegacy && !dontUpgradeFromContext(ctx) { err = o.Upgrade(ctx) if err != nil { return err @@ -155,7 +174,7 @@ func (o *NewDNSServerOptions) UnmarshalJSONContext(ctx context.Context, content return nil } -func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { +func (o *DNSServerOptions) Upgrade(ctx context.Context) error { if o.Type != C.DNSTypeLegacy { return nil } @@ -305,6 +324,27 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error { return nil } +type DNSServerAddressOptions struct { + Server string `json:"server"` + ServerPort uint16 `json:"server_port,omitempty"` +} + +func (o DNSServerAddressOptions) Build() M.Socksaddr { + return M.ParseSocksaddrHostPort(o.Server, o.ServerPort) +} + +func (o DNSServerAddressOptions) ServerIsDomain() bool { + return M.IsDomainName(o.Server) +} + +func (o *DNSServerAddressOptions) TakeServerOptions() ServerOptions { + return ServerOptions(*o) +} + +func (o *DNSServerAddressOptions) ReplaceServerOptions(options ServerOptions) { + *o = DNSServerAddressOptions(options) +} + type LegacyDNSServerOptions struct { Address string `json:"address"` AddressResolver string `json:"address_resolver,omitempty"` @@ -329,7 +369,7 @@ type LocalDNSServerOptions struct { type RemoteDNSServerOptions struct { LocalDNSServerOptions - ServerOptions + DNSServerAddressOptions LegacyAddressResolver string `json:"-"` LegacyAddressStrategy DomainStrategy `json:"-"` LegacyAddressFallbackDelay badoption.Duration `json:"-"` diff --git a/option/outbound.go b/option/outbound.go index 30ed4e79..4abc597c 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -99,7 +99,9 @@ type _DomainResolveOptions struct { type DomainResolveOptions _DomainResolveOptions func (o DomainResolveOptions) MarshalJSON() ([]byte, error) { - if o.Strategy == DomainStrategy(C.DomainStrategyAsIS) && + if o.Server == "" { + return []byte("{}"), nil + } else if o.Strategy == DomainStrategy(C.DomainStrategyAsIS) && !o.DisableCache && o.RewriteTTL == nil && o.ClientSubnet == nil { From 6ab62ea5c7a7ba4aca42500cf39931bdafb77130 Mon Sep 17 00:00:00 2001 From: k9982874 Date: Sat, 15 Mar 2025 18:10:35 +0800 Subject: [PATCH 60/94] Add ntp protocol sniffing --- common/sniff/ntp.go | 58 ++++++++++++++++++++++++++++ common/sniff/ntp_test.go | 33 ++++++++++++++++ docs/configuration/route/sniff.md | 1 + docs/configuration/route/sniff.zh.md | 1 + route/route.go | 1 + route/rule/rule_action.go | 2 + 6 files changed, 96 insertions(+) create mode 100644 common/sniff/ntp.go create mode 100644 common/sniff/ntp_test.go diff --git a/common/sniff/ntp.go b/common/sniff/ntp.go new file mode 100644 index 00000000..8b844c5b --- /dev/null +++ b/common/sniff/ntp.go @@ -0,0 +1,58 @@ +package sniff + +import ( + "context" + "encoding/binary" + "os" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" +) + +func NTP(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error { + // NTP packets must be at least 48 bytes long (standard NTP header size). + pLen := len(packet) + if pLen < 48 { + return os.ErrInvalid + } + // Check the LI (Leap Indicator) and Version Number (VN) in the first byte. + // We'll primarily focus on ensuring the version is valid for NTP. + // Many NTP versions are used, but let's check for generally accepted ones (3 & 4 for IPv4, plus potential extensions/customizations) + firstByte := packet[0] + li := (firstByte >> 6) & 0x03 // Extract LI + vn := (firstByte >> 3) & 0x07 // Extract VN + mode := firstByte & 0x07 // Extract Mode + + // Leap Indicator should be a valid value (0-3). + if li > 3 { + return os.ErrInvalid + } + + // Version Check (common NTP versions are 3 and 4) + if vn != 3 && vn != 4 { + return os.ErrInvalid + } + + // Check the Mode field for a client request (Mode 3). This validates it *is* a request. + if mode != 3 { + return os.ErrInvalid + } + + // Check Root Delay and Root Dispersion. While not strictly *required* for a request, + // we can check if they appear to be reasonable values (not excessively large). + rootDelay := binary.BigEndian.Uint32(packet[4:8]) + rootDispersion := binary.BigEndian.Uint32(packet[8:12]) + + // Check for unreasonably large root delay and dispersion. NTP RFC specifies max values of approximately 16 seconds. + // Convert to milliseconds for easy comparison. Each unit is 1/2^16 seconds. + if float64(rootDelay)/65536.0 > 16.0 { + return os.ErrInvalid + } + if float64(rootDispersion)/65536.0 > 16.0 { + return os.ErrInvalid + } + + metadata.Protocol = C.ProtocolNTP + + return nil +} diff --git a/common/sniff/ntp_test.go b/common/sniff/ntp_test.go new file mode 100644 index 00000000..5da94785 --- /dev/null +++ b/common/sniff/ntp_test.go @@ -0,0 +1,33 @@ +package sniff_test + +import ( + "context" + "encoding/hex" + "os" + "testing" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/sniff" + C "github.com/sagernet/sing-box/constant" + + "github.com/stretchr/testify/require" +) + +func TestSniffNTP(t *testing.T) { + t.Parallel() + packet, err := hex.DecodeString("1b0006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.NTP(context.Background(), &metadata, packet) + require.NoError(t, err) + require.Equal(t, metadata.Protocol, C.ProtocolNTP) +} + +func TestSniffNTPFailed(t *testing.T) { + t.Parallel() + packet, err := hex.DecodeString("400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.NTP(context.Background(), &metadata, packet) + require.ErrorIs(t, err, os.ErrInvalid) +} diff --git a/docs/configuration/route/sniff.md b/docs/configuration/route/sniff.md index 24bc8ef6..0fb386f7 100644 --- a/docs/configuration/route/sniff.md +++ b/docs/configuration/route/sniff.md @@ -22,6 +22,7 @@ If enabled in the inbound, the protocol and domain name (if present) of by the c | UDP | `dtls` | / | / | | TCP | `ssh` | / | SSH Client Name | | TCP | `rdp` | / | / | +| UDP | `ntp` | / | / | | QUIC Client | Type | |:------------------------:|:----------:| diff --git a/docs/configuration/route/sniff.zh.md b/docs/configuration/route/sniff.zh.md index cd582517..546210c8 100644 --- a/docs/configuration/route/sniff.zh.md +++ b/docs/configuration/route/sniff.zh.md @@ -22,6 +22,7 @@ | UDP | `dtls` | / | / | | TCP | `ssh` | / | SSH 客户端名称 | | TCP | `rdp` | / | / | +| UDP | `ntp` | / | / | | QUIC 客户端 | 类型 | |:------------------------:|:----------:| diff --git a/route/route.go b/route/route.go index 81b90fee..38f07412 100644 --- a/route/route.go +++ b/route/route.go @@ -601,6 +601,7 @@ func (r *Router) actionSniff( sniff.UTP, sniff.UDPTracker, sniff.DTLSRecord, + sniff.NTP, } } err = sniff.PeekPacket( diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index f1a570d4..07c147f3 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -371,6 +371,8 @@ func (r *RuleActionSniff) build() error { r.StreamSniffers = append(r.StreamSniffers, sniff.SSH) case C.ProtocolRDP: r.StreamSniffers = append(r.StreamSniffers, sniff.RDP) + case C.ProtocolNTP: + r.PacketSniffers = append(r.PacketSniffers, sniff.NTP) default: return E.New("unknown sniffer: ", name) } From 1befa4bf2f8689efb7eff4f011feb59708d1ecd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 15 Mar 2025 21:06:39 +0800 Subject: [PATCH 61/94] Fix Tailscale DNS --- go.mod | 26 +++++++-------- go.sum | 60 +++++++++++++++------------------- protocol/tailscale/endpoint.go | 23 +++++++++++++ 3 files changed, 61 insertions(+), 48 deletions(-) diff --git a/go.mod b/go.mod index 4c9682ac..4604fa71 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/sagernet/sing-tun v0.6.4 github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 - github.com/sagernet/tailscale v1.79.0-mod.1 + github.com/sagernet/tailscale v1.80.3-mod.0 github.com/sagernet/utls v1.6.7 github.com/sagernet/wireguard-go v0.0.1-beta.7 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 @@ -44,7 +44,7 @@ require ( go.uber.org/zap v1.27.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.33.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 golang.org/x/mod v0.23.0 golang.org/x/net v0.35.0 golang.org/x/sys v0.30.0 @@ -72,9 +72,9 @@ require ( github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.6.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gaissmai/bart v0.11.1 // indirect - github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect + github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect @@ -87,20 +87,19 @@ require ( github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/csrf v1.7.2 // indirect + github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect github.com/illarion/gonotify/v2 v2.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect github.com/jsimonetti/rtnetlink v1.4.0 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect - github.com/mdlayher/netlink v1.7.2 // indirect + github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect github.com/mdlayher/sdnotify v1.0.0 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect @@ -120,21 +119,20 @@ require ( github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect - github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect - github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect - github.com/tcnksm/go-httpstat v0.2.0 // indirect - github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect + github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect + github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect + github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/vishvananda/netns v0.0.4 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect - go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect + go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect - golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.29.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect diff --git a/go.sum b/go.sum index 9bada1f8..da98ca80 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yez github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= -github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc= github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= @@ -50,8 +50,8 @@ github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= -github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= +github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 h1:KbX3Z3CgiYlbaavUq3Cj9/MjpO+88S7/AGXzynVDv84= +github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -87,8 +87,8 @@ github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQN github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= -github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= +github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 h1:fiJdrgVBkjZ5B1HJ2WQwNOaXB+QyYcNXTA3t1XYLz0M= +github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= @@ -102,9 +102,6 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4= github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk= -github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8= github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= @@ -124,8 +121,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= -github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= -github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg= +github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o= github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= @@ -146,7 +143,6 @@ github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= -github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -200,8 +196,8 @@ github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2 github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= -github.com/sagernet/tailscale v1.79.0-mod.1 h1:wIuAH7VqBYJNk0h2+bTyk4F0OlSqHvyLDCBrD3i+XNI= -github.com/sagernet/tailscale v1.79.0-mod.1/go.mod h1:RKY5WjYLj3JJ7VO/8ZCw8eAFa4+kWU6A1Ftdk84uB14= +github.com/sagernet/tailscale v1.80.3-mod.0 h1:oHIdivbR/yxoiA9d3a2rRlhYn2shY9XVF35Rr8jW508= +github.com/sagernet/tailscale v1.80.3-mod.0/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI= @@ -228,16 +224,14 @@ github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29X github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= -github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w= -github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU= -github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g= -github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= +github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA= +github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc= +github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14= +github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= -github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= -github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= -github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw= -github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM= +github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= @@ -267,17 +261,17 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= -go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= -go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= +go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4hOxG5YpKCzkek= +go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -291,10 +285,8 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= @@ -306,11 +298,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index 6c2c111b..f24f931f 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -39,6 +39,7 @@ import ( "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/filemanager" "github.com/sagernet/tailscale/ipn" + tsDNS "github.com/sagernet/tailscale/net/dns" "github.com/sagernet/tailscale/net/netmon" "github.com/sagernet/tailscale/net/tsaddr" "github.com/sagernet/tailscale/tsnet" @@ -145,6 +146,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) { return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions()) }, + DNS: &dnsConfigurtor{}, } return &Endpoint{ Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP}, nil), @@ -471,3 +473,24 @@ func (d *endpointDialer) ListenPacket(ctx context.Context, destination M.Socksad d.logger.InfoContext(ctx, "output packet connection") return d.Dialer.ListenPacket(ctx, destination) } + +type dnsConfigurtor struct { + baseConfig tsDNS.OSConfig +} + +func (c *dnsConfigurtor) SetDNS(cfg tsDNS.OSConfig) error { + c.baseConfig = cfg + return nil +} + +func (c *dnsConfigurtor) SupportsSplitDNS() bool { + return true +} + +func (c *dnsConfigurtor) GetBaseConfig() (tsDNS.OSConfig, error) { + return c.baseConfig, nil +} + +func (c *dnsConfigurtor) Close() error { + return nil +} From 015f6efc0986f518921c992999d464e366033ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 16 Mar 2025 23:50:23 +0800 Subject: [PATCH 62/94] Add wildcard-sni support for shadow-tls inbound --- docs/configuration/inbound/shadowtls.md | 30 +++- docs/configuration/inbound/shadowtls.zh.md | 27 +++- go.mod | 2 +- go.sum | 4 +- option/shadowtls.go | 51 +++++++ protocol/shadowtls/inbound.go | 7 +- test/go.mod | 34 +++-- test/go.sum | 76 +++++----- test/shadowtls_test.go | 156 +++++++++++++++++++-- 9 files changed, 308 insertions(+), 79 deletions(-) diff --git a/docs/configuration/inbound/shadowtls.md b/docs/configuration/inbound/shadowtls.md index 0f6684d2..9dbf1dd5 100644 --- a/docs/configuration/inbound/shadowtls.md +++ b/docs/configuration/inbound/shadowtls.md @@ -1,3 +1,11 @@ +--- +icon: material/new-box +--- + +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [wildcard_sni](#wildcard_sni) + ### Structure ```json @@ -29,7 +37,8 @@ ... // Dial Fields } }, - "strict_mode": false + "strict_mode": false, + "wildcard_sni": "" } ``` @@ -55,7 +64,6 @@ ShadowTLS password. Only available in the ShadowTLS protocol 2. - #### users ShadowTLS users. @@ -66,6 +74,8 @@ Only available in the ShadowTLS protocol 3. ==Required== +When `wildcard_sni` is configured to `all`, the server address is optional. + Handshake server address and [Dial Fields](/configuration/shared/dial/). #### handshake_for_server_name @@ -79,3 +89,19 @@ Only available in the ShadowTLS protocol 2/3. ShadowTLS strict mode. Only available in the ShadowTLS protocol 3. + +#### wildcard_sni + +!!! question "Since sing-box 1.12.0" + +ShadowTLS wildcard SNI mode. + +Available values are: + +* `off`: (default) Disabled. +* `authed`: Authenticated connections will have their destination overwritten to `(servername):443` +* `all`: All connections will have their destination overwritten to `(servername):443` + +Additionally, connections matching `handshake_for_server_name` are not affected. + +Only available in the ShadowTLS protocol 3. diff --git a/docs/configuration/inbound/shadowtls.zh.md b/docs/configuration/inbound/shadowtls.zh.md index 1497ac42..be860051 100644 --- a/docs/configuration/inbound/shadowtls.zh.md +++ b/docs/configuration/inbound/shadowtls.zh.md @@ -1,3 +1,11 @@ +--- +icon: material/new-box +--- + +!!! quote "sing-box 1.12.0 中的更改" + + :material-plus: [wildcard_sni](#wildcard_sni) + ### 结构 ```json @@ -29,7 +37,8 @@ ... // 拨号字段 } }, - "strict_mode": false + "strict_mode": false, + "wildcard_sni": "" } ``` @@ -80,3 +89,19 @@ ShadowTLS 用户。 ShadowTLS 严格模式。 仅在 ShadowTLS 协议版本 3 中可用。 + +#### wildcard_sni + +!!! question "自 sing-box 1.12.0 起" + +ShadowTLS 通配符 SNI 模式。 + +可用值: + +* `off`:(默认)禁用。 +* `authed`:已认证的连接的目标将被重写为 `(servername):443`。 +* `all`:所有连接的目标将被重写为 `(servername):443`。 + +此外,匹配 `handshake_for_server_name` 的连接不受影响。 + +仅在 ShadowTLS 协议 3 中可用。 diff --git a/go.mod b/go.mod index 4604fa71..cd9d5351 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks2 v0.2.0 - github.com/sagernet/sing-shadowtls v0.2.0 + github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 github.com/sagernet/sing-tun v0.6.4 github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 diff --git a/go.sum b/go.sum index da98ca80..d217a5c2 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,8 @@ github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegE github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= -github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk= -github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo= +github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 h1:GFNJQAHhSXqAfxAw1wDG/QWbdpGH5Na3k8qUynqWnEA= +github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056/go.mod h1:HyacBPIFiKihJQR8LQp56FM4hBtd/7MZXnRxxQIOPsc= github.com/sagernet/sing-tun v0.6.4 h1:3Iew6UtAf1+mucVeHKNhAEQI5xmq3CUCbGptUbjebts= github.com/sagernet/sing-tun v0.6.4/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug= diff --git a/option/shadowtls.go b/option/shadowtls.go index a8be7740..7edbac40 100644 --- a/option/shadowtls.go +++ b/option/shadowtls.go @@ -1,5 +1,11 @@ package option +import ( + "encoding/json" + + E "github.com/sagernet/sing/common/exceptions" +) + type ShadowTLSInboundOptions struct { ListenOptions Version int `json:"version,omitempty"` @@ -8,6 +14,51 @@ type ShadowTLSInboundOptions struct { Handshake ShadowTLSHandshakeOptions `json:"handshake,omitempty"` HandshakeForServerName map[string]ShadowTLSHandshakeOptions `json:"handshake_for_server_name,omitempty"` StrictMode bool `json:"strict_mode,omitempty"` + WildcardSNI WildcardSNI `json:"wildcard_sni,omitempty"` +} + +type WildcardSNI int + +const ( + ShadowTLSWildcardSNIOff WildcardSNI = iota + ShadowTLSWildcardSNIAuthed + ShadowTLSWildcardSNIAll +) + +func (w WildcardSNI) MarshalJSON() ([]byte, error) { + return json.Marshal(w.String()) +} + +func (w WildcardSNI) String() string { + switch w { + case ShadowTLSWildcardSNIOff: + return "off" + case ShadowTLSWildcardSNIAuthed: + return "authed" + case ShadowTLSWildcardSNIAll: + return "all" + default: + panic("unknown wildcard SNI value") + } +} + +func (w *WildcardSNI) UnmarshalJSON(bytes []byte) error { + var valueString string + err := json.Unmarshal(bytes, &valueString) + if err != nil { + return err + } + switch valueString { + case "off", "": + *w = ShadowTLSWildcardSNIOff + case "authed": + *w = ShadowTLSWildcardSNIAuthed + case "all": + *w = ShadowTLSWildcardSNIAll + default: + return E.New("unknown wildcard SNI value: ", valueString) + } + return nil } type ShadowTLSUser struct { diff --git a/protocol/shadowtls/inbound.go b/protocol/shadowtls/inbound.go index 1db191d8..0508999b 100644 --- a/protocol/shadowtls/inbound.go +++ b/protocol/shadowtls/inbound.go @@ -57,7 +57,11 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo } } } - handshakeDialer, err := dialer.New(ctx, options.Handshake.DialerOptions, options.Handshake.ServerIsDomain()) + serverIsDomain := options.Handshake.ServerIsDomain() + if options.WildcardSNI != option.ShadowTLSWildcardSNIOff { + serverIsDomain = true + } + handshakeDialer, err := dialer.New(ctx, options.Handshake.DialerOptions, serverIsDomain) if err != nil { return nil, err } @@ -73,6 +77,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo }, HandshakeForServerName: handshakeForServerName, StrictMode: options.StrictMode, + WildcardSNI: shadowtls.WildcardSNI(options.WildcardSNI), Handler: (*inboundHandler)(inbound), Logger: logger, }) diff --git a/test/go.mod b/test/go.mod index 2baf3b41..e6823537 100644 --- a/test/go.mod +++ b/test/go.mod @@ -13,7 +13,7 @@ require ( github.com/docker/go-connections v0.5.0 github.com/gofrs/uuid/v5 v5.3.1 github.com/sagernet/quic-go v0.49.0-beta.1 - github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff + github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d github.com/sagernet/sing-quic v0.4.1-beta.1 github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks2 v0.2.0 @@ -30,7 +30,7 @@ require ( github.com/akutz/memconn v0.1.0 // indirect github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect github.com/andybalholm/brotli v1.1.0 // indirect - github.com/anytls/sing-anytls v0.0.2 // indirect + github.com/anytls/sing-anytls v0.0.6 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/caddyserver/certmagic v0.21.7 // indirect github.com/caddyserver/zerossl v0.1.3 // indirect @@ -46,11 +46,11 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.6.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gaissmai/bart v0.11.1 // indirect github.com/go-chi/chi/v5 v5.2.1 // indirect github.com/go-chi/render v1.0.3 // indirect - github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect + github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -65,13 +65,12 @@ require ( github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/csrf v1.7.2 // indirect + github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect github.com/illarion/gonotify/v2 v2.0.3 // indirect github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect - github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect github.com/jsimonetti/rtnetlink v1.4.0 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect @@ -81,7 +80,7 @@ require ( github.com/libdns/libdns v0.2.2 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/mdlayher/genetlink v1.3.2 // indirect - github.com/mdlayher/netlink v1.7.2 // indirect + github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect github.com/mdlayher/sdnotify v1.0.0 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect @@ -109,11 +108,11 @@ require ( github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/sing-mux v0.3.1 // indirect - github.com/sagernet/sing-shadowtls v0.2.0 // indirect - github.com/sagernet/sing-tun v0.6.1 // indirect + github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 // indirect + github.com/sagernet/sing-tun v0.6.2-0.20250319123703-35b5747b44ec // indirect github.com/sagernet/sing-vmess v0.2.0 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect - github.com/sagernet/tailscale v1.79.0-mod.1 // indirect + github.com/sagernet/tailscale v1.80.3-mod.0 // indirect github.com/sagernet/utls v1.6.7 // indirect github.com/sagernet/wireguard-go v0.0.1-beta.5 // indirect github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect @@ -123,10 +122,9 @@ require ( github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect - github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect - github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect - github.com/tcnksm/go-httpstat v0.2.0 // indirect - github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect + github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect + github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect + github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/vishvananda/netns v0.0.4 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect @@ -138,17 +136,17 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect - go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect + go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/crypto v0.33.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect golang.org/x/mod v0.23.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect - golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.29.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect diff --git a/test/go.sum b/test/go.sum index d63f3057..ce8bfb28 100644 --- a/test/go.sum +++ b/test/go.sum @@ -12,8 +12,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/anytls/sing-anytls v0.0.2 h1:25azSh0o/LMcIkhS4ZutgRTIGwh8O3wuOhsThVM9K9o= -github.com/anytls/sing-anytls v0.0.2/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/anytls/sing-anytls v0.0.6 h1:UatIjl/OvzWQGXQ1I2bAIkabL9WtihW0fA7G+DXGBUg= +github.com/anytls/sing-anytls v0.0.6/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= @@ -53,8 +53,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= -github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc= github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= @@ -63,8 +63,8 @@ github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= -github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= +github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 h1:KbX3Z3CgiYlbaavUq3Cj9/MjpO+88S7/AGXzynVDv84= +github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -100,8 +100,8 @@ github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQN github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= -github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= +github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 h1:fiJdrgVBkjZ5B1HJ2WQwNOaXB+QyYcNXTA3t1XYLz0M= +github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= @@ -114,9 +114,6 @@ github.com/illarion/gonotify/v2 v2.0.3 h1:B6+SKPo/0Sw8cRJh1aLzNEeNVFfzE3c6N+o+vy github.com/illarion/gonotify/v2 v2.0.3/go.mod h1:38oIJTgFqupkEydkkClkbL6i5lXV/bxdH9do5TALPEE= github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4= github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k= -github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk= -github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8= github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -142,8 +139,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= -github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= -github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg= +github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o= github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= @@ -172,7 +169,6 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -205,8 +201,8 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff h1:5UGghwx8cI14qFa0ienrLekAYfhdKAiWvJUkY7rHmsI= -github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d h1:8GJnvXlOBdgCa0spumUzPbMamkEbud4sfNTd8+1YaEg= +github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI= github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78= github.com/sagernet/sing-quic v0.4.1-beta.1 h1:V2VfMckT3EQR3ZdfSzJgZZDsvfZZH42QAZpnOnHKa0s= @@ -215,16 +211,16 @@ github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegE github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= -github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk= -github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo= -github.com/sagernet/sing-tun v0.6.1 h1:4l0+gnEKcGjlWfUVTD+W0BRApqIny/lU2ZliurE+VMo= -github.com/sagernet/sing-tun v0.6.1/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= +github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 h1:GFNJQAHhSXqAfxAw1wDG/QWbdpGH5Na3k8qUynqWnEA= +github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056/go.mod h1:HyacBPIFiKihJQR8LQp56FM4hBtd/7MZXnRxxQIOPsc= +github.com/sagernet/sing-tun v0.6.2-0.20250319123703-35b5747b44ec h1:9/OYGb9qDmUFIhqd3S+3eni62EKRQR1rSmRH18baA/M= +github.com/sagernet/sing-tun v0.6.2-0.20250319123703-35b5747b44ec/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI= github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= -github.com/sagernet/tailscale v1.79.0-mod.1 h1:wIuAH7VqBYJNk0h2+bTyk4F0OlSqHvyLDCBrD3i+XNI= -github.com/sagernet/tailscale v1.79.0-mod.1/go.mod h1:RKY5WjYLj3JJ7VO/8ZCw8eAFa4+kWU6A1Ftdk84uB14= +github.com/sagernet/tailscale v1.80.3-mod.0 h1:oHIdivbR/yxoiA9d3a2rRlhYn2shY9XVF35Rr8jW508= +github.com/sagernet/tailscale v1.80.3-mod.0/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc= @@ -251,16 +247,14 @@ github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29X github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= -github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w= -github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU= -github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g= -github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= +github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA= +github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc= +github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14= +github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= -github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= -github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= -github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw= -github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM= +github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= @@ -300,8 +294,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= -go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= -go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= +go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4hOxG5YpKCzkek= +go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -310,10 +304,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= @@ -339,10 +333,8 @@ golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= @@ -355,14 +347,14 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/test/shadowtls_test.go b/test/shadowtls_test.go index f64492ee..28cd1da0 100644 --- a/test/shadowtls_test.go +++ b/test/shadowtls_test.go @@ -2,6 +2,7 @@ package main import ( "context" + "crypto/tls" "net" "net/http" "net/netip" @@ -19,25 +20,43 @@ import ( func TestShadowTLS(t *testing.T) { t.Run("v1", func(t *testing.T) { - testShadowTLS(t, 1, "", false) + testShadowTLS(t, 1, "", false, option.ShadowTLSWildcardSNIOff) }) t.Run("v2", func(t *testing.T) { - testShadowTLS(t, 2, "hello", false) + testShadowTLS(t, 2, "hello", false, option.ShadowTLSWildcardSNIOff) }) t.Run("v3", func(t *testing.T) { - testShadowTLS(t, 3, "hello", false) + testShadowTLS(t, 3, "hello", false, option.ShadowTLSWildcardSNIOff) }) t.Run("v2-utls", func(t *testing.T) { - testShadowTLS(t, 2, "hello", true) + testShadowTLS(t, 2, "hello", true, option.ShadowTLSWildcardSNIOff) }) t.Run("v3-utls", func(t *testing.T) { - testShadowTLS(t, 3, "hello", true) + testShadowTLS(t, 3, "hello", true, option.ShadowTLSWildcardSNIOff) + }) + t.Run("v3-wildcard-sni-authed", func(t *testing.T) { + testShadowTLS(t, 3, "hello", false, option.ShadowTLSWildcardSNIAuthed) + }) + t.Run("v3-wildcard-sni-all", func(t *testing.T) { + testShadowTLS(t, 3, "hello", false, option.ShadowTLSWildcardSNIAll) + }) + t.Run("v3-wildcard-sni-authed-utls", func(t *testing.T) { + testShadowTLS(t, 3, "hello", true, option.ShadowTLSWildcardSNIAll) + }) + t.Run("v3-wildcard-sni-all-utls", func(t *testing.T) { + testShadowTLS(t, 3, "hello", true, option.ShadowTLSWildcardSNIAll) }) } -func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool) { +func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool, wildcardSNI option.WildcardSNI) { method := shadowaead_2022.List[0] ssPassword := mkBase64(t, 16) + var clientServerName string + if wildcardSNI != option.ShadowTLSWildcardSNIOff { + clientServerName = "cloudflare.com" + } else { + clientServerName = "google.com" + } startInstance(t, option.Options{ Inbounds: []option.Inbound{ { @@ -67,9 +86,10 @@ func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool) ServerPort: 443, }, }, - Version: version, - Password: password, - Users: []option.ShadowTLSUser{{Password: password}}, + Version: version, + Password: password, + Users: []option.ShadowTLSUser{{Password: password}}, + WildcardSNI: wildcardSNI, }, }, { @@ -107,7 +127,7 @@ func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool) OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{ TLS: &option.OutboundTLSOptions{ Enabled: true, - ServerName: "google.com", + ServerName: clientServerName, UTLS: &option.OutboundUTLSOptions{ Enabled: utlsEanbled, }, @@ -157,7 +177,7 @@ func TestShadowTLSFallback(t *testing.T) { }, Handshake: option.ShadowTLSHandshakeOptions{ ServerOptions: option.ServerOptions{ - Server: "google.com", + Server: "bing.com", ServerPort: 443, }, }, @@ -177,13 +197,125 @@ func TestShadowTLSFallback(t *testing.T) { }, }, } - response, err := client.Get("https://google.com") + response, err := client.Get("https://bing.com") require.NoError(t, err) require.Equal(t, response.StatusCode, 200) response.Body.Close() client.CloseIdleConnections() } +func TestShadowTLSFallbackWildcardAll(t *testing.T) { + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeShadowTLS, + Options: &option.ShadowTLSInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), + ListenPort: serverPort, + }, + Version: 3, + Users: []option.ShadowTLSUser{ + {Password: "hello"}, + }, + WildcardSNI: option.ShadowTLSWildcardSNIAll, + }, + }, + }, + }) + client := &http.Client{ + Transport: &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + var d net.Dialer + return d.DialContext(ctx, network, "127.0.0.1:"+F.ToString(serverPort)) + }, + }, + } + response, err := client.Get("https://www.bing.com") + require.NoError(t, err) + require.Equal(t, response.StatusCode, 200) + response.Body.Close() + client.CloseIdleConnections() +} + +func TestShadowTLSFallbackWildcardAuthedFail(t *testing.T) { + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeShadowTLS, + Options: &option.ShadowTLSInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), + ListenPort: serverPort, + }, + Handshake: option.ShadowTLSHandshakeOptions{ + ServerOptions: option.ServerOptions{ + Server: "bing.com", + ServerPort: 443, + }, + }, + Version: 3, + Users: []option.ShadowTLSUser{ + {Password: "hello"}, + }, + WildcardSNI: option.ShadowTLSWildcardSNIAuthed, + }, + }, + }, + }) + client := &http.Client{ + Transport: &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + var d net.Dialer + return d.DialContext(ctx, network, "127.0.0.1:"+F.ToString(serverPort)) + }, + }, + } + _, err := client.Get("https://baidu.com") + expected := &tls.CertificateVerificationError{} + require.ErrorAs(t, err, &expected) + client.CloseIdleConnections() +} + +func TestShadowTLSFallbackWildcardOffFail(t *testing.T) { + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeShadowTLS, + Options: &option.ShadowTLSInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())), + ListenPort: serverPort, + }, + Handshake: option.ShadowTLSHandshakeOptions{ + ServerOptions: option.ServerOptions{ + Server: "bing.com", + ServerPort: 443, + }, + }, + Version: 3, + Users: []option.ShadowTLSUser{ + {Password: "hello"}, + }, + WildcardSNI: option.ShadowTLSWildcardSNIOff, + }, + }, + }, + }) + client := &http.Client{ + Transport: &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + var d net.Dialer + return d.DialContext(ctx, network, "127.0.0.1:"+F.ToString(serverPort)) + }, + }, + } + _, err := client.Get("https://baidu.com") + expected := &tls.CertificateVerificationError{} + require.ErrorAs(t, err, &expected) + client.CloseIdleConnections() +} + func TestShadowTLSInbound(t *testing.T) { method := shadowaead_2022.List[0] password := mkBase64(t, 16) From d998a73a4c144de57f97f8d19f14ed0a8e8345a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 17 Mar 2025 18:01:00 +0800 Subject: [PATCH 63/94] Fix unhandled DNS loop --- common/dialer/dialer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index 00ebbeac..b93b7096 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -52,7 +52,7 @@ func NewWithOptions(options Options) (N.Dialer, error) { return nil, err } } - if options.RemoteIsDomain && (dialOptions.Detour == "" || options.ResolverOnDetour) { + if options.RemoteIsDomain && (dialOptions.Detour == "" || options.ResolverOnDetour || dialOptions.DomainResolver != nil && dialOptions.DomainResolver.Server != "") { networkManager := service.FromContext[adapter.NetworkManager](options.Context) dnsTransport := service.FromContext[adapter.DNSTransportManager](options.Context) var defaultOptions adapter.NetworkOptions From 0b52e96b3c6a7732e215f3bf599eec08b8463cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 18 Mar 2025 09:27:53 +0800 Subject: [PATCH 64/94] Remove map usage in options --- option/shadowtls.go | 15 ++++++++------- protocol/shadowtls/inbound.go | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/option/shadowtls.go b/option/shadowtls.go index 7edbac40..81ef9a43 100644 --- a/option/shadowtls.go +++ b/option/shadowtls.go @@ -4,17 +4,18 @@ import ( "encoding/json" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/json/badjson" ) type ShadowTLSInboundOptions struct { ListenOptions - Version int `json:"version,omitempty"` - Password string `json:"password,omitempty"` - Users []ShadowTLSUser `json:"users,omitempty"` - Handshake ShadowTLSHandshakeOptions `json:"handshake,omitempty"` - HandshakeForServerName map[string]ShadowTLSHandshakeOptions `json:"handshake_for_server_name,omitempty"` - StrictMode bool `json:"strict_mode,omitempty"` - WildcardSNI WildcardSNI `json:"wildcard_sni,omitempty"` + Version int `json:"version,omitempty"` + Password string `json:"password,omitempty"` + Users []ShadowTLSUser `json:"users,omitempty"` + Handshake ShadowTLSHandshakeOptions `json:"handshake,omitempty"` + HandshakeForServerName *badjson.TypedMap[string, ShadowTLSHandshakeOptions] `json:"handshake_for_server_name,omitempty"` + StrictMode bool `json:"strict_mode,omitempty"` + WildcardSNI WildcardSNI `json:"wildcard_sni,omitempty"` } type WildcardSNI int diff --git a/protocol/shadowtls/inbound.go b/protocol/shadowtls/inbound.go index 0508999b..812df1ef 100644 --- a/protocol/shadowtls/inbound.go +++ b/protocol/shadowtls/inbound.go @@ -46,14 +46,16 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo var handshakeForServerName map[string]shadowtls.HandshakeConfig if options.Version > 1 { handshakeForServerName = make(map[string]shadowtls.HandshakeConfig) - for serverName, serverOptions := range options.HandshakeForServerName { - handshakeDialer, err := dialer.New(ctx, serverOptions.DialerOptions, serverOptions.ServerIsDomain()) - if err != nil { - return nil, err - } - handshakeForServerName[serverName] = shadowtls.HandshakeConfig{ - Server: serverOptions.ServerOptions.Build(), - Dialer: handshakeDialer, + if options.HandshakeForServerName != nil { + for _, entry := range options.HandshakeForServerName.Entries() { + handshakeDialer, err := dialer.New(ctx, entry.Value.DialerOptions, entry.Value.ServerIsDomain()) + if err != nil { + return nil, err + } + handshakeForServerName[entry.Key] = shadowtls.HandshakeConfig{ + Server: entry.Value.ServerOptions.Build(), + Dialer: handshakeDialer, + } } } } From ea012b4173dba4b29213a62f12c3212804ef5ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 18 Mar 2025 19:17:11 +0800 Subject: [PATCH 65/94] Add wildcard name support for predefined records --- dns/router.go | 15 +-------------- route/rule/rule_action.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/dns/router.go b/dns/router.go index bd4eb3f1..42cebd23 100644 --- a/dns/router.go +++ b/dns/router.go @@ -263,20 +263,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte return nil, tun.ErrDrop } case *R.RuleActionPredefined: - return &mDNS.Msg{ - MsgHdr: mDNS.MsgHdr{ - Id: message.Id, - Response: true, - Authoritative: true, - RecursionDesired: true, - RecursionAvailable: true, - Rcode: action.Rcode, - }, - Question: message.Question, - Answer: action.Answer, - Ns: action.Ns, - Extra: action.Extra, - }, nil + return action.Response(message), nil } } var responseCheck func(responseAddrs []netip.Addr) bool diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index 07c147f3..098b9d3a 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -447,3 +447,32 @@ func (r *RuleActionPredefined) String() string { options = append(options, common.Map(r.Extra, dns.RR.String)...) return F.ToString("predefined(", strings.Join(options, ","), ")") } + +func (r *RuleActionPredefined) Response(request *dns.Msg) *dns.Msg { + return &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Id: request.Id, + Response: true, + Authoritative: true, + RecursionDesired: true, + RecursionAvailable: true, + Rcode: r.Rcode, + }, + Question: request.Question, + Answer: rewriteRecords(r.Answer, request.Question[0]), + Ns: rewriteRecords(r.Ns, request.Question[0]), + Extra: rewriteRecords(r.Extra, request.Question[0]), + } +} + +func rewriteRecords(records []dns.RR, question dns.Question) []dns.RR { + return common.Map(records, func(it dns.RR) dns.RR { + if strings.HasPrefix(it.Header().Name, "*") { + if strings.HasSuffix(question.Name, it.Header().Name[1:]) { + it = dns.Copy(it) + it.Header().Name = question.Name + } + } + return it + }) +} From d379dcc53d71fbe38436234812bebcc1c9cc33ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 18 Mar 2025 14:21:08 +0800 Subject: [PATCH 66/94] Add netns support --- common/dialer/default.go | 45 ++++++++++++++----------- common/listener/listener.go | 31 +++++++++++++++++ common/listener/listener_tcp.go | 30 +++++++++-------- common/listener/listener_udp.go | 11 +++++- docs/configuration/shared/dial.md | 44 +++++++++++++++--------- docs/configuration/shared/dial.zh.md | 38 +++++++++++++-------- docs/configuration/shared/listen.md | 29 ++++++++++++---- docs/configuration/shared/listen.zh.md | 29 ++++++++++++---- go.mod | 2 +- option/inbound.go | 1 + option/outbound.go | 1 + option/simple.go | 8 +++-- protocol/mixed/inbound.go | 2 +- protocol/redirect/tproxy.go | 46 +++++++++++++++----------- protocol/socks/inbound.go | 2 +- protocol/tor/proxy.go | 2 +- 16 files changed, 218 insertions(+), 103 deletions(-) diff --git a/common/dialer/default.go b/common/dialer/default.go index 8877e9f3..e8ae0f13 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -10,6 +10,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/conntrack" + "github.com/sagernet/sing-box/common/listener" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/option" @@ -35,6 +36,7 @@ type DefaultDialer struct { udpListener net.ListenConfig udpAddr4 string udpAddr6 string + netns string networkManager adapter.NetworkManager networkStrategy *C.NetworkStrategy defaultNetworkStrategy bool @@ -198,6 +200,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial udpListener: listener, udpAddr4: udpAddr4, udpAddr6: udpAddr6, + netns: options.NetNs, networkManager: networkManager, networkStrategy: networkStrategy, defaultNetworkStrategy: defaultNetworkStrategy, @@ -214,19 +217,21 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address return nil, E.New("domain not resolved") } if d.networkStrategy == nil { - switch N.NetworkName(network) { - case N.NetworkUDP: - if !address.IsIPv6() { - return trackConn(d.udpDialer4.DialContext(ctx, network, address.String())) - } else { - return trackConn(d.udpDialer6.DialContext(ctx, network, address.String())) + return trackConn(listener.ListenNetworkNamespace[net.Conn](d.netns, func() (net.Conn, error) { + switch N.NetworkName(network) { + case N.NetworkUDP: + if !address.IsIPv6() { + return d.udpDialer4.DialContext(ctx, network, address.String()) + } else { + return d.udpDialer6.DialContext(ctx, network, address.String()) + } } - } - if !address.IsIPv6() { - return trackConn(DialSlowContext(&d.dialer4, ctx, network, address)) - } else { - return trackConn(DialSlowContext(&d.dialer6, ctx, network, address)) - } + if !address.IsIPv6() { + return DialSlowContext(&d.dialer4, ctx, network, address) + } else { + return DialSlowContext(&d.dialer6, ctx, network, address) + } + })) } else { return d.DialParallelInterface(ctx, network, address, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay) } @@ -282,13 +287,15 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { if d.networkStrategy == nil { - if destination.IsIPv6() { - return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6)) - } else if destination.IsIPv4() && !destination.Addr.IsUnspecified() { - return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4)) - } else { - return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4)) - } + return trackPacketConn(listener.ListenNetworkNamespace[net.PacketConn](d.netns, func() (net.PacketConn, error) { + if destination.IsIPv6() { + return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6) + } else if destination.IsIPv4() && !destination.Addr.IsUnspecified() { + return d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4) + } else { + return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4) + } + })) } else { return d.ListenSerialInterfacePacket(ctx, destination, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay) } diff --git a/common/listener/listener.go b/common/listener/listener.go index 289f15c5..8a4cad34 100644 --- a/common/listener/listener.go +++ b/common/listener/listener.go @@ -4,6 +4,8 @@ import ( "context" "net" "net/netip" + "runtime" + "strings" "sync/atomic" "github.com/sagernet/sing-box/adapter" @@ -14,6 +16,8 @@ import ( "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + + "github.com/vishvananda/netns" ) type Listener struct { @@ -135,3 +139,30 @@ func (l *Listener) UDPConn() *net.UDPConn { func (l *Listener) ListenOptions() option.ListenOptions { return l.listenOptions } + +func ListenNetworkNamespace[T any](nameOrPath string, block func() (T, error)) (T, error) { + if nameOrPath != "" { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + currentNs, err := netns.Get() + if err != nil { + return common.DefaultValue[T](), E.Cause(err, "get current netns") + } + defer netns.Set(currentNs) + var targetNs netns.NsHandle + if strings.HasPrefix(nameOrPath, "/") { + targetNs, err = netns.GetFromPath(nameOrPath) + } else { + targetNs, err = netns.GetFromName(nameOrPath) + } + if err != nil { + return common.DefaultValue[T](), E.Cause(err, "get netns ", nameOrPath) + } + defer targetNs.Close() + err = netns.Set(targetNs) + if err != nil { + return common.DefaultValue[T](), E.Cause(err, "set netns to ", nameOrPath) + } + } + return block() +} diff --git a/common/listener/listener_tcp.go b/common/listener/listener_tcp.go index 646d4017..c5995fad 100644 --- a/common/listener/listener_tcp.go +++ b/common/listener/listener_tcp.go @@ -16,9 +16,12 @@ import ( ) func (l *Listener) ListenTCP() (net.Listener, error) { + //nolint:staticcheck + if l.listenOptions.ProxyProtocol || l.listenOptions.ProxyProtocolAcceptNoHeader { + return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0") + } var err error bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort) - var tcpListener net.Listener var listenConfig net.ListenConfig if l.listenOptions.TCPKeepAlive >= 0 { keepIdle := time.Duration(l.listenOptions.TCPKeepAlive) @@ -37,20 +40,19 @@ func (l *Listener) ListenTCP() (net.Listener, error) { } setMultiPathTCP(&listenConfig) } - if l.listenOptions.TCPFastOpen { - var tfoConfig tfo.ListenConfig - tfoConfig.ListenConfig = listenConfig - tcpListener, err = tfoConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String()) - } else { - tcpListener, err = listenConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String()) - } - if err == nil { - l.logger.Info("tcp server started at ", tcpListener.Addr()) - } - //nolint:staticcheck - if l.listenOptions.ProxyProtocol || l.listenOptions.ProxyProtocolAcceptNoHeader { - return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0") + tcpListener, err := ListenNetworkNamespace[net.Listener](l.listenOptions.NetNs, func() (net.Listener, error) { + if l.listenOptions.TCPFastOpen { + var tfoConfig tfo.ListenConfig + tfoConfig.ListenConfig = listenConfig + return tfoConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String()) + } else { + return listenConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String()) + } + }) + if err != nil { + return nil, err } + l.logger.Info("tcp server started at ", tcpListener.Addr()) l.tcpListener = tcpListener return tcpListener, err } diff --git a/common/listener/listener_udp.go b/common/listener/listener_udp.go index 10d6dc38..d8b117a3 100644 --- a/common/listener/listener_udp.go +++ b/common/listener/listener_udp.go @@ -1,6 +1,7 @@ package listener import ( + "context" "net" "net/netip" "os" @@ -24,7 +25,9 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) { if !udpFragment { lc.Control = control.Append(lc.Control, control.DisableUDPFragment()) } - udpConn, err := lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String()) + udpConn, err := ListenNetworkNamespace[net.PacketConn](l.listenOptions.NetNs, func() (net.PacketConn, error) { + return lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String()) + }) if err != nil { return nil, err } @@ -34,6 +37,12 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) { return udpConn, err } +func (l *Listener) ListenPacket(listenConfig net.ListenConfig, ctx context.Context, network string, address string) (net.PacketConn, error) { + return ListenNetworkNamespace[net.PacketConn](l.listenOptions.NetNs, func() (net.PacketConn, error) { + return listenConfig.ListenPacket(ctx, network, address) + }) +} + func (l *Listener) UDPAddr() M.Socksaddr { return l.udpAddr } diff --git a/docs/configuration/shared/dial.md b/docs/configuration/shared/dial.md index 7f0bf251..f7507531 100644 --- a/docs/configuration/shared/dial.md +++ b/docs/configuration/shared/dial.md @@ -5,7 +5,8 @@ icon: material/new-box !!! quote "Changes in sing-box 1.12.0" :material-plus: [domain_resolver](#domain_resolver) - :material-delete-clock: [domain_strategy](#domain_strategy) + :material-delete-clock: [domain_strategy](#domain_strategy) + :material-plus: [netns](#netns) !!! quote "Changes in sing-box 1.11.0" @@ -18,24 +19,25 @@ icon: material/new-box ```json { - "detour": "upstream-out", - "bind_interface": "en0", - "inet4_bind_address": "0.0.0.0", - "inet6_bind_address": "::", - "routing_mark": 1234, + "detour": "", + "bind_interface": "", + "inet4_bind_address": "", + "inet6_bind_address": "", + "routing_mark": 0, "reuse_addr": false, - "connect_timeout": "5s", + "connect_timeout": "", "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, + "netns": "", "domain_resolver": "", // or {} - "network_strategy": "default", + "network_strategy": "", "network_type": [], "fallback_network_type": [], - "fallback_delay": "300ms", + "fallback_delay": "", // Deprecated - "domain_strategy": "prefer_ipv6" + "domain_strategy": "" } ``` @@ -75,6 +77,15 @@ Set netfilter routing mark. Reuse listener address. +#### connect_timeout + +Connect timeout, in golang's Duration format. + +A duration string is a possibly signed sequence of +decimal numbers, each with optional fraction and a unit suffix, +such as "300ms", "-1.5h" or "2h45m". +Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + #### tcp_fast_open Enable TCP Fast Open. @@ -91,14 +102,15 @@ Enable TCP Multi Path. Enable UDP fragmentation. -#### connect_timeout +#### netns -Connect timeout, in golang's Duration format. +!!! question "Since sing-box 1.12.0" -A duration string is a possibly signed sequence of -decimal numbers, each with optional fraction and a unit suffix, -such as "300ms", "-1.5h" or "2h45m". -Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". +!!! quote "" + + Only supported on Linux. + +Set network namespace, name or path. #### domain_resolver diff --git a/docs/configuration/shared/dial.zh.md b/docs/configuration/shared/dial.zh.md index 4a7d9562..5abd6ad9 100644 --- a/docs/configuration/shared/dial.zh.md +++ b/docs/configuration/shared/dial.zh.md @@ -5,7 +5,8 @@ icon: material/new-box !!! quote "sing-box 1.12.0 中的更改" :material-plus: [domain_resolver](#domain_resolver) - :material-delete-clock: [domain_strategy](#domain_strategy) + :material-delete-clock: [domain_strategy](#domain_strategy) + :material-plus: [netns](#netns) !!! quote "sing-box 1.11.0 中的更改" @@ -18,25 +19,26 @@ icon: material/new-box ```json { - "detour": "upstream-out", - "bind_interface": "en0", - "inet4_bind_address": "0.0.0.0", - "inet6_bind_address": "::", - "routing_mark": 1234, + "detour": "", + "bind_interface": "", + "inet4_bind_address": "", + "inet6_bind_address": "", + "routing_mark": 0, "reuse_addr": false, - "connect_timeout": "5s", + "connect_timeout": "", "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, + "netns": "", "domain_resolver": "", // 或 {} "network_strategy": "", "network_type": [], "fallback_network_type": [], - "fallback_delay": "300ms", + "fallback_delay": "", // 废弃的 - "domain_strategy": "prefer_ipv6" + "domain_strategy": "" } ``` @@ -76,6 +78,13 @@ icon: material/new-box 重用监听地址。 +#### connect_timeout + +连接超时,采用 golang 的 Duration 格式。 + +持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。 +有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。 + #### tcp_fast_open 启用 TCP Fast Open。 @@ -92,12 +101,15 @@ icon: material/new-box 启用 UDP 分段。 -#### connect_timeout +#### netns -连接超时,采用 golang 的 Duration 格式。 +!!! question "自 sing-box 1.12.0 起" -持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。 -有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。 +!!! quote "" + + 仅支持 Linux。 + +设置网络命名空间,名称或路径。 #### domain_resolver diff --git a/docs/configuration/shared/listen.md b/docs/configuration/shared/listen.md index 3e1b000f..ab6d07ce 100644 --- a/docs/configuration/shared/listen.md +++ b/docs/configuration/shared/listen.md @@ -1,7 +1,11 @@ --- -icon: material/delete-clock +icon: material/new-box --- +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [netns](#netns) + !!! quote "Changes in sing-box 1.11.0" :material-delete-clock: [sniff](#sniff) @@ -14,17 +18,18 @@ icon: material/delete-clock ```json { - "listen": "::", - "listen_port": 5353, + "listen": "", + "listen_port": 0, "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, - "udp_timeout": "5m", - "detour": "another-in", + "udp_timeout": "", + "netns": "", + "detour": "", "sniff": false, "sniff_override_destination": false, - "sniff_timeout": "300ms", - "domain_strategy": "prefer_ipv6", + "sniff_timeout": "", + "domain_strategy": "", "udp_disable_domain_unmapping": false } ``` @@ -72,6 +77,16 @@ UDP NAT expiration time. `5m` will be used by default. +#### netns + +!!! question "Since sing-box 1.12.0" + +!!! quote "" + + Only supported on Linux. + +Set network namespace, name or path. + #### detour If set, connections will be forwarded to the specified inbound. diff --git a/docs/configuration/shared/listen.zh.md b/docs/configuration/shared/listen.zh.md index 4f8ca9d6..bc67ac98 100644 --- a/docs/configuration/shared/listen.zh.md +++ b/docs/configuration/shared/listen.zh.md @@ -1,7 +1,11 @@ --- -icon: material/delete-clock +icon: material/new-box --- +!!! quote "Changes in sing-box 1.12.0" + + :material-plus: [netns](#netns) + !!! quote "sing-box 1.11.0 中的更改" :material-delete-clock: [sniff](#sniff) @@ -14,17 +18,18 @@ icon: material/delete-clock ```json { - "listen": "::", - "listen_port": 5353, + "listen": "", + "listen_port": 0, "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, - "udp_timeout": "5m", - "detour": "another-in", + "udp_timeout": "", + "netns": "", + "detour": "", "sniff": false, "sniff_override_destination": false, - "sniff_timeout": "300ms", - "domain_strategy": "prefer_ipv6", + "sniff_timeout": "", + "domain_strategy": "", "udp_disable_domain_unmapping": false } ``` @@ -73,6 +78,16 @@ UDP NAT 过期时间。 默认使用 `5m`。 +#### netns + +!!! question "自 sing-box 1.12.0 起" + +!!! quote "" + + 仅支持 Linux。 + +设置网络命名空间,名称或路径。 + #### detour 如果设置,连接将被转发到指定的入站。 diff --git a/go.mod b/go.mod index cd9d5351..70ab29bd 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.10.0 + github.com/vishvananda/netns v0.0.4 go.uber.org/zap v1.27.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.33.0 @@ -122,7 +123,6 @@ require ( github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect - github.com/vishvananda/netns v0.0.4 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/option/inbound.go b/option/inbound.go index 1cf16ff6..b704c7e3 100644 --- a/option/inbound.go +++ b/option/inbound.go @@ -68,6 +68,7 @@ type ListenOptions struct { UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragmentDefault bool `json:"-"` UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"` + NetNs string `json:"netns,omitempty"` // Deprecated: removed ProxyProtocol bool `json:"proxy_protocol,omitempty"` diff --git a/option/outbound.go b/option/outbound.go index 4abc597c..1b852d26 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -77,6 +77,7 @@ type DialerOptions struct { TCPMultiPath bool `json:"tcp_multi_path,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragmentDefault bool `json:"-"` + NetNs string `json:"netns,omitempty"` DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"` NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"` NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` diff --git a/option/simple.go b/option/simple.go index 5fda30ef..f244ba18 100644 --- a/option/simple.go +++ b/option/simple.go @@ -7,13 +7,15 @@ import ( type SocksInboundOptions struct { ListenOptions - Users []auth.User `json:"users,omitempty"` + Users []auth.User `json:"users,omitempty"` + DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"` } type HTTPMixedInboundOptions struct { ListenOptions - Users []auth.User `json:"users,omitempty"` - SetSystemProxy bool `json:"set_system_proxy,omitempty"` + Users []auth.User `json:"users,omitempty"` + DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"` + SetSystemProxy bool `json:"set_system_proxy,omitempty"` InboundTLSOptionsContainer } diff --git a/protocol/mixed/inbound.go b/protocol/mixed/inbound.go index b675a536..fe84aa01 100644 --- a/protocol/mixed/inbound.go +++ b/protocol/mixed/inbound.go @@ -85,7 +85,7 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada } switch headerBytes[0] { case socks4.Version, socks5.Version: - return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose) + return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, metadata.Source, onClose) default: return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose) } diff --git a/protocol/redirect/tproxy.go b/protocol/redirect/tproxy.go index 7860e173..f9a455e1 100644 --- a/protocol/redirect/tproxy.go +++ b/protocol/redirect/tproxy.go @@ -121,40 +121,48 @@ func (t *TProxy) NewPacketEx(buffer *buf.Buffer, oob []byte, source M.Socksaddr) t.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, M.SocksaddrFromNetIP(destination), nil) } -type tproxyPacketWriter struct { - ctx context.Context - source netip.AddrPort - destination M.Socksaddr - conn *net.UDPConn -} - func (t *TProxy) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) { ctx := log.ContextWithNewID(t.ctx) - writer := &tproxyPacketWriter{ctx: ctx, source: source.AddrPort(), destination: destination} + writer := &tproxyPacketWriter{ + ctx: ctx, + listener: t.listener, + source: source.AddrPort(), + destination: destination, + } return true, ctx, writer, func(it error) { common.Close(common.PtrOrNil(writer.conn)) } } +type tproxyPacketWriter struct { + ctx context.Context + listener *listener.Listener + source netip.AddrPort + destination M.Socksaddr + conn *net.UDPConn +} + func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { defer buffer.Release() - conn := w.conn - if w.destination == destination && conn != nil { - _, err := conn.WriteToUDPAddrPort(buffer.Bytes(), w.source) - if err != nil { - w.conn = nil + if w.listener.ListenOptions().NetNs == "" { + conn := w.conn + if w.destination == destination && conn != nil { + _, err := conn.WriteToUDPAddrPort(buffer.Bytes(), w.source) + if err != nil { + w.conn = nil + } + return err } - return err } - var listener net.ListenConfig - listener.Control = control.Append(listener.Control, control.ReuseAddr()) - listener.Control = control.Append(listener.Control, redir.TProxyWriteBack()) - packetConn, err := listener.ListenPacket(w.ctx, "udp", destination.String()) + var listenConfig net.ListenConfig + listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr()) + listenConfig.Control = control.Append(listenConfig.Control, redir.TProxyWriteBack()) + packetConn, err := w.listener.ListenPacket(listenConfig, w.ctx, "udp", destination.String()) if err != nil { return err } udpConn := packetConn.(*net.UDPConn) - if w.destination == destination { + if w.listener.ListenOptions().NetNs == "" && w.destination == destination { w.conn = udpConn } else { defer udpConn.Close() diff --git a/protocol/socks/inbound.go b/protocol/socks/inbound.go index 820c7bbb..6b828152 100644 --- a/protocol/socks/inbound.go +++ b/protocol/socks/inbound.go @@ -62,7 +62,7 @@ func (h *Inbound) Close() error { } func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { - err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose) + err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, metadata.Source, onClose) N.CloseOnHandshakeFailure(conn, onClose, err) if err != nil { if E.IsClosedOrCanceled(err) { diff --git a/protocol/tor/proxy.go b/protocol/tor/proxy.go index feab7971..6b7db7c3 100644 --- a/protocol/tor/proxy.go +++ b/protocol/tor/proxy.go @@ -99,7 +99,7 @@ func (l *ProxyListener) acceptLoop() { } func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error { - return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, M.SocksaddrFromNet(conn.RemoteAddr()), nil) + return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, nil, M.SocksaddrFromNet(conn.RemoteAddr()), nil) } func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { From c5df6c8c49186621ae98f466d53946b0ac83a9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 20 Mar 2025 20:48:23 +0800 Subject: [PATCH 67/94] Explicitly reject detour to empty direct outbounds --- adapter/dns.go | 2 +- common/dialer/detour.go | 36 ++++++++++++++++++++++++------ common/dialer/dialer.go | 3 ++- dns/router.go | 2 +- dns/transport/dhcp/dhcp.go | 10 ++------- dns/transport/hosts/hosts.go | 7 +++++- dns/transport/https.go | 11 ++++++++- dns/transport/local/local.go | 7 +++++- dns/transport/quic/http3.go | 8 +++++-- dns/transport/quic/quic.go | 7 +++++- dns/transport/tcp.go | 11 ++++++++- dns/transport/tls.go | 11 ++++++++- dns/transport/udp.go | 11 ++++++++- dns/transport_dialer.go | 16 ++++++++------ dns/transport_manager.go | 2 +- experimental/libbox/dns.go | 7 +++++- option/dns.go | 43 ++++++++++++++---------------------- option/rule.go | 3 +-- option/rule_dns.go | 1 - protocol/direct/outbound.go | 9 ++++++++ 20 files changed, 142 insertions(+), 65 deletions(-) diff --git a/adapter/dns.go b/adapter/dns.go index e0f381b8..942f3566 100644 --- a/adapter/dns.go +++ b/adapter/dns.go @@ -45,10 +45,10 @@ type RDRCStore interface { } type DNSTransport interface { + Lifecycle Type() string Tag() string Dependencies() []string - Reset() Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) } diff --git a/common/dialer/detour.go b/common/dialer/detour.go index e4a46049..5c0b552b 100644 --- a/common/dialer/detour.go +++ b/common/dialer/detour.go @@ -6,26 +6,39 @@ import ( "sync" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) +type DirectDialer interface { + IsEmpty() bool +} + type DetourDialer struct { outboundManager adapter.OutboundManager detour string + legacyDNSDialer bool dialer N.Dialer initOnce sync.Once initErr error } -func NewDetour(outboundManager adapter.OutboundManager, detour string) N.Dialer { - return &DetourDialer{outboundManager: outboundManager, detour: detour} +func NewDetour(outboundManager adapter.OutboundManager, detour string, legacyDNSDialer bool) N.Dialer { + return &DetourDialer{ + outboundManager: outboundManager, + detour: detour, + legacyDNSDialer: legacyDNSDialer, + } } -func (d *DetourDialer) Start() error { - _, err := d.Dialer() - return err +func InitializeDetour(dialer N.Dialer) error { + detourDialer, isDetour := common.Cast[*DetourDialer](dialer) + if !isDetour { + return nil + } + return common.Error(detourDialer.Dialer()) } func (d *DetourDialer) Dialer() (N.Dialer, error) { @@ -34,11 +47,20 @@ func (d *DetourDialer) Dialer() (N.Dialer, error) { } func (d *DetourDialer) init() { - var loaded bool - d.dialer, loaded = d.outboundManager.Outbound(d.detour) + dialer, loaded := d.outboundManager.Outbound(d.detour) if !loaded { d.initErr = E.New("outbound detour not found: ", d.detour) + return } + if !d.legacyDNSDialer { + if directDialer, isDirect := dialer.(DirectDialer); isDirect { + if directDialer.IsEmpty() { + d.initErr = E.New("detour to an empty direct outbound makes no sense") + return + } + } + } + d.dialer = dialer } func (d *DetourDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index b93b7096..88e16740 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -23,6 +23,7 @@ type Options struct { DirectResolver bool ResolverOnDetour bool NewDialer bool + LegacyDNSDialer bool } // TODO: merge with NewWithOptions @@ -45,7 +46,7 @@ func NewWithOptions(options Options) (N.Dialer, error) { if outboundManager == nil { return nil, E.New("missing outbound manager") } - dialer = NewDetour(outboundManager, dialOptions.Detour) + dialer = NewDetour(outboundManager, dialOptions.Detour, options.LegacyDNSDialer) } else { dialer, err = NewDefault(options.Context, dialOptions) if err != nil { diff --git a/dns/router.go b/dns/router.go index 42cebd23..44edadbd 100644 --- a/dns/router.go +++ b/dns/router.go @@ -449,6 +449,6 @@ func (r *Router) LookupReverseMapping(ip netip.Addr) (string, bool) { func (r *Router) ResetNetwork() { r.ClearCache() for _, transport := range r.transport.Transports() { - transport.Reset() + transport.Close() } } diff --git a/dns/transport/dhcp/dhcp.go b/dns/transport/dhcp/dhcp.go index c75d7369..92dd1f8b 100644 --- a/dns/transport/dhcp/dhcp.go +++ b/dns/transport/dhcp/dhcp.go @@ -81,7 +81,7 @@ func (t *Transport) Start(stage adapter.StartStage) error { func (t *Transport) Close() error { for _, transport := range t.transports { - transport.Reset() + transport.Close() } if t.interfaceCallback != nil { t.networkManager.InterfaceMonitor().UnregisterCallback(t.interfaceCallback) @@ -89,12 +89,6 @@ func (t *Transport) Close() error { return nil } -func (t *Transport) Reset() { - for _, transport := range t.transports { - transport.Reset() - } -} - func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { err := t.fetchServers() if err != nil { @@ -252,7 +246,7 @@ func (t *Transport) recreateServers(iface *control.Interface, serverAddrs []M.So transports = append(transports, transport.NewUDPRaw(t.logger, t.TransportAdapter, serverDialer, serverAddr)) } for _, transport := range t.transports { - transport.Reset() + transport.Close() } t.transports = transports return nil diff --git a/dns/transport/hosts/hosts.go b/dns/transport/hosts/hosts.go index 0a1dd395..a5eecb40 100644 --- a/dns/transport/hosts/hosts.go +++ b/dns/transport/hosts/hosts.go @@ -51,7 +51,12 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt }, nil } -func (t *Transport) Reset() { +func (t *Transport) Start(stage adapter.StartStage) error { + return nil +} + +func (t *Transport) Close() error { + return nil } func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/dns/transport/https.go b/dns/transport/https.go index bd150d5f..1750fd26 100644 --- a/dns/transport/https.go +++ b/dns/transport/https.go @@ -10,6 +10,7 @@ import ( "strconv" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/dns" @@ -149,9 +150,17 @@ func NewHTTPSRaw( } } -func (t *HTTPSTransport) Reset() { +func (t *HTTPSTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return dialer.InitializeDetour(t.dialer) +} + +func (t *HTTPSTransport) Close() error { t.transport.CloseIdleConnections() t.transport = t.transport.Clone() + return nil } func (t *HTTPSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index 4e05ff02..f50405a5 100644 --- a/dns/transport/local/local.go +++ b/dns/transport/local/local.go @@ -40,7 +40,12 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt }, nil } -func (t *Transport) Reset() { +func (t *Transport) Start(stage adapter.StartStage) error { + return nil +} + +func (t *Transport) Close() error { + return nil } func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/dns/transport/quic/http3.go b/dns/transport/quic/http3.go index e2a75b50..0d871741 100644 --- a/dns/transport/quic/http3.go +++ b/dns/transport/quic/http3.go @@ -111,8 +111,12 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options }, nil } -func (t *HTTP3Transport) Reset() { - t.transport.Close() +func (t *HTTP3Transport) Start(stage adapter.StartStage) error { + return nil +} + +func (t *HTTP3Transport) Close() error { + return t.transport.Close() } func (t *HTTP3Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/dns/transport/quic/quic.go b/dns/transport/quic/quic.go index 4ae9ac16..fc5101ee 100644 --- a/dns/transport/quic/quic.go +++ b/dns/transport/quic/quic.go @@ -68,13 +68,18 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options }, nil } -func (t *Transport) Reset() { +func (t *Transport) Start(stage adapter.StartStage) error { + return nil +} + +func (t *Transport) Close() error { t.access.Lock() defer t.access.Unlock() connection := t.connection if connection != nil { connection.CloseWithError(0, "") } + return nil } func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/dns/transport/tcp.go b/dns/transport/tcp.go index 4abeee2f..a814c030 100644 --- a/dns/transport/tcp.go +++ b/dns/transport/tcp.go @@ -6,6 +6,7 @@ import ( "io" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/log" @@ -46,7 +47,15 @@ func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options o }, nil } -func (t *TCPTransport) Reset() { +func (t *TCPTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return dialer.InitializeDetour(t.dialer) +} + +func (t *TCPTransport) Close() error { + return nil } func (t *TCPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/dns/transport/tls.go b/dns/transport/tls.go index ce88d425..a99bf2f7 100644 --- a/dns/transport/tls.go +++ b/dns/transport/tls.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/dns" @@ -65,13 +66,21 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o }, nil } -func (t *TLSTransport) Reset() { +func (t *TLSTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return dialer.InitializeDetour(t.dialer) +} + +func (t *TLSTransport) Close() error { t.access.Lock() defer t.access.Unlock() for connection := t.connections.Front(); connection != nil; connection = connection.Next() { connection.Value.Close() } t.connections.Init() + return nil } func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/dns/transport/udp.go b/dns/transport/udp.go index 8c905c4c..8d9f0515 100644 --- a/dns/transport/udp.go +++ b/dns/transport/udp.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/log" @@ -64,11 +65,19 @@ func NewUDPRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer } } -func (t *UDPTransport) Reset() { +func (t *UDPTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return dialer.InitializeDetour(t.dialer) +} + +func (t *UDPTransport) Close() error { t.access.Lock() defer t.access.Unlock() close(t.done) t.done = make(chan struct{}) + return nil } func (t *UDPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/dns/transport_dialer.go b/dns/transport_dialer.go index 0b15c7ea..b3ee8082 100644 --- a/dns/transport_dialer.go +++ b/dns/transport_dialer.go @@ -20,9 +20,10 @@ func NewLocalDialer(ctx context.Context, options option.LocalDNSServerOptions) ( return dialer.NewDefaultOutbound(ctx), nil } else { return dialer.NewWithOptions(dialer.Options{ - Context: ctx, - Options: options.DialerOptions, - DirectResolver: true, + Context: ctx, + Options: options.DialerOptions, + DirectResolver: true, + LegacyDNSDialer: options.Legacy, }) } } @@ -43,10 +44,11 @@ func NewRemoteDialer(ctx context.Context, options option.RemoteDNSServerOptions) return transportDialer, nil } else { return dialer.NewWithOptions(dialer.Options{ - Context: ctx, - Options: options.DialerOptions, - RemoteIsDomain: options.ServerIsDomain(), - DirectResolver: true, + Context: ctx, + Options: options.DialerOptions, + RemoteIsDomain: options.ServerIsDomain(), + DirectResolver: true, + LegacyDNSDialer: options.Legacy, }) } } diff --git a/dns/transport_manager.go b/dns/transport_manager.go index 8666a1b9..dff886df 100644 --- a/dns/transport_manager.go +++ b/dns/transport_manager.go @@ -225,7 +225,7 @@ func (m *TransportManager) Remove(tag string) error { } } if started { - transport.Reset() + transport.Close() } return nil } diff --git a/experimental/libbox/dns.go b/experimental/libbox/dns.go index 7e143442..d5c97b7e 100644 --- a/experimental/libbox/dns.go +++ b/experimental/libbox/dns.go @@ -38,7 +38,12 @@ func newPlatformTransport(iif LocalDNSTransport, tag string, options option.Loca } } -func (p *platformTransport) Reset() { +func (p *platformTransport) Start(stage adapter.StartStage) error { + return nil +} + +func (p *platformTransport) Close() error { + return nil } func (p *platformTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/option/dns.go b/option/dns.go index 1a42b0f4..f303b894 100644 --- a/option/dns.go +++ b/option/dns.go @@ -191,34 +191,24 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error { serverType = C.DNSTypeUDP } } - var remoteOptions RemoteDNSServerOptions - if options.Detour == "" { - remoteOptions = RemoteDNSServerOptions{ - LocalDNSServerOptions: LocalDNSServerOptions{ - LegacyStrategy: options.Strategy, - LegacyDefaultDialer: options.Detour == "", - LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}), - }, - LegacyAddressResolver: options.AddressResolver, - LegacyAddressStrategy: options.AddressStrategy, - LegacyAddressFallbackDelay: options.AddressFallbackDelay, - } - } else { - remoteOptions = RemoteDNSServerOptions{ - LocalDNSServerOptions: LocalDNSServerOptions{ - DialerOptions: DialerOptions{ - Detour: options.Detour, - DomainResolver: &DomainResolveOptions{ - Server: options.AddressResolver, - Strategy: options.AddressStrategy, - }, - FallbackDelay: options.AddressFallbackDelay, + remoteOptions := RemoteDNSServerOptions{ + LocalDNSServerOptions: LocalDNSServerOptions{ + DialerOptions: DialerOptions{ + Detour: options.Detour, + DomainResolver: &DomainResolveOptions{ + Server: options.AddressResolver, + Strategy: options.AddressStrategy, }, - LegacyStrategy: options.Strategy, - LegacyDefaultDialer: options.Detour == "", - LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}), + FallbackDelay: options.AddressFallbackDelay, }, - } + Legacy: true, + LegacyStrategy: options.Strategy, + LegacyDefaultDialer: options.Detour == "", + LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}), + }, + LegacyAddressResolver: options.AddressResolver, + LegacyAddressStrategy: options.AddressStrategy, + LegacyAddressFallbackDelay: options.AddressFallbackDelay, } switch serverType { case C.DNSTypeLocal: @@ -362,6 +352,7 @@ type HostsDNSServerOptions struct { type LocalDNSServerOptions struct { DialerOptions + Legacy bool `json:"-"` LegacyStrategy DomainStrategy `json:"-"` LegacyDefaultDialer bool `json:"-"` LegacyClientSubnet netip.Prefix `json:"-"` diff --git a/option/rule.go b/option/rule.go index b769dab8..41bcc126 100644 --- a/option/rule.go +++ b/option/rule.go @@ -125,10 +125,9 @@ func (r *DefaultRule) UnmarshalJSON(data []byte) error { return badjson.UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction) } -func (r *DefaultRule) IsValid() bool { +func (r DefaultRule) IsValid() bool { var defaultValue DefaultRule defaultValue.Invert = r.Invert - defaultValue.Action = r.Action return !reflect.DeepEqual(r, defaultValue) } diff --git a/option/rule_dns.go b/option/rule_dns.go index 9d6fb138..87b15017 100644 --- a/option/rule_dns.go +++ b/option/rule_dns.go @@ -132,7 +132,6 @@ func (r *DefaultDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte) func (r DefaultDNSRule) IsValid() bool { var defaultValue DefaultDNSRule defaultValue.Invert = r.Invert - defaultValue.DNSRuleAction = r.DNSRuleAction return !reflect.DeepEqual(r, defaultValue) } diff --git a/protocol/direct/outbound.go b/protocol/direct/outbound.go index 7ad756f2..9cd1490b 100644 --- a/protocol/direct/outbound.go +++ b/protocol/direct/outbound.go @@ -4,6 +4,7 @@ import ( "context" "net" "net/netip" + "reflect" "time" "github.com/sagernet/sing-box/adapter" @@ -27,6 +28,7 @@ func RegisterOutbound(registry *outbound.Registry) { var ( _ N.ParallelDialer = (*Outbound)(nil) _ dialer.ParallelNetworkDialer = (*Outbound)(nil) + _ dialer.DirectDialer = (*Outbound)(nil) ) type Outbound struct { @@ -37,6 +39,7 @@ type Outbound struct { fallbackDelay time.Duration overrideOption int overrideDestination M.Socksaddr + isEmpty bool // loopBack *loopBackDetector } @@ -56,6 +59,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL domainStrategy: C.DomainStrategy(options.DomainStrategy), fallbackDelay: time.Duration(options.FallbackDelay), dialer: outboundDialer.(dialer.ParallelInterfaceDialer), + //nolint:staticcheck + isEmpty: reflect.DeepEqual(options.DialerOptions, option.DialerOptions{UDPFragmentDefault: true}) && options.OverrideAddress == "" && options.OverridePort == 0, // loopBack: newLoopBackDetector(router), } //nolint:staticcheck @@ -242,6 +247,10 @@ func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M. return conn, newDestination, nil } +func (h *Outbound) IsEmpty() bool { + return h.isEmpty +} + /*func (h *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) { return E.New("reject loopback connection to ", metadata.Destination) From 20dd4b74c65b9aba7ce2a9a1eb4b272c4742227e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 21 Mar 2025 15:51:58 +0800 Subject: [PATCH 68/94] release: Do not build tailscale on iOS and tvOS --- Makefile | 4 ++-- cmd/internal/build_libbox/main.go | 26 +++++++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 68e6ffa1..0a5a85b2 100644 --- a/Makefile +++ b/Makefile @@ -233,8 +233,8 @@ lib: go run ./cmd/internal/build_libbox -target ios lib_install: - go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.4 - go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.4 + go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.6 + go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.6 docs: venv/bin/mkdocs serve diff --git a/cmd/internal/build_libbox/main.go b/cmd/internal/build_libbox/main.go index cad3662b..8f82ff36 100644 --- a/cmd/internal/build_libbox/main.go +++ b/cmd/internal/build_libbox/main.go @@ -45,6 +45,7 @@ var ( debugFlags []string sharedTags []string iosTags []string + memcTags []string debugTags []string ) @@ -58,8 +59,9 @@ func init() { sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=") debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag) - sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_tailscale") + sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api") iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack") + memcTags = append(memcTags, "with_tailscale") debugTags = append(debugTags, "debug") } @@ -99,18 +101,19 @@ func buildAndroid() { "-javapkg=io.nekohasekai", "-libname=box", } + if !debugEnabled { args = append(args, sharedFlags...) } else { args = append(args, debugFlags...) } - args = append(args, "-tags") - if !debugEnabled { - args = append(args, strings.Join(sharedTags, ",")) - } else { - args = append(args, strings.Join(append(sharedTags, debugTags...), ",")) + tags := append(sharedTags, memcTags...) + if debugEnabled { + tags = append(tags, debugTags...) } + + args = append(args, "-tags", strings.Join(tags, ",")) args = append(args, "./experimental/libbox") command := exec.Command(build_shared.GoBinPath+"/gomobile", args...) @@ -148,7 +151,9 @@ func buildApple() { "-v", "-target", bindTarget, "-libname=box", + "-tags-macos=" + strings.Join(memcTags, ","), } + if !debugEnabled { args = append(args, sharedFlags...) } else { @@ -156,12 +161,11 @@ func buildApple() { } tags := append(sharedTags, iosTags...) - args = append(args, "-tags") - if !debugEnabled { - args = append(args, strings.Join(tags, ",")) - } else { - args = append(args, strings.Join(append(tags, debugTags...), ",")) + if debugEnabled { + tags = append(tags, debugTags...) } + + args = append(args, "-tags", strings.Join(tags, ",")) args = append(args, "./experimental/libbox") command := exec.Command(build_shared.GoBinPath+"/gomobile", args...) From 0c6b7e7d16a5f8765a77252f735448e29bfb02c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 24 Mar 2025 20:18:39 +0800 Subject: [PATCH 69/94] Update gVisor to 20250319.0 --- Makefile | 9 +-------- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 0a5a85b2..0de2c8f9 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,6 @@ NAME = sing-box COMMIT = $(shell git rev-parse --short HEAD) -TAGS_GO120 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api,with_quic,with_utls -TAGS_GO123 = with_tailscale -TAGS ?= $(TAGS_GO120),$(TAGS_GO123) +TAGS ?= with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api,with_quic,with_utls,with_tailscale TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_utls,with_reality_server GOHOSTOS = $(shell go env GOHOSTOS) @@ -20,11 +18,6 @@ build: export GOTOOLCHAIN=local && \ go build $(MAIN_PARAMS) $(MAIN) -ci_build_go120: - export GOTOOLCHAIN=local && \ - go build $(PARAMS) $(MAIN) && \ - go build $(PARAMS) -tags "$(TAGS_GO120)" $(MAIN) - ci_build: export GOTOOLCHAIN=local && \ go build $(PARAMS) $(MAIN) && \ diff --git a/go.mod b/go.mod index 70ab29bd..71192c36 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/gomobile v0.1.4 - github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff + github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb github.com/sagernet/quic-go v0.49.0-beta.1 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a diff --git a/go.sum b/go.sum index d217a5c2..6c98c317 100644 --- a/go.sum +++ b/go.sum @@ -167,8 +167,8 @@ github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQ github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY= github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E= -github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs= -github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw= +github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38= +github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= From d1a11df54f763867dceb4b05e657da0be97eb0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 24 Mar 2025 20:23:55 +0800 Subject: [PATCH 70/94] Fail when default DNS server not found --- dns/transport_manager.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dns/transport_manager.go b/dns/transport_manager.go index dff886df..f41c9f9e 100644 --- a/dns/transport_manager.go +++ b/dns/transport_manager.go @@ -59,6 +59,9 @@ func (m *TransportManager) Start(stage adapter.StartStage) error { transports := m.transports m.access.Unlock() if stage == adapter.StartStateStart { + if m.defaultTag != "" && m.defaultTransport == nil { + return E.New("default DNS server not found: ", m.defaultTag) + } return m.startTransports(m.transports) } else { for _, outbound := range transports { From 8b27e1f23f7bbb5ba5457872491a3c12e5ea418d Mon Sep 17 00:00:00 2001 From: Rambling2076 Date: Wed, 26 Mar 2025 05:22:43 +0000 Subject: [PATCH 71/94] Fix missing `with_tailscale` in Dockerfile Signed-off-by: Rambling2076 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c9b32b8e..b2700b59 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN set -ex \ && export COMMIT=$(git rev-parse --short HEAD) \ && export VERSION=$(go run ./cmd/internal/read_tag) \ && go build -v -trimpath -tags \ - "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api" \ + "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api,with_tailscale" \ -o /go/bin/sing-box \ -ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid=" \ ./cmd/sing-box From f0c34647573bf4dad0cfca2084fd2d769562666b Mon Sep 17 00:00:00 2001 From: anytls <198425817+anytls@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:06:37 +0800 Subject: [PATCH 72/94] Update anytls Co-authored-by: anytls --- docs/configuration/inbound/anytls.md | 6 +++--- docs/configuration/inbound/anytls.zh.md | 6 +++--- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/configuration/inbound/anytls.md b/docs/configuration/inbound/anytls.md index 7eca9434..55790810 100644 --- a/docs/configuration/inbound/anytls.md +++ b/docs/configuration/inbound/anytls.md @@ -44,10 +44,10 @@ Default padding scheme: ``` stop=8 -0=34-120 +0=30-30 1=100-400 -2=400-500,c,500-1000,c,400-500,c,500-1000,c,500-1000,c,400-500 -3=500-1000 +2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000 +3=9-9,500-1000 4=500-1000 5=500-1000 6=500-1000 diff --git a/docs/configuration/inbound/anytls.zh.md b/docs/configuration/inbound/anytls.zh.md index 5c119b72..099da777 100644 --- a/docs/configuration/inbound/anytls.zh.md +++ b/docs/configuration/inbound/anytls.zh.md @@ -44,10 +44,10 @@ AnyTLS 填充方案行数组。 ``` stop=8 -0=34-120 +0=30-30 1=100-400 -2=400-500,c,500-1000,c,400-500,c,500-1000,c,500-1000,c,400-500 -3=500-1000 +2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000 +3=9-9,500-1000 4=500-1000 5=500-1000 6=500-1000 diff --git a/go.mod b/go.mod index 71192c36..6d1b2bd9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/sagernet/sing-box go 1.23.1 require ( - github.com/anytls/sing-anytls v0.0.6 + github.com/anytls/sing-anytls v0.0.7 github.com/caddyserver/certmagic v0.21.7 github.com/cloudflare/circl v1.6.0 github.com/cretz/bine v0.2.0 diff --git a/go.sum b/go.sum index 6c98c317..4f68652f 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/anytls/sing-anytls v0.0.6 h1:UatIjl/OvzWQGXQ1I2bAIkabL9WtihW0fA7G+DXGBUg= -github.com/anytls/sing-anytls v0.0.6/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/anytls/sing-anytls v0.0.7 h1:0Q5dHNB2sqkFAWZCyK2vjQ/ckI5Iz3V/Frf3k7mBrGc= +github.com/anytls/sing-anytls v0.0.7/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= From 883cd0eb6253570d1f09224ecdb64784d3a34729 Mon Sep 17 00:00:00 2001 From: dyhkwong <50692134+dyhkwong@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:19:57 +0800 Subject: [PATCH 73/94] Fix DNS over QUIC stream close --- dns/transport/quic/quic.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dns/transport/quic/quic.go b/dns/transport/quic/quic.go index fc5101ee..18f8b9fe 100644 --- a/dns/transport/quic/quic.go +++ b/dns/transport/quic/quic.go @@ -140,12 +140,12 @@ func (t *Transport) exchange(ctx context.Context, message *mDNS.Msg, conn quic.C if err != nil { return nil, err } - defer stream.Close() - defer stream.CancelRead(0) err = transport.WriteMessage(stream, 0, message) if err != nil { + stream.Close() return nil, err } + stream.Close() return transport.ReadMessage(stream) } From 407f2dad759d8f109381810c7975f615de684b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 28 Mar 2025 23:20:45 +0800 Subject: [PATCH 74/94] Fix Tailscale dialer --- common/dialer/dialer.go | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- protocol/tailscale/endpoint.go | 17 +++++++++++++++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index 88e16740..bfd13784 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -102,12 +102,12 @@ func NewWithOptions(options Options) (N.Dialer, error) { } dnsQueryOptions.Transport = transport resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay) - } else if options.NewDialer { - return nil, E.New("missing domain resolver for domain server address") } else { transports := dnsTransport.Transports() if len(transports) < 2 { dnsQueryOptions.Transport = dnsTransport.Default() + } else if options.NewDialer { + return nil, E.New("missing domain resolver for domain server address") } else { deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) } diff --git a/go.mod b/go.mod index 6d1b2bd9..a9c8a131 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/sagernet/sing-tun v0.6.4 github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 - github.com/sagernet/tailscale v1.80.3-mod.0 + github.com/sagernet/tailscale v1.80.3-mod.2 github.com/sagernet/utls v1.6.7 github.com/sagernet/wireguard-go v0.0.1-beta.7 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 diff --git a/go.sum b/go.sum index 4f68652f..e95f0463 100644 --- a/go.sum +++ b/go.sum @@ -196,8 +196,8 @@ github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2 github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= -github.com/sagernet/tailscale v1.80.3-mod.0 h1:oHIdivbR/yxoiA9d3a2rRlhYn2shY9XVF35Rr8jW508= -github.com/sagernet/tailscale v1.80.3-mod.0/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= +github.com/sagernet/tailscale v1.80.3-mod.2 h1:hT0CI74q727EuCcgQ+T4pvon8V0aoi4vTAxah7GsNMQ= +github.com/sagernet/tailscale v1.80.3-mod.2/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI= diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index f24f931f..26aa6ebd 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -2,8 +2,10 @@ package tailscale import ( "context" + "crypto/tls" "fmt" "net" + "net/http" "net/netip" "net/url" "os" @@ -147,6 +149,17 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions()) }, DNS: &dnsConfigurtor{}, + HTTPClient: &http.Client{ + Transport: &http.Transport{ + ForceAttemptHTTP2: true, + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + return outboundDialer.DialContext(ctx, network, M.ParseSocksaddr(address)) + }, + TLSClientConfig: &tls.Config{ + RootCAs: adapter.RootPoolFromContext(ctx), + }, + }, + }, } return &Endpoint{ Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP}, nil), @@ -446,6 +459,10 @@ func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) } +func (t *Endpoint) Server() *tsnet.Server { + return t.server +} + func addressFromAddr(destination netip.Addr) tcpip.Address { if destination.Is6() { return tcpip.AddrFrom16(destination.As16()) From fd6e5abb86348782a14d370b863ead2938dfe3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 1 Apr 2025 21:32:49 +0800 Subject: [PATCH 75/94] Allow direct outbounds without `domain_resolver` --- common/dialer/dialer.go | 3 ++- protocol/direct/outbound.go | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index bfd13784..7bf05d1b 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -24,6 +24,7 @@ type Options struct { ResolverOnDetour bool NewDialer bool LegacyDNSDialer bool + DirectOutbound bool } // TODO: merge with NewWithOptions @@ -108,7 +109,7 @@ func NewWithOptions(options Options) (N.Dialer, error) { dnsQueryOptions.Transport = dnsTransport.Default() } else if options.NewDialer { return nil, E.New("missing domain resolver for domain server address") - } else { + } else if !options.DirectOutbound { deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) } } diff --git a/protocol/direct/outbound.go b/protocol/direct/outbound.go index 9cd1490b..84838bc0 100644 --- a/protocol/direct/outbound.go +++ b/protocol/direct/outbound.go @@ -48,7 +48,12 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if options.Detour != "" { return nil, E.New("`detour` is not supported in direct context") } - outboundDialer, err := dialer.New(ctx, options.DialerOptions, true) + outboundDialer, err := dialer.NewWithOptions(dialer.Options{ + Context: ctx, + Options: options.DialerOptions, + RemoteIsDomain: true, + DirectOutbound: true, + }) if err != nil { return nil, err } From 839c4a31fc918abcd7de1c313e176b7f0a4bf122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 3 Apr 2025 16:23:26 +0800 Subject: [PATCH 76/94] release: Update Go to 1.24.2 --- .github/workflows/build.yml | 10 +++++----- .github/workflows/lint.yml | 2 +- .github/workflows/linux.yml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f6b2af9b..65a87109 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24 + go-version: ^1.24.2 - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -109,7 +109,7 @@ jobs: if: ${{ ! matrix.legacy_go }} uses: actions/setup-go@v5 with: - go-version: ^1.24 + go-version: ^1.24.2 - name: Cache Legacy Go if: matrix.require_legacy_go id: cache-legacy-go @@ -294,7 +294,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24 + go-version: ^1.24.2 - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -374,7 +374,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24 + go-version: ^1.24.2 - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -472,7 +472,7 @@ jobs: if: matrix.if uses: actions/setup-go@v5 with: - go-version: ^1.24 + go-version: ^1.24.2 - name: Setup Xcode stable if: matrix.if && github.ref == 'refs/heads/main-next' run: |- diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c7e5d6e6..022e4664 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24 + go-version: ^1.24.2 - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 7393e654..51292cee 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24 + go-version: ^1.24.2 - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -66,7 +66,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.24 + go-version: ^1.24.2 - name: Setup Android NDK if: matrix.os == 'android' uses: nttld/setup-ndk@v1 From 7185c03f011d9d168d3e2236c27d3e775264cc8a Mon Sep 17 00:00:00 2001 From: ReleTor <191429954+ReleTor@users.noreply.github.com> Date: Sun, 6 Apr 2025 20:33:13 +0800 Subject: [PATCH 77/94] Fix fetch ECH configs --- common/tls/ech.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/tls/ech.go b/common/tls/ech.go index 880ca27c..ddb9b5dd 100644 --- a/common/tls/ech.go +++ b/common/tls/ech.go @@ -123,6 +123,7 @@ func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) if response.Rcode != mDNS.RcodeSuccess { return nil, E.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list") } + match: for _, rr := range response.Answer { switch resource := rr.(type) { case *mDNS.HTTPS: @@ -133,11 +134,14 @@ func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) return nil, E.Cause(err, "decode ECH config") } s.config.EncryptedClientHelloConfigList = echConfigList + break match } } } } - return nil, E.New("no ECH config found in DNS records") + if len(s.config.EncryptedClientHelloConfigList) == 0 { + return nil, E.New("no ECH config found in DNS records") + } } tlsConn, err := s.Client(conn) if err != nil { From 5155f95ace00045ec52c5c13d5f48c987ee52f2b Mon Sep 17 00:00:00 2001 From: iikira <2571583272@qq.com> Date: Sun, 6 Apr 2025 22:03:24 +0800 Subject: [PATCH 78/94] Fix UDP DNS server crash Signed-off-by: iikira --- dns/transport/udp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/transport/udp.go b/dns/transport/udp.go index 8d9f0515..ec17c71b 100644 --- a/dns/transport/udp.go +++ b/dns/transport/udp.go @@ -212,8 +212,8 @@ type dnsConnection struct { func (c *dnsConnection) Close(err error) { c.closeOnce.Do(func() { - close(c.done) c.err = err + close(c.done) }) c.Conn.Close() } From ed3aca4208c5f5f4dcac75e4ad1d577922472d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 8 Apr 2025 16:15:15 +0800 Subject: [PATCH 79/94] release: Skip override version for iOS --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 65a87109..3bb8a435 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -549,10 +549,13 @@ jobs: MACOS_PROJECT_VERSION=$(go run -v ./cmd/internal/app_store_connect next_macos_project_version) echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION" echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION" >> "$GITHUB_ENV" + - name: Update version + if: matrix.if && matrix.name != 'iOS' + run: |- + go run -v ./cmd/internal/update_apple_version --ci - name: Build if: matrix.if run: |- - go run -v ./cmd/internal/update_apple_version --ci cd clients/apple xcodebuild archive \ -scheme "${{ matrix.scheme }}" \ From 3722fc4987fe3c17afa84199493455834db38d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 8 Apr 2025 21:51:35 +0800 Subject: [PATCH 80/94] Fix DNS dialer --- common/dialer/default.go | 34 ++++++++++++++++++++-------------- route/network.go | 12 +++++++++++- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/common/dialer/default.go b/common/dialer/default.go index e8ae0f13..b076f049 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -71,18 +71,8 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial listener.Control = control.Append(listener.Control, bindFunc) } if options.RoutingMark > 0 { - dialer.Control = control.Append(dialer.Control, control.RoutingMark(uint32(options.RoutingMark))) - listener.Control = control.Append(listener.Control, control.RoutingMark(uint32(options.RoutingMark))) - } - if networkManager != nil { - autoRedirectOutputMark := networkManager.AutoRedirectOutputMark() - if autoRedirectOutputMark > 0 { - if options.RoutingMark > 0 { - return nil, E.New("`routing_mark` is conflict with `tun.auto_redirect` with `tun.route_[_exclude]_address_set") - } - dialer.Control = control.Append(dialer.Control, control.RoutingMark(autoRedirectOutputMark)) - listener.Control = control.Append(listener.Control, control.RoutingMark(autoRedirectOutputMark)) - } + dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, uint32(options.RoutingMark), false)) + listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, uint32(options.RoutingMark), false)) } disableDefaultBind := options.BindInterface != "" || options.Inet4BindAddress != nil || options.Inet6BindAddress != nil if disableDefaultBind || options.TCPFastOpen { @@ -127,8 +117,8 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial } } if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 { - dialer.Control = control.Append(dialer.Control, control.RoutingMark(defaultOptions.RoutingMark)) - listener.Control = control.Append(listener.Control, control.RoutingMark(defaultOptions.RoutingMark)) + dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true)) + listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true)) } } if options.ReuseAddr { @@ -210,6 +200,22 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial }, nil } +func setMarkWrapper(networkManager adapter.NetworkManager, mark uint32, isDefault bool) control.Func { + if networkManager == nil { + return control.RoutingMark(mark) + } + return func(network, address string, conn syscall.RawConn) error { + if networkManager.AutoRedirectOutputMark() != 0 { + if isDefault { + return E.New("`route.default_mark` is conflict with `tun.auto_redirect`") + } else { + return E.New("`routing_mark` is conflict with `tun.auto_redirect`") + } + } + return control.RoutingMark(mark)(network, address, conn) + } +} + func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) { if !address.IsValid() { return nil, E.New("invalid address") diff --git a/route/network.go b/route/network.go index a494ce09..6c2e995f 100644 --- a/route/network.go +++ b/route/network.go @@ -303,7 +303,7 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func { if r.interfaceMonitor == nil { return nil } - return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) { + bindFunc := control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) { remoteAddr := M.ParseSocksaddr(address).Addr if remoteAddr.IsValid() { iif, err := r.interfaceFinder.ByAddr(remoteAddr) @@ -317,6 +317,16 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func { } return defaultInterface.Name, defaultInterface.Index, nil }) + return func(network, address string, conn syscall.RawConn) error { + err := bindFunc(network, address, conn) + if err != nil { + return err + } + if r.autoRedirectOutputMark > 0 { + return control.RoutingMark(r.autoRedirectOutputMark)(network, address, conn) + } + return nil + } } } From 01b3d719f515de1491c012dbdfe22759c63bed23 Mon Sep 17 00:00:00 2001 From: anytls <198425817+anytls@users.noreply.github.com> Date: Wed, 9 Apr 2025 12:36:39 +0900 Subject: [PATCH 81/94] Update anytls Co-authored-by: anytls --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a9c8a131..b565177f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/sagernet/sing-box go 1.23.1 require ( - github.com/anytls/sing-anytls v0.0.7 + github.com/anytls/sing-anytls v0.0.8 github.com/caddyserver/certmagic v0.21.7 github.com/cloudflare/circl v1.6.0 github.com/cretz/bine v0.2.0 diff --git a/go.sum b/go.sum index e95f0463..e97e77ef 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/anytls/sing-anytls v0.0.7 h1:0Q5dHNB2sqkFAWZCyK2vjQ/ckI5Iz3V/Frf3k7mBrGc= -github.com/anytls/sing-anytls v0.0.7/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/anytls/sing-anytls v0.0.8 h1:1u/fnH1HoeeMV5mX7/eUOjLBvPdkd1UJRmXiRi6Vymc= +github.com/anytls/sing-anytls v0.0.8/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= From 013c707ee8ef2ef993f2c5494be5a28cd3333e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 12 Apr 2025 21:26:22 +0800 Subject: [PATCH 82/94] Improve local DNS server --- dns/transport/local/local.go | 3 +- dns/transport/local/resolv.go | 17 ++++--- dns/transport/local/resolv_darwin_cgo.go | 3 +- dns/transport/local/resolv_unix.go | 3 +- dns/transport/local/resolv_windows.go | 57 +++++++++++++++--------- experimental/libbox/config.go | 11 ++++- experimental/libbox/monitor.go | 19 ++++++-- experimental/libbox/service.go | 1 + go.mod | 2 +- go.sum | 4 +- 10 files changed, 82 insertions(+), 38 deletions(-) diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index f50405a5..df473b3c 100644 --- a/dns/transport/local/local.go +++ b/dns/transport/local/local.go @@ -35,6 +35,7 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt } return &Transport{ TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options), + ctx: ctx, hosts: hosts.NewFile(hosts.DefaultPath), dialer: transportDialer, }, nil @@ -57,7 +58,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil } } - systemConfig := getSystemDNSConfig() + systemConfig := getSystemDNSConfig(t.ctx) if systemConfig.singleRequest || !(message.Question[0].Qtype == mDNS.TypeA || message.Question[0].Qtype == mDNS.TypeAAAA) { return t.exchangeSingleRequest(ctx, systemConfig, message, domain) } else { diff --git a/dns/transport/local/resolv.go b/dns/transport/local/resolv.go index 5484d0ec..754d4539 100644 --- a/dns/transport/local/resolv.go +++ b/dns/transport/local/resolv.go @@ -1,6 +1,7 @@ package local import ( + "context" "os" "runtime" "strings" @@ -23,19 +24,21 @@ type resolverConfig struct { var resolvConf resolverConfig -func getSystemDNSConfig() *dnsConfig { - resolvConf.tryUpdate("/etc/resolv.conf") +func getSystemDNSConfig(ctx context.Context) *dnsConfig { + resolvConf.tryUpdate(ctx, "/etc/resolv.conf") return resolvConf.dnsConfig.Load() } -func (conf *resolverConfig) init() { - conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf")) +func (conf *resolverConfig) init(ctx context.Context) { + conf.dnsConfig.Store(dnsReadConfig(ctx, "/etc/resolv.conf")) conf.lastChecked = time.Now() conf.ch = make(chan struct{}, 1) } -func (conf *resolverConfig) tryUpdate(name string) { - conf.initOnce.Do(conf.init) +func (conf *resolverConfig) tryUpdate(ctx context.Context, name string) { + conf.initOnce.Do(func() { + conf.init(ctx) + }) if conf.dnsConfig.Load().noReload { return @@ -59,7 +62,7 @@ func (conf *resolverConfig) tryUpdate(name string) { return } } - dnsConf := dnsReadConfig(name) + dnsConf := dnsReadConfig(ctx, name) conf.dnsConfig.Store(dnsConf) } diff --git a/dns/transport/local/resolv_darwin_cgo.go b/dns/transport/local/resolv_darwin_cgo.go index dfcb8a8a..f9d82a36 100644 --- a/dns/transport/local/resolv_darwin_cgo.go +++ b/dns/transport/local/resolv_darwin_cgo.go @@ -11,6 +11,7 @@ package local import "C" import ( + "context" "time" E "github.com/sagernet/sing/common/exceptions" @@ -18,7 +19,7 @@ import ( "github.com/miekg/dns" ) -func dnsReadConfig(_ string) *dnsConfig { +func dnsReadConfig(_ context.Context, _ string) *dnsConfig { if C.res_init() != 0 { return &dnsConfig{ servers: defaultNS, diff --git a/dns/transport/local/resolv_unix.go b/dns/transport/local/resolv_unix.go index c3953145..f77f3553 100644 --- a/dns/transport/local/resolv_unix.go +++ b/dns/transport/local/resolv_unix.go @@ -4,6 +4,7 @@ package local import ( "bufio" + "context" "net" "net/netip" "os" @@ -13,7 +14,7 @@ import ( "github.com/miekg/dns" ) -func dnsReadConfig(name string) *dnsConfig { +func dnsReadConfig(_ context.Context, name string) *dnsConfig { conf := &dnsConfig{ ndots: 1, timeout: 5 * time.Second, diff --git a/dns/transport/local/resolv_windows.go b/dns/transport/local/resolv_windows.go index f6b81090..76f758c6 100644 --- a/dns/transport/local/resolv_windows.go +++ b/dns/transport/local/resolv_windows.go @@ -1,6 +1,7 @@ package local import ( + "context" "net" "net/netip" "os" @@ -8,10 +9,13 @@ import ( "time" "unsafe" + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing/service" + "golang.org/x/sys/windows" ) -func dnsReadConfig(_ string) *dnsConfig { +func dnsReadConfig(ctx context.Context, _ string) *dnsConfig { conf := &dnsConfig{ ndots: 1, timeout: 5 * time.Second, @@ -22,35 +26,35 @@ func dnsReadConfig(_ string) *dnsConfig { conf.servers = defaultNS } }() - aas, err := adapterAddresses() + addresses, err := adapterAddresses() if err != nil { return nil } - - for _, aa := range aas { - // Only take interfaces whose OperStatus is IfOperStatusUp(0x01) into DNS configs. - if aa.OperStatus != windows.IfOperStatusUp { + var dnsAddresses []struct { + ifName string + netip.Addr + } + for _, address := range addresses { + if address.OperStatus != windows.IfOperStatusUp { continue } - - // Only take interfaces which have at least one gateway - if aa.FirstGatewayAddress == nil { + if address.IfType == windows.IF_TYPE_TUNNEL { continue } - - for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next { - sa, err := dns.Address.Sockaddr.Sockaddr() + if address.FirstGatewayAddress == nil { + continue + } + for dnsServerAddress := address.FirstDnsServerAddress; dnsServerAddress != nil; dnsServerAddress = dnsServerAddress.Next { + rawSockaddr, err := dnsServerAddress.Address.Sockaddr.Sockaddr() if err != nil { continue } - var ip netip.Addr - switch sa := sa.(type) { + var dnsServerAddr netip.Addr + switch sockaddr := rawSockaddr.(type) { case *syscall.SockaddrInet4: - ip = netip.AddrFrom4([4]byte{sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]}) + dnsServerAddr = netip.AddrFrom4(sockaddr.Addr) case *syscall.SockaddrInet6: - var addr16 [16]byte - copy(addr16[:], sa.Addr[:]) - if addr16[0] == 0xfe && addr16[1] == 0xc0 { + if sockaddr.Addr[0] == 0xfe && sockaddr.Addr[1] == 0xc0 { // fec0/10 IPv6 addresses are site local anycast DNS // addresses Microsoft sets by default if no other // IPv6 DNS address is set. Site local anycast is @@ -58,14 +62,27 @@ func dnsReadConfig(_ string) *dnsConfig { // https://datatracker.ietf.org/doc/html/rfc3879 continue } - ip = netip.AddrFrom16(addr16) + dnsServerAddr = netip.AddrFrom16(sockaddr.Addr) default: // Unexpected type. continue } - conf.servers = append(conf.servers, net.JoinHostPort(ip.String(), "53")) + dnsAddresses = append(dnsAddresses, struct { + ifName string + netip.Addr + }{ifName: windows.UTF16PtrToString(address.FriendlyName), Addr: dnsServerAddr}) } } + var myInterface string + if networkManager := service.FromContext[adapter.NetworkManager](ctx); networkManager != nil { + myInterface = networkManager.InterfaceMonitor().MyInterface() + } + for _, address := range dnsAddresses { + if address.ifName == myInterface { + continue + } + conf.servers = append(conf.servers, net.JoinHostPort(address.String(), "53")) + } return conf } diff --git a/experimental/libbox/config.go b/experimental/libbox/config.go index d3149dc2..8fc1b66c 100644 --- a/experimental/libbox/config.go +++ b/experimental/libbox/config.go @@ -116,6 +116,10 @@ func (s *platformInterfaceStub) FindProcessInfo(ctx context.Context, network str return nil, os.ErrInvalid } +func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error { + return nil +} + type interfaceMonitorStub struct{} func (s *interfaceMonitorStub) Start() error { @@ -145,8 +149,11 @@ func (s *interfaceMonitorStub) RegisterCallback(callback tun.DefaultInterfaceUpd func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) { } -func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error { - return nil +func (s *interfaceMonitorStub) RegisterMyInterface(interfaceName string) { +} + +func (s *interfaceMonitorStub) MyInterface() string { + return "" } func FormatConfig(configContent string) (*StringBox, error) { diff --git a/experimental/libbox/monitor.go b/experimental/libbox/monitor.go index 05973ec6..00f63abd 100644 --- a/experimental/libbox/monitor.go +++ b/experimental/libbox/monitor.go @@ -15,9 +15,10 @@ var ( type platformDefaultInterfaceMonitor struct { *platformInterfaceWrapper - element *list.Element[tun.NetworkUpdateCallback] - callbacks list.List[tun.DefaultInterfaceUpdateCallback] - logger logger.Logger + logger logger.Logger + element *list.Element[tun.NetworkUpdateCallback] + callbacks list.List[tun.DefaultInterfaceUpdateCallback] + myInterface string } func (m *platformDefaultInterfaceMonitor) Start() error { @@ -102,3 +103,15 @@ func (m *platformDefaultInterfaceMonitor) updateDefaultInterface(interfaceName s callback(newInterface, 0) } } + +func (m *platformDefaultInterfaceMonitor) RegisterMyInterface(interfaceName string) { + m.defaultInterfaceAccess.Lock() + defer m.defaultInterfaceAccess.Unlock() + m.myInterface = interfaceName +} + +func (m *platformDefaultInterfaceMonitor) MyInterface() string { + m.defaultInterfaceAccess.Lock() + defer m.defaultInterfaceAccess.Unlock() + return m.myInterface +} diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index 48d614ff..7f80e6fe 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -164,6 +164,7 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions if err != nil { return nil, E.Cause(err, "query tun name") } + options.InterfaceMonitor.RegisterMyInterface(options.Name) dupFd, err := dup(int(tunFd)) if err != nil { return nil, E.Cause(err, "dup tun file descriptor") diff --git a/go.mod b/go.mod index b565177f..1d394115 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks2 v0.2.0 github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 - github.com/sagernet/sing-tun v0.6.4 + github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/tailscale v1.80.3-mod.2 diff --git a/go.sum b/go.sum index e97e77ef..022a8436 100644 --- a/go.sum +++ b/go.sum @@ -190,8 +190,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 h1:GFNJQAHhSXqAfxAw1wDG/QWbdpGH5Na3k8qUynqWnEA= github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056/go.mod h1:HyacBPIFiKihJQR8LQp56FM4hBtd/7MZXnRxxQIOPsc= -github.com/sagernet/sing-tun v0.6.4 h1:3Iew6UtAf1+mucVeHKNhAEQI5xmq3CUCbGptUbjebts= -github.com/sagernet/sing-tun v0.6.4/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= +github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a h1:2aLxZFD2HPCLrnFGpH+KBuPqMOk0cuaDE2dgEvANuMk= +github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug= github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= From 26ae7a61e96b0314398b5ad3135edc8d1450e730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 17 Apr 2025 15:49:36 +0800 Subject: [PATCH 83/94] Fix missing handling of legacy `domain_strategy` options --- common/dialer/dialer.go | 40 +++++--- docs/configuration/shared/dial.md | 2 +- docs/configuration/shared/dial.zh.md | 4 + docs/migration.md | 55 +++++++++- docs/migration.zh.md | 146 ++++++++++++++++++--------- experimental/deprecated/constants.go | 14 +++ 6 files changed, 195 insertions(+), 66 deletions(-) diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index 7bf05d1b..7bf8ff02 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -83,6 +83,7 @@ func NewWithOptions(options Options) (N.Dialer, error) { dialOptions.DomainStrategy != option.DomainStrategy(C.DomainStrategyAsIS) { //nolint:staticcheck strategy = C.DomainStrategy(dialOptions.DomainStrategy) + deprecated.Report(options.Context, deprecated.OptionLegacyDomainStrategyOptions) } server = dialOptions.DomainResolver.Server dnsQueryOptions = adapter.DNSQueryOptions{ @@ -95,22 +96,31 @@ func NewWithOptions(options Options) (N.Dialer, error) { resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay) } else if options.DirectResolver { return nil, E.New("missing domain resolver for domain server address") - } else if defaultOptions.DomainResolver != "" { - dnsQueryOptions = defaultOptions.DomainResolveOptions - transport, loaded := dnsTransport.Transport(defaultOptions.DomainResolver) - if !loaded { - return nil, E.New("default domain resolver not found: " + defaultOptions.DomainResolver) - } - dnsQueryOptions.Transport = transport - resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay) } else { - transports := dnsTransport.Transports() - if len(transports) < 2 { - dnsQueryOptions.Transport = dnsTransport.Default() - } else if options.NewDialer { - return nil, E.New("missing domain resolver for domain server address") - } else if !options.DirectOutbound { - deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) + if defaultOptions.DomainResolver != "" { + dnsQueryOptions = defaultOptions.DomainResolveOptions + transport, loaded := dnsTransport.Transport(defaultOptions.DomainResolver) + if !loaded { + return nil, E.New("default domain resolver not found: " + defaultOptions.DomainResolver) + } + dnsQueryOptions.Transport = transport + resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay) + } else { + transports := dnsTransport.Transports() + if len(transports) < 2 { + dnsQueryOptions.Transport = dnsTransport.Default() + } else if options.NewDialer { + return nil, E.New("missing domain resolver for domain server address") + } else if !options.DirectOutbound { + deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) + } + } + if + //nolint:staticcheck + dialOptions.DomainStrategy != option.DomainStrategy(C.DomainStrategyAsIS) { + //nolint:staticcheck + dnsQueryOptions.Strategy = C.DomainStrategy(dialOptions.DomainStrategy) + deprecated.Report(options.Context, deprecated.OptionLegacyDomainStrategyOptions) } } dialer = NewResolveDialer( diff --git a/docs/configuration/shared/dial.md b/docs/configuration/shared/dial.md index f7507531..97fbfce3 100644 --- a/docs/configuration/shared/dial.md +++ b/docs/configuration/shared/dial.md @@ -206,7 +206,7 @@ Only take effect when `domain_strategy` or `network_strategy` is set. !!! failure "Deprecated in sing-box 1.12.0" - `domain_strategy` is merged to [domain_resolver](#domain_resolver) in sing-box 1.12.0. + `domain_strategy` is deprecated and will be removed in sing-box 1.14.0, check [Migration](/migration/#migrate-outbound-domain-strategy-option-to-domain-resolver). Available values: `prefer_ipv4`, `prefer_ipv6`, `ipv4_only`, `ipv6_only`. diff --git a/docs/configuration/shared/dial.zh.md b/docs/configuration/shared/dial.zh.md index 5abd6ad9..292cdc7c 100644 --- a/docs/configuration/shared/dial.zh.md +++ b/docs/configuration/shared/dial.zh.md @@ -194,6 +194,10 @@ icon: material/new-box #### domain_strategy +!!! failure "已在 sing-box 1.12.0 废弃" + + `domain_strategy` 已废弃且将在 sing-box 1.14.0 中被移除,参阅 [迁移指南](/migration/#migrate-outbound-domain-strategy-option-to-domain-resolver)。 + 可选值:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。 如果设置,域名将在请求发出之前解析为 IP。 diff --git a/docs/migration.md b/docs/migration.md index 895f48f1..ca50a70a 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -516,13 +516,13 @@ DNS servers are refactored for better performance and scalability. The legacy outbound DNS rules are deprecated and can be replaced by new domain resolver options. !!! info "References" - + [DNS rule](/configuration/dns/rule/#outbound) / [Dial Fields](/configuration/shared/dial/#domain_resolver) / [Route](/configuration/route/#domain_resolver) === ":material-card-remove: Deprecated" - + ```json { "dns": { @@ -586,6 +586,57 @@ The legacy outbound DNS rules are deprecated and can be replaced by new domain r } ``` +### Migrate outbound domain strategy option to domain resolver + +!!! info "References" + + [Dial Fields](/configuration/shared/dial/#domain_strategy) + +The `domain_strategy` option in Dial Fields has been deprecated and can be replaced with the new domain resolver option. + +Note that due to the use of Dial Fields by some of the new DNS servers introduced in sing-box 1.12, +some people mistakenly believe that `domain_strategy` is the same feature as in the legacy DNS servers. + +=== ":material-card-remove: Deprecated" + + ```json + { + "outbounds": [ + { + "type": "socks", + "server": "example.org", + "server_port": 2080, + "domain_strategy": "prefer_ipv4", + } + ] + } + ``` + +=== ":material-card-multiple: New" + + ```json + { + "dns": { + "servers": [ + { + "type": "local" + } + ] + }, + "outbounds": [ + { + "type": "socks", + "server": "example.org", + "server_port": 2080, + "domain_resolver": { + "server": "local", + "strategy": "prefer_ipv4" + } + } + ] + } + ``` + ## 1.11.0 ### Migrate legacy special outbounds to rule actions diff --git a/docs/migration.zh.md b/docs/migration.zh.md index f82f2521..a2779e23 100644 --- a/docs/migration.zh.md +++ b/docs/migration.zh.md @@ -16,7 +16,7 @@ DNS 服务器已经重构。 === "Local" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -28,9 +28,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -46,7 +46,7 @@ DNS 服务器已经重构。 === "TCP" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -58,9 +58,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -77,7 +77,7 @@ DNS 服务器已经重构。 === "UDP" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -89,9 +89,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -108,7 +108,7 @@ DNS 服务器已经重构。 === "TLS" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -120,9 +120,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -139,7 +139,7 @@ DNS 服务器已经重构。 === "HTTPS" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -151,9 +151,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -170,7 +170,7 @@ DNS 服务器已经重构。 === "QUIC" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -182,9 +182,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -201,7 +201,7 @@ DNS 服务器已经重构。 === "HTTP3" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -213,9 +213,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -232,7 +232,7 @@ DNS 服务器已经重构。 === "DHCP" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -247,9 +247,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -269,7 +269,7 @@ DNS 服务器已经重构。 === "FakeIP" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -299,9 +299,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -333,7 +333,7 @@ DNS 服务器已经重构。 === "RCode" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -345,9 +345,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -368,7 +368,7 @@ DNS 服务器已经重构。 === "带有域名地址的服务器" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -385,9 +385,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -410,7 +410,7 @@ DNS 服务器已经重构。 === "带有域策略的服务器" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -434,9 +434,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -466,7 +466,7 @@ DNS 服务器已经重构。 === "带有客户端子网的服务器" === ":material-card-remove: 弃用的" - + ```json { "dns": { @@ -483,9 +483,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "dns": { @@ -586,6 +586,56 @@ DNS 服务器已经重构。 } ``` +### 迁移出站域名策略选项到域名解析器 + +拨号字段中的 `domain_strategy` 选项已被弃用,可以用新的域名解析器选项替代。 + +请注意,由于 sing-box 1.12 中引入的一些新 DNS 服务器使用了拨号字段,一些人错误地认为 `domain_strategy` 与旧 DNS 服务器中的功能相同。 + +!!! info "参考" + + [拨号字段](/configuration/shared/dial/#domain_strategy) + +=== ":material-card-remove: 弃用的" + + ```json + { + "outbounds": [ + { + "type": "socks", + "server": "example.org", + "server_port": 2080, + "domain_strategy": "prefer_ipv4", + } + ] + } + ``` + +=== ":material-card-multiple: 新的" + + ```json + { + "dns": { + "servers": [ + { + "type": "local" + } + ] + }, + "outbounds": [ + { + "type": "socks", + "server": "example.org", + "server_port": 2080, + "domain_resolver": { + "server": "local", + "strategy": "prefer_ipv4" + } + } + ] + } + ``` + ## 1.11.0 ### 迁移旧的特殊出站到规则动作 @@ -601,7 +651,7 @@ DNS 服务器已经重构。 === "Block" === ":material-card-remove: 弃用的" - + ```json { "outbounds": [ @@ -614,7 +664,7 @@ DNS 服务器已经重构。 "rules": [ { ..., - + "outbound": "block" } ] @@ -623,14 +673,14 @@ DNS 服务器已经重构。 ``` === ":material-card-multiple: 新的" - + ```json { "route": { "rules": [ { ..., - + "action": "reject" } ] @@ -641,13 +691,13 @@ DNS 服务器已经重构。 === "DNS" === ":material-card-remove: 弃用的" - + ```json { "inbound": [ { ..., - + "sniff": true } ], @@ -667,9 +717,9 @@ DNS 服务器已经重构。 } } ``` - + === ":material-card-multiple: 新的" - + ```json { "route": { @@ -1133,4 +1183,4 @@ sing-box 1.9.0 使 QueryFullProcessImageNameW 输出 Win32 路径(如 `C:\fold } } } - ``` \ No newline at end of file + ``` diff --git a/experimental/deprecated/constants.go b/experimental/deprecated/constants.go index 16216716..5dfdfd47 100644 --- a/experimental/deprecated/constants.go +++ b/experimental/deprecated/constants.go @@ -161,6 +161,7 @@ var OptionLegacyDNSFakeIPOptions = Note{ Description: "legacy DNS fakeip options", DeprecatedVersion: "1.12.0", ScheduledVersion: "1.14.0", + EnvName: "LEGACY_DNS_FAKEIP_OPTIONS", MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-to-new-dns-server-formats", } @@ -169,6 +170,7 @@ var OptionOutboundDNSRuleItem = Note{ Description: "outbound DNS rule item", DeprecatedVersion: "1.12.0", ScheduledVersion: "1.14.0", + EnvName: "OUTBOUND_DNS_RULE_ITEM", MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-outbound-dns-rule-items-to-domain-resolver", } @@ -177,6 +179,7 @@ var OptionMissingDomainResolver = Note{ Description: "missing `route.default_domain_resolver` or `domain_resolver` in dial fields", DeprecatedVersion: "1.12.0", ScheduledVersion: "1.14.0", + EnvName: "MISSING_DOMAIN_RESOLVER", MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-outbound-dns-rule-items-to-domain-resolver", } @@ -185,9 +188,19 @@ var OptionLegacyECHOptions = Note{ Description: "legacy ECH options", DeprecatedVersion: "1.12.0", ScheduledVersion: "1.13.0", + EnvName: "LEGACY_ECH_OPTIONS", MigrationLink: "https://sing-box.sagernet.org/deprecated/#legacy-ech-fields", } +var OptionLegacyDomainStrategyOptions = Note{ + Name: "legacy-domain-strategy-options", + Description: "legacy domain strategy options", + DeprecatedVersion: "1.12.0", + ScheduledVersion: "1.14.0", + EnvName: "LEGACY_DOMAIN_STRATEGY_OPTIONS", + MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-domain-strategy-options", +} + var Options = []Note{ OptionBadMatchSource, OptionGEOIP, @@ -204,4 +217,5 @@ var Options = []Note{ OptionOutboundDNSRuleItem, OptionMissingDomainResolver, OptionLegacyECHOptions, + OptionLegacyDomainStrategyOptions, } From 55ca427721b0671938e86660a1d7e0ccbe2fab4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 17 Apr 2025 17:54:19 +0800 Subject: [PATCH 84/94] documentation: Try to make the play review happy --- docs/clients/privacy.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/clients/privacy.md b/docs/clients/privacy.md index b1ecd2aa..61df7102 100644 --- a/docs/clients/privacy.md +++ b/docs/clients/privacy.md @@ -9,6 +9,10 @@ and the data generated by the software is always on your device. ## Android +The broad package (App) visibility (QUERY_ALL_PACKAGES) permission +is used to provide per-application proxy features for VPN, +sing-box will not collect your app list. + If your configuration contains `wifi_ssid` or `wifi_bssid` routing rules, sing-box uses the location permission in the background to get information about the connected Wi-Fi network to make them work. From d6ab06e1aa172696ad436d313fa5bf990ca2ce21 Mon Sep 17 00:00:00 2001 From: caelansar <31852257+caelansar@users.noreply.github.com> Date: Sat, 19 Apr 2025 01:20:17 -0500 Subject: [PATCH 85/94] Fix callback deletion in UDP transport --- dns/transport/udp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/transport/udp.go b/dns/transport/udp.go index ec17c71b..e2ad9db7 100644 --- a/dns/transport/udp.go +++ b/dns/transport/udp.go @@ -117,7 +117,7 @@ func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M conn.access.Unlock() defer func() { conn.access.Lock() - delete(conn.callbacks, messageId) + delete(conn.callbacks, exMessage.Id) conn.access.Unlock() }() rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes()) From fc0096b0e659a2cb48e3cec358046046b6fe9850 Mon Sep 17 00:00:00 2001 From: reletor <191429954+reletor@users.noreply.github.com> Date: Sat, 19 Apr 2025 15:39:39 +0800 Subject: [PATCH 86/94] documentation: Minor fixes --- docs/configuration/dns/server/index.md | 27 ++++++++++++----------- docs/configuration/dns/server/index.zh.md | 27 ++++++++++++----------- docs/configuration/route/sniff.zh.md | 4 ++-- docs/migration.md | 8 ++++--- docs/migration.zh.md | 8 ++++--- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/docs/configuration/dns/server/index.md b/docs/configuration/dns/server/index.md index 0dae05c3..706eb54f 100644 --- a/docs/configuration/dns/server/index.md +++ b/docs/configuration/dns/server/index.md @@ -27,19 +27,20 @@ icon: material/alert-decagram The type of the DNS server. -| Type | Format | -|-----------------|-----------------------------| -| empty (default) | [Legacy](./legacy/) | -| `tcp` | [TCP](./tcp/) | -| `udp` | [UDP](./udp/) | -| `tls` | [TLS](./tls/) | -| `https` | [HTTPS](./https/) | -| `quic` | [QUIC](./quic/) | -| `h3` | [HTTP/3](./http3/) | -| `predefined` | [Predefined](./predefined/) | -| `dhcp` | [DHCP](./dhcp/) | -| `fakeip` | [Fake IP](./fakeip/) | -| `tailscale` | [Tailscale](./tailscale/) | +| Type | Format | +|-----------------|---------------------------| +| empty (default) | [Legacy](./legacy/) | +| `local` | [Local](./local/) | +| `hosts` | [Hosts](./hosts/) | +| `tcp` | [TCP](./tcp/) | +| `udp` | [UDP](./udp/) | +| `tls` | [TLS](./tls/) | +| `quic` | [QUIC](./quic/) | +| `https` | [HTTPS](./https/) | +| `h3` | [HTTP/3](./http3/) | +| `dhcp` | [DHCP](./dhcp/) | +| `fakeip` | [Fake IP](./fakeip/) | +| `tailscale` | [Tailscale](./tailscale/) | #### tag diff --git a/docs/configuration/dns/server/index.zh.md b/docs/configuration/dns/server/index.zh.md index 2ad99502..61977c67 100644 --- a/docs/configuration/dns/server/index.zh.md +++ b/docs/configuration/dns/server/index.zh.md @@ -27,19 +27,20 @@ icon: material/alert-decagram DNS 服务器的类型。 -| 类型 | 格式 | -|-----------------|-----------------------------| -| empty (default) | [Legacy](./legacy/) | -| `tcp` | [TCP](./tcp/) | -| `udp` | [UDP](./udp/) | -| `tls` | [TLS](./tls/) | -| `https` | [HTTPS](./https/) | -| `quic` | [QUIC](./quic/) | -| `h3` | [HTTP/3](./http3/) | -| `predefined` | [Predefined](./predefined/) | -| `dhcp` | [DHCP](./dhcp/) | -| `fakeip` | [Fake IP](./fakeip/) | -| `tailscale` | [Tailscale](./tailscale/) | +| 类型 | 格式 | +|-----------------|---------------------------| +| empty (default) | [Legacy](./legacy/) | +| `local` | [Local](./local/) | +| `hosts` | [Hosts](./hosts/) | +| `tcp` | [TCP](./tcp/) | +| `udp` | [UDP](./udp/) | +| `tls` | [TLS](./tls/) | +| `quic` | [QUIC](./quic/) | +| `https` | [HTTPS](./https/) | +| `h3` | [HTTP/3](./http3/) | +| `dhcp` | [DHCP](./dhcp/) | +| `fakeip` | [Fake IP](./fakeip/) | +| `tailscale` | [Tailscale](./tailscale/) | #### tag diff --git a/docs/configuration/route/sniff.zh.md b/docs/configuration/route/sniff.zh.md index 546210c8..8cb9488f 100644 --- a/docs/configuration/route/sniff.zh.md +++ b/docs/configuration/route/sniff.zh.md @@ -26,7 +26,7 @@ | QUIC 客户端 | 类型 | |:------------------------:|:----------:| -| Chromium/Cronet | `chrimium` | +| Chromium/Cronet | `chromium` | | Safari/Apple Network API | `safari` | | Firefox / uquic firefox | `firefox` | -| quic-go / uquic chrome | `quic-go` | \ No newline at end of file +| quic-go / uquic chrome | `quic-go` | diff --git a/docs/migration.md b/docs/migration.md index ca50a70a..50a0655f 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -292,7 +292,7 @@ DNS servers are refactored for better performance and scalability. } ], "fakeip": { - "enable": true, + "enabled": true, "inet4_range": "198.18.0.0/15", "inet6_range": "fc00::/18" } @@ -556,7 +556,8 @@ The legacy outbound DNS rules are deprecated and can be replaced by new domain r "dns": { "servers": [ { - "type": "local" + "type": "local", + "tag": "local" } ] }, @@ -619,7 +620,8 @@ some people mistakenly believe that `domain_strategy` is the same feature as in "dns": { "servers": [ { - "type": "local" + "type": "local", + "tag": "local" } ] }, diff --git a/docs/migration.zh.md b/docs/migration.zh.md index a2779e23..255b7069 100644 --- a/docs/migration.zh.md +++ b/docs/migration.zh.md @@ -292,7 +292,7 @@ DNS 服务器已经重构。 } ], "fakeip": { - "enable": true, + "enabled": true, "inet4_range": "198.18.0.0/15", "inet6_range": "fc00::/18" } @@ -556,7 +556,8 @@ DNS 服务器已经重构。 "dns": { "servers": [ { - "type": "local" + "type": "local", + "tag": "local" } ] }, @@ -618,7 +619,8 @@ DNS 服务器已经重构。 "dns": { "servers": [ { - "type": "local" + "type": "local", + "tag": "local" } ] }, From 2316ca38a931769d3640163358f19e0a48c0a1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 22 Apr 2025 15:08:30 +0800 Subject: [PATCH 87/94] Fix fetch ECH configs --- common/tls/ech.go | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/common/tls/ech.go b/common/tls/ech.go index ddb9b5dd..de911126 100644 --- a/common/tls/ech.go +++ b/common/tls/ech.go @@ -10,6 +10,8 @@ import ( "net" "os" "strings" + "sync" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/dns" @@ -46,7 +48,10 @@ func parseECHClientConfig(ctx context.Context, options option.OutboundTLSOptions tlsConfig.EncryptedClientHelloConfigList = block.Bytes return &STDClientConfig{tlsConfig}, nil } else { - return &STDECHClientConfig{STDClientConfig{tlsConfig}, service.FromContext[adapter.DNSRouter](ctx)}, nil + return &STDECHClientConfig{ + STDClientConfig: STDClientConfig{tlsConfig}, + dnsRouter: service.FromContext[adapter.DNSRouter](ctx), + }, nil } } @@ -99,11 +104,28 @@ func reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error { type STDECHClientConfig struct { STDClientConfig - dnsRouter adapter.DNSRouter + access sync.Mutex + dnsRouter adapter.DNSRouter + lastTTL time.Duration + lastUpdate time.Time } func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) { - if len(s.config.EncryptedClientHelloConfigList) == 0 { + tlsConn, err := s.fetchAndHandshake(ctx, conn) + if err != nil { + return nil, err + } + err = tlsConn.HandshakeContext(ctx) + if err != nil { + return nil, err + } + return tlsConn, nil +} + +func (s *STDECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) { + s.access.Lock() + defer s.access.Unlock() + if len(s.config.EncryptedClientHelloConfigList) == 0 || s.lastTTL == 0 || time.Now().Sub(s.lastUpdate) > s.lastTTL { message := &mDNS.Msg{ MsgHdr: mDNS.MsgHdr{ RecursionDesired: true, @@ -133,6 +155,8 @@ func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) if err != nil { return nil, E.Cause(err, "decode ECH config") } + s.lastTTL = time.Duration(rr.Header().Ttl) * time.Second + s.lastUpdate = time.Now() s.config.EncryptedClientHelloConfigList = echConfigList break match } @@ -143,19 +167,11 @@ func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) return nil, E.New("no ECH config found in DNS records") } } - tlsConn, err := s.Client(conn) - if err != nil { - return nil, err - } - err = tlsConn.HandshakeContext(ctx) - if err != nil { - return nil, err - } - return tlsConn, nil + return s.Client(conn) } func (s *STDECHClientConfig) Clone() Config { - return &STDECHClientConfig{STDClientConfig{s.config.Clone()}, s.dnsRouter} + return &STDECHClientConfig{STDClientConfig: STDClientConfig{s.config.Clone()}, dnsRouter: s.dnsRouter, lastUpdate: s.lastUpdate} } func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) { From a15f35a07690a6f04148c74290759c7325edd77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 22 Apr 2025 18:18:46 +0800 Subject: [PATCH 88/94] Fix tailscale sending unexpected stuff --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1d394115..d4dd9fba 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 - github.com/sagernet/tailscale v1.80.3-mod.2 + github.com/sagernet/tailscale v1.80.3-mod.4 github.com/sagernet/utls v1.6.7 github.com/sagernet/wireguard-go v0.0.1-beta.7 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 diff --git a/go.sum b/go.sum index 022a8436..c67128b8 100644 --- a/go.sum +++ b/go.sum @@ -196,8 +196,8 @@ github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2 github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= -github.com/sagernet/tailscale v1.80.3-mod.2 h1:hT0CI74q727EuCcgQ+T4pvon8V0aoi4vTAxah7GsNMQ= -github.com/sagernet/tailscale v1.80.3-mod.2/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= +github.com/sagernet/tailscale v1.80.3-mod.4 h1:9UgYq8m9mwX5dbTbueVxbRh+bq7AayxemJGM2PkJQnE= +github.com/sagernet/tailscale v1.80.3-mod.4/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI= From 73f4b413668d9218e5d554cec6c377de0208ffab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 16:27:56 +0800 Subject: [PATCH 89/94] Fix DNS lookup --- dns/client.go | 2 +- dns/router.go | 11 +++++++---- dns/transport/local/local.go | 6 ++++-- route/conn.go | 33 ++++++++++++++++++++++++++++++--- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/dns/client.go b/dns/client.go index f456f878..90c950b4 100644 --- a/dns/client.go +++ b/dns/client.go @@ -483,7 +483,7 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp } func MessageToAddresses(response *dns.Msg) ([]netip.Addr, error) { - if response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError { + if response.Rcode != dns.RcodeSuccess { return nil, RcodeError(response.Rcode) } addresses := make([]netip.Addr, 0, len(response.Answer)) diff --git a/dns/router.go b/dns/router.go index 44edadbd..bf4361ce 100644 --- a/dns/router.go +++ b/dns/router.go @@ -323,6 +323,9 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ err error ) printResult := func() { + if err == nil && len(responseAddrs) == 0 { + err = E.New("empty result") + } if err != nil { if errors.Is(err, ErrResponseRejectedCached) { r.logger.DebugContext(ctx, "response rejected for ", domain, " (cached)") @@ -331,15 +334,15 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ } else { r.logger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain)) } - } else if len(responseAddrs) == 0 { - r.logger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result") - err = RcodeNameError + } + if err != nil { + err = E.Cause(err, "lookup ", domain) } } responseAddrs, cached = r.client.LookupCache(domain, options.Strategy) if cached { if len(responseAddrs) == 0 { - return nil, RcodeNameError + return nil, E.New("lookup ", domain, ": empty result (cached)") } return responseAddrs, nil } diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index df473b3c..15be54c3 100644 --- a/dns/transport/local/local.go +++ b/dns/transport/local/local.go @@ -3,6 +3,7 @@ package local import ( "context" "math/rand" + "net/netip" "time" "github.com/sagernet/sing-box/adapter" @@ -90,8 +91,9 @@ func (t *Transport) exchangeParallel(ctx context.Context, systemConfig *dnsConfi startRacer := func(ctx context.Context, fqdn string) { response, err := t.tryOneName(ctx, systemConfig, fqdn, message) if err == nil { - addresses, _ := dns.MessageToAddresses(response) - if len(addresses) == 0 { + var addresses []netip.Addr + addresses, err = dns.MessageToAddresses(response) + if err == nil && len(addresses) == 0 { err = E.New(fqdn, ": empty result") } } diff --git a/route/conn.go b/route/conn.go index 582562eb..f46283ad 100644 --- a/route/conn.go +++ b/route/conn.go @@ -7,6 +7,7 @@ import ( "net" "net/netip" "os" + "strings" "sync" "sync/atomic" "time" @@ -66,7 +67,17 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co remoteConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination) } if err != nil { - err = E.Cause(err, "open outbound connection") + var remoteString string + if len(metadata.DestinationAddresses) > 0 { + remoteString = "[" + strings.Join(common.Map(metadata.DestinationAddresses, netip.Addr.String), ",") + "]" + } else { + remoteString = metadata.Destination.String() + } + var dialerString string + if outbound, isOutbound := this.(adapter.Outbound); isOutbound { + dialerString = " using outbound/" + outbound.Type() + "[" + outbound.Tag() + "]" + } + err = E.Cause(err, "open connection to ", remoteString, dialerString) N.CloseOnHandshakeFailure(conn, onClose, err) m.logger.ErrorContext(ctx, err) return @@ -133,8 +144,19 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial remoteConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination) } if err != nil { + var remoteString string + if len(metadata.DestinationAddresses) > 0 { + remoteString = "[" + strings.Join(common.Map(metadata.DestinationAddresses, netip.Addr.String), ",") + "]" + } else { + remoteString = metadata.Destination.String() + } + var dialerString string + if outbound, isOutbound := this.(adapter.Outbound); isOutbound { + dialerString = " using outbound/" + outbound.Type() + "[" + outbound.Tag() + "]" + } + err = E.Cause(err, "open packet connection to ", remoteString, dialerString) N.CloseOnHandshakeFailure(conn, onClose, err) - m.logger.ErrorContext(ctx, "open outbound packet connection: ", err) + m.logger.ErrorContext(ctx, err) return } remotePacketConn = bufio.NewUnbindPacketConn(remoteConn) @@ -149,8 +171,13 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial remotePacketConn, err = this.ListenPacket(ctx, metadata.Destination) } if err != nil { + var dialerString string + if outbound, isOutbound := this.(adapter.Outbound); isOutbound { + dialerString = " using outbound/" + outbound.Type() + "[" + outbound.Tag() + "]" + } + err = E.Cause(err, "listen packet connection using ", dialerString) N.CloseOnHandshakeFailure(conn, onClose, err) - m.logger.ErrorContext(ctx, "listen outbound packet connection: ", err) + m.logger.ErrorContext(ctx, err) return } } From 05b6a088a6cfb5398dc9d32c8abd0fe2c80bbedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 25 Apr 2025 11:22:55 +0800 Subject: [PATCH 90/94] clash-api: Add more meta api --- experimental/clashapi/api_meta.go | 10 +++++++ experimental/clashapi/api_meta_upgrade.go | 36 +++++++++++++++++++++++ experimental/clashapi/server.go | 3 ++ 3 files changed, 49 insertions(+) create mode 100644 experimental/clashapi/api_meta_upgrade.go diff --git a/experimental/clashapi/api_meta.go b/experimental/clashapi/api_meta.go index 29add8ae..77dad797 100644 --- a/experimental/clashapi/api_meta.go +++ b/experimental/clashapi/api_meta.go @@ -4,6 +4,7 @@ import ( "bytes" "net" "net/http" + "runtime/debug" "time" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" @@ -12,14 +13,23 @@ import ( "github.com/sagernet/ws/wsutil" "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/render" ) // API created by Clash.Meta func (s *Server) setupMetaAPI(r chi.Router) { + if s.logDebug { + r := chi.NewRouter() + r.Put("/gc", func(w http.ResponseWriter, r *http.Request) { + debug.FreeOSMemory() + }) + r.Mount("/", middleware.Profiler()) + } r.Get("/memory", memory(s.trafficManager)) r.Mount("/group", groupRouter(s)) + r.Mount("/upgrade", upgradeRouter(s)) } type Memory struct { diff --git a/experimental/clashapi/api_meta_upgrade.go b/experimental/clashapi/api_meta_upgrade.go new file mode 100644 index 00000000..df70088e --- /dev/null +++ b/experimental/clashapi/api_meta_upgrade.go @@ -0,0 +1,36 @@ +package clashapi + +import ( + "net/http" + + E "github.com/sagernet/sing/common/exceptions" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/render" +) + +func upgradeRouter(server *Server) http.Handler { + r := chi.NewRouter() + r.Post("/ui", updateExternalUI(server)) + return r +} + +func updateExternalUI(server *Server) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + if server.externalUI == "" { + render.Status(r, http.StatusNotFound) + render.JSON(w, r, newError("external UI not enabled")) + return + } + server.logger.Info("upgrading external UI") + err := server.downloadExternalUI() + if err != nil { + server.logger.Error(E.Cause(err, "upgrade external ui")) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(err.Error())) + return + } + server.logger.Info("updated external UI") + render.JSON(w, r, render.M{"status": "ok"}) + } +} diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index e6d8c4cf..3a2d4827 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -49,6 +49,8 @@ type Server struct { httpServer *http.Server trafficManager *trafficontrol.Manager urlTestHistory adapter.URLTestHistoryStorage + logDebug bool + mode string modeList []string modeUpdateHook chan<- struct{} @@ -74,6 +76,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op Handler: chiRouter, }, trafficManager: trafficManager, + logDebug: logFactory.Level() >= log.LevelDebug, modeList: options.ModeList, externalController: options.ExternalController != "", externalUIDownloadURL: options.ExternalUIDownloadURL, From e528d425a17de9bbdd402d90aa266ba87746fd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 26 Apr 2025 17:25:36 +0800 Subject: [PATCH 91/94] Fix wireguard `listen_port` --- option/outbound.go | 1 - protocol/wireguard/endpoint.go | 4 ++-- protocol/wireguard/outbound.go | 4 +--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/option/outbound.go b/option/outbound.go index 1b852d26..e4475ee0 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -83,7 +83,6 @@ type DialerOptions struct { NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"` FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"` - IsWireGuardListener bool `json:"-"` // Deprecated: migrated to domain resolver DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index 29acbf12..4165d126 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -45,8 +45,8 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL logger: logger, localAddresses: options.Address, } - if options.Detour == "" { - options.IsWireGuardListener = true + if options.Detour != "" && options.ListenPort != 0 { + return nil, E.New("`listen_port` is conflict with `detour`") } outboundDialer, err := dialer.NewWithOptions(dialer.Options{ Context: ctx, diff --git a/protocol/wireguard/outbound.go b/protocol/wireguard/outbound.go index 1e7a32c7..129e69b8 100644 --- a/protocol/wireguard/outbound.go +++ b/protocol/wireguard/outbound.go @@ -46,9 +46,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL logger: logger, localAddresses: options.LocalAddress, } - if options.Detour == "" { - options.IsWireGuardListener = true - } else if options.GSO { + if options.Detour != "" && options.GSO { return nil, E.New("gso is conflict with detour") } outboundDialer, err := dialer.NewWithOptions(dialer.Options{ From c6f661344a7bd65b3479f0f3bfd051dd1272a562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E5=AE=B9?= Date: Sat, 26 Apr 2025 14:03:51 +0800 Subject: [PATCH 92/94] Report invalid DNS address early --- dns/transport/https.go | 3 +++ dns/transport/quic/http3.go | 3 +++ dns/transport/quic/quic.go | 4 ++++ dns/transport/tcp.go | 4 ++++ dns/transport/tls.go | 3 +++ dns/transport/udp.go | 4 ++++ 6 files changed, 21 insertions(+) diff --git a/dns/transport/https.go b/dns/transport/https.go index 1750fd26..a13d9116 100644 --- a/dns/transport/https.go +++ b/dns/transport/https.go @@ -96,6 +96,9 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options if serverAddr.Port == 0 { serverAddr.Port = 443 } + if !serverAddr.IsValid() { + return nil, E.New("invalid server address: ", serverAddr) + } return NewHTTPSRaw( dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeHTTPS, tag, options.RemoteDNSServerOptions), logger, diff --git a/dns/transport/quic/http3.go b/dns/transport/quic/http3.go index 0d871741..fd1591a3 100644 --- a/dns/transport/quic/http3.go +++ b/dns/transport/quic/http3.go @@ -92,6 +92,9 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options if serverAddr.Port == 0 { serverAddr.Port = 443 } + if !serverAddr.IsValid() { + return nil, E.New("invalid server address: ", serverAddr) + } return &HTTP3Transport{ TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeHTTP3, tag, options.RemoteDNSServerOptions), logger: logger, diff --git a/dns/transport/quic/quic.go b/dns/transport/quic/quic.go index 18f8b9fe..515aff58 100644 --- a/dns/transport/quic/quic.go +++ b/dns/transport/quic/quic.go @@ -16,6 +16,7 @@ import ( sQUIC "github.com/sagernet/sing-quic" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" + E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -58,6 +59,9 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options if serverAddr.Port == 0 { serverAddr.Port = 853 } + if !serverAddr.IsValid() { + return nil, E.New("invalid server address: ", serverAddr) + } return &Transport{ TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeQUIC, tag, options.RemoteDNSServerOptions), ctx: ctx, diff --git a/dns/transport/tcp.go b/dns/transport/tcp.go index a814c030..3039c574 100644 --- a/dns/transport/tcp.go +++ b/dns/transport/tcp.go @@ -13,6 +13,7 @@ import ( "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -40,6 +41,9 @@ func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options o if serverAddr.Port == 0 { serverAddr.Port = 53 } + if !serverAddr.IsValid() { + return nil, E.New("invalid server address: ", serverAddr) + } return &TCPTransport{ TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeTCP, tag, options), dialer: transportDialer, diff --git a/dns/transport/tls.go b/dns/transport/tls.go index a99bf2f7..b6ac294a 100644 --- a/dns/transport/tls.go +++ b/dns/transport/tls.go @@ -57,6 +57,9 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o if serverAddr.Port == 0 { serverAddr.Port = 853 } + if !serverAddr.IsValid() { + return nil, E.New("invalid server address: ", serverAddr) + } return &TLSTransport{ TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeTLS, tag, options.RemoteDNSServerOptions), logger: logger, diff --git a/dns/transport/udp.go b/dns/transport/udp.go index e2ad9db7..a9c1d4d9 100644 --- a/dns/transport/udp.go +++ b/dns/transport/udp.go @@ -13,6 +13,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -47,6 +48,9 @@ func NewUDP(ctx context.Context, logger log.ContextLogger, tag string, options o if serverAddr.Port == 0 { serverAddr.Port = 53 } + if !serverAddr.IsValid() { + return nil, E.New("invalid server address: ", serverAddr) + } return NewUDPRaw(logger, dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeUDP, tag, options), transportDialer, serverAddr), nil } From ae132ec7ac1bd6ca6941b8d63df0f07749c88c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 28 Apr 2025 10:31:34 +0800 Subject: [PATCH 93/94] documentation: Fix anytls padding scheme description --- docs/configuration/inbound/anytls.md | 22 ++++++++++++---------- docs/configuration/inbound/anytls.zh.md | 22 ++++++++++++---------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/docs/configuration/inbound/anytls.md b/docs/configuration/inbound/anytls.md index 55790810..f3780119 100644 --- a/docs/configuration/inbound/anytls.md +++ b/docs/configuration/inbound/anytls.md @@ -42,16 +42,18 @@ AnyTLS padding scheme line array. Default padding scheme: -``` -stop=8 -0=30-30 -1=100-400 -2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000 -3=9-9,500-1000 -4=500-1000 -5=500-1000 -6=500-1000 -7=500-1000 +```json +[ + "stop=8", + "0=30-30", + "1=100-400", + "2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000", + "3=9-9,500-1000", + "4=500-1000", + "5=500-1000", + "6=500-1000", + "7=500-1000" +] ``` #### tls diff --git a/docs/configuration/inbound/anytls.zh.md b/docs/configuration/inbound/anytls.zh.md index 099da777..55b6749e 100644 --- a/docs/configuration/inbound/anytls.zh.md +++ b/docs/configuration/inbound/anytls.zh.md @@ -42,16 +42,18 @@ AnyTLS 填充方案行数组。 默认填充方案: -``` -stop=8 -0=30-30 -1=100-400 -2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000 -3=9-9,500-1000 -4=500-1000 -5=500-1000 -6=500-1000 -7=500-1000 +```json +[ + "stop=8", + "0=30-30", + "1=100-400", + "2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000", + "3=9-9,500-1000", + "4=500-1000", + "5=500-1000", + "6=500-1000", + "7=500-1000" +] ``` #### tls From b37e1e7616fa1e565150920d14a7c54257668afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 28 Apr 2025 10:31:15 +0800 Subject: [PATCH 94/94] documentation: Bump version --- docs/changelog.md | 175 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 93964734..628dad4d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,12 +2,20 @@ icon: material/alert-decagram --- +#### 1.12.0-beta.7 + +* Fixes and improvements + ### 1.11.9 * Fixes and improvements _We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected)._ +#### 1.12.0-beta.5 + +* Fixes and improvements + ### 1.11.8 * Improve `auto_redirect` **1** @@ -20,38 +28,205 @@ see [Tun](/configuration/inbound/tun/#auto_redirect). _We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected)._ +#### 1.12.0-beta.3 + +* Fixes and improvements + ### 1.11.7 * Fixes and improvements _We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected)._ +#### 1.12.0-beta.1 + +* Fixes and improvements + +**1**: + +Now `auto_redirect` fixes compatibility issues between tun and Docker bridge networks, +see [Tun](/configuration/inbound/tun/#auto_redirect). + ### 1.11.6 * Fixes and improvements _We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected)._ +#### 1.12.0-alpha.19 + +* Update gVisor to 20250319.0 +* Fixes and improvements + +#### 1.12.0-alpha.18 + +* Add wildcard SNI support for ShadowTLS inbound **1** +* Fixes and improvements + +**1**: + +See [ShadowTLS](/configuration/inbound/shadowtls/#wildcard_sni). + +#### 1.12.0-alpha.17 + +* Add NTP sniffer **1** +* Fixes and improvements + +**1**: + +See [Protocol Sniff](/configuration/route/sniff/). + +#### 1.12.0-alpha.16 + +* Update `domain_resolver` behavior **1** +* Fixes and improvements + +**1**: + +`route.default_domain_resolver` or `outbound.domain_resolver` is now optional when only one DNS server is configured. + +See [Dial Fields](/configuration/shared/dial/#domain_resolver). + ### 1.11.5 * Fixes and improvements _We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected)._ +#### 1.12.0-alpha.13 + +* Move `predefined` DNS server to DNS rule action **1** +* Fixes and improvements + +**1**: + +See [DNS Rule Action](/configuration/dns/rule_action/#predefined). + ### 1.11.4 * Fixes and improvements +#### 1.12.0-alpha.11 + +* Fixes and improvements + +#### 1.12.0-alpha.10 + +* Add AnyTLS protocol **1** +* Improve `resolve` route action **2** +* Migrate to stdlib ECH implementation **3** +* Fixes and improvements + +**1**: + +The new AnyTLS protocol claims to mitigate TLS proxy traffic characteristics and comes with a new multiplexing scheme. + +See [AnyTLS Inbound](/configuration/inbound/anytls/) and [AnyTLS Outbound](/configuration/outbound/anytls/). + +**2**: + +`resolve` route action now accepts `disable_cache` and other options like in DNS route actions, see [Route Action](/configuration/route/rule_action). + +**3**: + +See [TLS](/configuration/shared/tls). + +The build tag `with_ech` is no longer needed and has been removed. + +#### 1.12.0-alpha.7 + +* Add Tailscale DNS server **1** +* Fixes and improvements + +**1**: + +See [Tailscale](/configuration/dns/server/tailscale/). + +#### 1.12.0-alpha.6 + +* Add Tailscale endpoint **1** +* Drop support for go1.22 **2** +* Fixes and improvements + +**1**: + +See [Tailscale](/configuration/endpoint/tailscale/). + +**2**: + +Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile. + +For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go). + ### 1.11.3 * Fixes and improvements _This version overwrites 1.11.2, as incorrect binaries were released due to a bug in the continuous integration process._ +#### 1.12.0-alpha.5 + +* Fixes and improvements + ### 1.11.1 * Fixes and improvements +#### 1.12.0-alpha.2 + +* Update quic-go to v0.49.0 +* Fixes and improvements + +#### 1.12.0-alpha.1 + +* Refactor DNS servers **1** +* Add domain resolver options**2** +* Add TLS fragment route options **3** +* Add certificate options **4** + +**1**: + +DNS servers are refactored for better performance and scalability. + +See [DNS server](/configuration/dns/server/). + +For migration, see [Migrate to new DNS server formats](/migration/#migrate-to-new-dns-servers). + +Compatibility for old formats will be removed in sing-box 1.14.0. + +**2**: + +Legacy `outbound` DNS rules are deprecated +and can be replaced by the new `domain_resolver` option. + +See [Dial Fields](/configuration/shared/dial/#domain_resolver) and +[Route](/configuration/route/#default_domain_resolver). + +For migration, +see [Migrate outbound DNS rule items to domain resolver](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver). + +**3**: + +The new TLS fragment route options allow you to fragment TLS handshakes to bypass firewalls. + +This feature is intended to circumvent simple firewalls based on **plaintext packet matching**, and should not be used +to circumvent real censorship. + +Since it is not designed for performance, it should not be applied to all connections, but only to server names that are +known to be blocked. + +See [Route Action](/configuration/route/rule_action/#tls_fragment). + +**4**: + +New certificate options allow you to manage the default list of trusted X509 CA certificates. + +For the system certificate list, fixed Go not reading Android trusted certificates correctly. + +You can also use the Mozilla Included List instead, or add trusted certificates yourself. + +See [Certificate](/configuration/certificate/). + ### 1.11.0 Important changes since 1.10: