Compare commits

..

No commits in common. "348cc39975bc0d384c6b9d38c4ec0590d8ccf8bb" and "49498f643926c6f109f918f148c533298b4c8352" have entirely different histories.

27 changed files with 151 additions and 327 deletions

View File

@ -181,14 +181,13 @@ jobs:
fi fi
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}" echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
PKG_VERSION="${{ needs.calculate_version.outputs.version }}" PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
PKG_VERSION="${PKG_VERSION//-/\~}" PKG_VERSION="${PKG_VERSION//-/\~}-1"
echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}" echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
- name: Package DEB - name: Package DEB
if: matrix.debian != '' if: matrix.debian != ''
run: | run: |
set -xeuo pipefail set -xeuo pipefail
sudo gem install fpm sudo gem install fpm
sudo apt-get update
sudo apt-get install -y debsigs sudo apt-get install -y debsigs
cp .fpm_systemd .fpm cp .fpm_systemd .fpm
fpm -t deb \ fpm -t deb \
@ -227,7 +226,6 @@ jobs:
run: |- run: |-
set -xeuo pipefail set -xeuo pipefail
sudo gem install fpm sudo gem install fpm
sudo apt-get update
sudo apt-get install -y libarchive-tools sudo apt-get install -y libarchive-tools
cp .fpm_systemd .fpm cp .fpm_systemd .fpm
fpm -t pacman \ fpm -t pacman \

View File

@ -120,7 +120,6 @@ jobs:
set -xeuo pipefail set -xeuo pipefail
sudo gem install fpm sudo gem install fpm
sudo apt-get install -y debsigs sudo apt-get install -y debsigs
cp .fpm_systemd .fpm
fpm -t deb \ fpm -t deb \
--name "${NAME}" \ --name "${NAME}" \
-v "$PKG_VERSION" \ -v "$PKG_VERSION" \
@ -139,7 +138,6 @@ jobs:
run: |- run: |-
set -xeuo pipefail set -xeuo pipefail
sudo gem install fpm sudo gem install fpm
cp .fpm_systemd .fpm
fpm -t rpm \ fpm -t rpm \
--name "${NAME}" \ --name "${NAME}" \
-v "$PKG_VERSION" \ -v "$PKG_VERSION" \

@ -1 +1 @@
Subproject commit 6a15780ce1659a234816f7248cbc09e8ea54a1be Subproject commit 55f31c29bb68895ce544e0dfbf852b4b3e32b530

View File

@ -333,17 +333,7 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
} }
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) { func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
udpListener := d.udpListener return d.udpListener.ListenPacket(context.Background(), network, address)
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) { func trackConn(conn net.Conn, err error) (net.Conn, error) {

View File

@ -31,18 +31,13 @@ func BitTorrent(_ context.Context, metadata *adapter.InboundContext, reader io.R
return os.ErrInvalid return os.ErrInvalid
} }
const header = "BitTorrent protocol"
var protocol [19]byte var protocol [19]byte
var n int _, err = reader.Read(protocol[:])
n, err = reader.Read(protocol[:])
if string(protocol[:n]) != header[:n] {
return os.ErrInvalid
}
if err != nil { if err != nil {
return E.Cause1(ErrNeedMoreData, err) return E.Cause1(ErrNeedMoreData, err)
} }
if n < 19 { if string(protocol[:]) != "BitTorrent protocol" {
return ErrNeedMoreData return os.ErrInvalid
} }
metadata.Protocol = C.ProtocolBitTorrent metadata.Protocol = C.ProtocolBitTorrent

View File

@ -32,27 +32,6 @@ 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) { func TestSniffUTP(t *testing.T) {
t.Parallel() t.Parallel()

View File

@ -20,36 +20,22 @@ func StreamDomainNameQuery(readCtx context.Context, metadata *adapter.InboundCon
if err != nil { if err != nil {
return E.Cause1(ErrNeedMoreData, err) return E.Cause1(ErrNeedMoreData, err)
} }
if length < 12 { if length == 0 {
return os.ErrInvalid return os.ErrInvalid
} }
buffer := buf.NewSize(int(length)) buffer := buf.NewSize(int(length))
defer buffer.Release() defer buffer.Release()
var n int _, err = buffer.ReadFullFrom(reader, buffer.FreeLen())
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 { if err != nil {
return E.Cause1(ErrNeedMoreData, err) return E.Cause1(ErrNeedMoreData, err)
} }
return DomainNameQuery(readCtx, metadata, packet) return DomainNameQuery(readCtx, metadata, buffer.Bytes())
} }
func DomainNameQuery(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error { func DomainNameQuery(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error {
var msg mDNS.Msg var msg mDNS.Msg
err := msg.Unpack(packet) err := msg.Unpack(packet)
if err != nil || msg.Response || len(msg.Question) == 0 || len(msg.Answer) > 0 || len(msg.Ns) > 0 { if err != nil {
return err return err
} }
metadata.Protocol = C.ProtocolDNS metadata.Protocol = C.ProtocolDNS

View File

@ -1,7 +1,6 @@
package sniff_test package sniff_test
import ( import (
"bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"testing" "testing"
@ -22,32 +21,3 @@ func TestSniffDNS(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, C.ProtocolDNS, metadata.Protocol) 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)
}

View File

@ -68,7 +68,7 @@ func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.
} }
sniffError = E.Errors(sniffError, err) sniffError = E.Errors(sniffError, err)
} }
if !errors.Is(sniffError, ErrNeedMoreData) { if !errors.Is(err, ErrNeedMoreData) {
break break
} }
} }

View File

@ -15,11 +15,10 @@ func SSH(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
const sshPrefix = "SSH-2.0-" const sshPrefix = "SSH-2.0-"
bReader := bufio.NewReader(reader) bReader := bufio.NewReader(reader)
prefix, err := bReader.Peek(len(sshPrefix)) prefix, err := bReader.Peek(len(sshPrefix))
if string(prefix[:]) != sshPrefix[:len(prefix)] {
return os.ErrInvalid
}
if err != nil { if err != nil {
return E.Cause1(ErrNeedMoreData, err) return E.Cause1(ErrNeedMoreData, err)
} else if string(prefix) != sshPrefix {
return os.ErrInvalid
} }
fistLine, _, err := bReader.ReadLine() fistLine, _, err := bReader.ReadLine()
if err != nil { if err != nil {

View File

@ -24,24 +24,3 @@ func TestSniffSSH(t *testing.T) {
require.Equal(t, C.ProtocolSSH, metadata.Protocol) require.Equal(t, C.ProtocolSSH, metadata.Protocol)
require.Equal(t, "dropbear", metadata.Client) 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)
}

View File

@ -2,12 +2,6 @@
icon: material/alert-decagram 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 ### 1.11.8
* Improve `auto_redirect` **1** * Improve `auto_redirect` **1**

View File

@ -8,57 +8,56 @@ icon: material/package
=== ":material-debian: Debian / APT" === ":material-debian: Debian / APT"
```bash ```bash
sudo mkdir -p /etc/apt/keyrings && sudo mkdir -p /etc/apt/keyrings &&
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc && sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc &&
sudo chmod a+r /etc/apt/keyrings/sagernet.asc && sudo chmod a+r /etc/apt/keyrings/sagernet.asc &&
echo ' echo '
Types: deb Types: deb
URIs: https://deb.sagernet.org/ URIs: https://deb.sagernet.org/
Suites: * Suites: *
Components: * Components: *
Enabled: yes Enabled: yes
Signed-By: /etc/apt/keyrings/sagernet.asc Signed-By: /etc/apt/keyrings/sagernet.asc
' | sudo tee /etc/apt/sources.list.d/sagernet.sources && ' | sudo tee /etc/apt/sources.list.d/sagernet.sources &&
sudo apt-get update && sudo apt-get update &&
sudo apt-get install sing-box # or sing-box-beta sudo apt-get install sing-box # or sing-box-beta
``` ```
=== ":material-redhat: Redhat / DNF 5" === ":material-redhat: Redhat / DNF 5"
```bash ```bash
sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo && sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo &&
sudo dnf install sing-box # or sing-box-beta sudo dnf install sing-box # or sing-box-beta
``` ```
=== ":material-redhat: Redhat / DNF 4" === ":material-redhat: Redhat / DNF 4"
```bash ```bash
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo && sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo &&
sudo dnf -y install dnf-plugins-core && sudo dnf -y install dnf-plugins-core &&
sudo dnf install sing-box # or sing-box-beta sudo dnf install sing-box # or sing-box-beta
``` ```
## :material-download-box: Manual Installation ## :material-download-box: Manual Installation
The script download and install the latest package from GitHub releases The script download and install the latest package from GitHub releases for deb or rpm based Linux distributions, ArchLinux and OpenWrt.
for deb or rpm based Linux distributions, ArchLinux and OpenWrt.
```shell
```shell curl -fsSL https://sing-box.app/install.sh | sh
curl -fsSL https://sing-box.app/install.sh | sh ```
```
or latest beta:
or latest beta:
```shell
```shell curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta
curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta ```
```
or specific version:
or specific version:
```shell
```shell curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version>
curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version> ```
```
## :material-book-lock-open: Managed Installation ## :material-book-lock-open: Managed Installation

View File

@ -26,38 +26,39 @@ icon: material/package
=== ":material-redhat: Redhat / DNF 5" === ":material-redhat: Redhat / DNF 5"
```bash ```bash
sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo && sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo &&
sudo dnf install sing-box # or sing-box-beta sudo dnf install sing-box # or sing-box-beta
``` ```
=== ":material-redhat: Redhat / DNF 4" === ":material-redhat: Redhat / DNF 4"
```bash ```bash
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo && sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo &&
sudo dnf -y install dnf-plugins-core && sudo dnf -y install dnf-plugins-core &&
sudo dnf install sing-box # or sing-box-beta sudo dnf install sing-box # or sing-box-beta
``` ```
## :material-download-box: 手动安装 ## :material-download-box: 手动安装
该脚本从 GitHub 发布中下载并安装最新的软件包,适用于基于 deb 或 rpm 的 Linux 发行版、ArchLinux 和 OpenWrt。 === ":material-debian: Debian / DEB"
```shell ```bash
curl -fsSL https://sing-box.app/install.sh | sh bash <(curl -fsSL https://sing-box.app/deb-install.sh)
``` ```
或最新测试版: === ":material-redhat: Redhat / RPM"
```shell ```bash
curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta bash <(curl -fsSL https://sing-box.app/rpm-install.sh)
``` ```
(这适用于任何使用 `rpm``systemd` 的发行版。由于 `rpm` 定义依赖关系的方式,如果安装成功,就多半能用。)
或指定版本: === ":simple-archlinux: Archlinux / PKG"
```shell ```bash
curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version> bash <(curl -fsSL https://sing-box.app/arch-install.sh)
``` ```
## :material-book-lock-open: 托管安装 ## :material-book-lock-open: 托管安装

View File

@ -3,92 +3,76 @@
download_beta=false download_beta=false
download_version="" download_version=""
while [ $# -gt 0 ]; do for arg in "$@"; do
case "$1" in if [[ "$arg" == "--beta" ]]; then
--beta) download_beta=true
download_beta=true elif [[ "$arg" == "--version" ]]; then
shift download_version=true
;; elif [[ "$download_version" == 'true' ]]; then
--version) download_version="$arg"
shift else
if [ $# -eq 0 ]; then echo "Unknown argument: $arg"
echo "Missing argument for --version" echo "Usage: $0 [--beta] [--version <version>]"
echo "Usage: $0 [--beta] [--version <version>]" exit 1
exit 1 fi
fi
download_version="$1"
shift
;;
*)
echo "Unknown argument: $1"
echo "Usage: $0 [--beta] [--version <version>]"
exit 1
;;
esac
done done
if command -v pacman >/dev/null 2>&1; then if [[ $(command -v dpkg) ]]; then
os="linux"
arch=$(uname -m)
package_suffix=".pkg.tar.zst"
package_install="pacman -U --noconfirm"
elif command -v dpkg >/dev/null 2>&1; then
os="linux" os="linux"
arch=$(dpkg --print-architecture) arch=$(dpkg --print-architecture)
package_suffix=".deb" package_suffix=".deb"
package_install="dpkg -i" package_install="dpkg -i"
elif command -v dnf >/dev/null 2>&1; then elif [[ $(command -v dnf) ]]; then
os="linux" os="linux"
arch=$(uname -m) arch=$(uname -m)
package_suffix=".rpm" package_suffix=".rpm"
package_install="dnf install -y" package_install="dnf install -y"
elif command -v rpm >/dev/null 2>&1; then elif [[ $(command -v rpm) ]]; then
os="linux" os="linux"
arch=$(uname -m) arch=$(uname -m)
package_suffix=".rpm" package_suffix=".rpm"
package_install="rpm -i" package_install="rpm -i"
elif command -v opkg >/dev/null 2>&1; then elif [[ $(command -v pacman) ]]; then
os="linux"
arch=$(uname -m)
package_suffix=".pkg.tar.zst"
package_install="pacman -U --noconfirm"
elif [[ $(command -v opkg) ]]; then
os="openwrt" os="openwrt"
. /etc/os-release source /etc/os-release
arch="$OPENWRT_ARCH" arch="$OPENWRT_ARCH"
package_suffix=".ipk" package_suffix=".ipk"
package_install="opkg update && opkg install" package_install="opkg update && opkg install -y"
else else
echo "Missing supported package manager." echo "Missing supported package manager."
exit 1 exit 1
fi fi
if [ -z "$download_version" ]; then if [[ -z "$download_version" ]]; then
if [ "$download_beta" != "true" ]; then if [[ "$download_beta" != 'true' ]]; then
if [ -n "$GITHUB_TOKEN" ]; 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) latest_release=$(curl -s --fail-with-body -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases/latest)
else else
latest_release=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases/latest) latest_release=$(curl -s --fail-with-body https://api.github.com/repos/SagerNet/sing-box/releases/latest)
fi fi
curl_exit_status=$? curl_exit_status=$?
if [ $curl_exit_status -ne 0 ]; then if [[ $curl_exit_status -ne 0 ]]; then
exit $curl_exit_status
fi
if [ "$(echo "$latest_release" | grep tag_name | wc -l)" -eq 0 ]; then
echo "$latest_release" echo "$latest_release"
exit 1 exit $?
fi fi
download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | awk -F: '{print $2}' | sed 's/[", v]//g') download_version=$(echo "$latest_release" | grep tag_name | cut -d ":" -f2 | sed 's/\"//g;s/\,//g;s/\ //g;s/v//')
else else
if [ -n "$GITHUB_TOKEN" ]; 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_release=$(curl -s --fail-with-body -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases)
else else
latest_release=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases) latest_release=$(curl -s --fail-with-body https://api.github.com/repos/SagerNet/sing-box/releases)
fi fi
curl_exit_status=$? curl_exit_status=$?
if [ $curl_exit_status -ne 0 ]; then if [[ $? -ne 0 ]]; then
exit $curl_exit_status
fi
if [ "$(echo "$latest_release" | grep tag_name | wc -l)" -eq 0 ]; then
echo "$latest_release" echo "$latest_release"
exit 1 exit $?
fi fi
download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | awk -F: '{print $2}' | sed 's/[", v]//g') download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | cut -d ":" -f2 | sed 's/\"//g;s/\,//g;s/\ //g;s/v//')
fi fi
fi fi
@ -96,21 +80,18 @@ 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}" package_url="https://github.com/SagerNet/sing-box/releases/download/v${download_version}/${package_name}"
echo "Downloading $package_url" echo "Downloading $package_url"
if [ -n "$GITHUB_TOKEN" ]; then if [[ -n "$GITHUB_TOKEN" ]]; then
curl --fail -Lo "$package_name" -H "Authorization: token ${GITHUB_TOKEN}" "$package_url" curl --fail-with-body -Lo "$package_name" -H "Authorization: token ${GITHUB_TOKEN}" "$package_url"
else else
curl --fail -Lo "$package_name" "$package_url" curl --fail-with-body -Lo "$package_name" "$package_url"
fi fi
curl_exit_status=$? if [[ $? -ne 0 ]]; then
if [ $curl_exit_status -ne 0 ]; then exit $?
exit $curl_exit_status
fi fi
if command -v sudo >/dev/null 2>&1; then if [[ $(command -v sudo) ]]; then
package_install="sudo $package_install" package_install="sudo $package_install"
fi fi
echo "$package_install $package_name" echo "$package_install $package_name" && $package_install "$package_name" && rm "$package_name"
sh -c "$package_install \"$package_name\""
rm -f "$package_name"

10
go.mod
View File

@ -26,18 +26,18 @@ require (
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
github.com/sagernet/quic-go v0.49.0-beta.1 github.com/sagernet/quic-go v0.49.0-beta.1
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.6.7 github.com/sagernet/sing v0.6.6
github.com/sagernet/sing-dns v0.4.2 github.com/sagernet/sing-dns v0.4.1
github.com/sagernet/sing-mux v0.3.1 github.com/sagernet/sing-mux v0.3.1
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 github.com/sagernet/sing-quic v0.4.1
github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks v0.2.7
github.com/sagernet/sing-shadowsocks2 v0.2.0 github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/sagernet/sing-shadowtls v0.2.0 github.com/sagernet/sing-shadowtls v0.2.0
github.com/sagernet/sing-tun v0.6.4 github.com/sagernet/sing-tun v0.6.4
github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/sing-vmess v0.2.0
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
github.com/sagernet/utls v1.6.7 github.com/sagernet/utls v1.6.7
github.com/sagernet/wireguard-go v0.0.1-beta.7 github.com/sagernet/wireguard-go v0.0.1-beta.5
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0

22
go.sum
View File

@ -119,14 +119,16 @@ 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 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= 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.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.6.7 h1:NIWBLZ9AUWDXAQBKGleKwsitbQrI9M0nqoheXhUKnrI= github.com/sagernet/sing v0.6.6-0.20250406121928-926a5a1e8bb7 h1:ZJauxLmH12Gzv3nucfjsSBQw9UA8t7Sxu8pYHBSP2TU=
github.com/sagernet/sing v0.6.7/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.6.6-0.20250406121928-926a5a1e8bb7/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-dns v0.4.2 h1:cWe2XPUBFLep2j9kJV4Epg3bctGhMvrrl/sWi9Wszfg= github.com/sagernet/sing v0.6.6 h1:3JkvJ0vqDj/jJcx0a+ve/6lMOrSzZm30I3wrIuZtmRE=
github.com/sagernet/sing-dns v0.4.2/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8= 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-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI= 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-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 h1:iwpCX6H3nZEOGUGwx0q5azcgYOA9f6v9YssihXoRKHk= github.com/sagernet/sing-quic v0.4.1 h1:pxlMa4efZu/M07RgGagNNDDyl6ZUwpmNUjRTpgHOWK4=
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY= github.com/sagernet/sing-quic v0.4.1/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= 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-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 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
@ -135,14 +137,14 @@ 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-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 h1:3Iew6UtAf1+mucVeHKNhAEQI5xmq3CUCbGptUbjebts=
github.com/sagernet/sing-tun v0.6.4/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-tun v0.6.4/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug= github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI=
github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= 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 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= 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 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= 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= github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo= github.com/sagernet/wireguard-go v0.0.1-beta.5/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 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= 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= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=

View File

@ -252,14 +252,6 @@ type _RejectActionOptions struct {
type RejectActionOptions _RejectActionOptions 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 { func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*_RejectActionOptions)(r)) err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
if err != nil { if err != nil {

View File

@ -10,7 +10,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/adapter/outbound"
@ -192,29 +191,9 @@ func (s *Outbound) DialContext(ctx context.Context, network string, destination
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn, err := client.Dial(network, destination.String()) return 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) { func (s *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid 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
}

View File

@ -418,7 +418,6 @@ match:
Port: metadata.Destination.Port, Port: metadata.Destination.Port,
Fqdn: routeOptions.OverrideAddress.Fqdn, Fqdn: routeOptions.OverrideAddress.Fqdn,
} }
metadata.DestinationAddresses = nil
} }
if routeOptions.OverridePort > 0 { if routeOptions.OverridePort > 0 {
metadata.Destination = M.Socksaddr{ metadata.Destination = M.Socksaddr{

View File

@ -223,9 +223,6 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
err error err error
) )
printResult := func() { printResult := func() {
if err == nil && len(responseAddrs) == 0 {
err = E.New("empty result")
}
if err != nil { if err != nil {
if errors.Is(err, dns.ErrResponseRejectedCached) { if errors.Is(err, dns.ErrResponseRejectedCached) {
r.dnsLogger.DebugContext(ctx, "response rejected for ", domain, " (cached)") r.dnsLogger.DebugContext(ctx, "response rejected for ", domain, " (cached)")
@ -234,6 +231,9 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
} else { } else {
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain)) 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) responseAddrs, cached = r.dnsClient.LookupCache(ctx, domain, strategy)

View File

@ -102,10 +102,7 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 { if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
item, err := NewDomainItem(options.Domain, options.DomainSuffix) item := NewDomainItem(options.Domain, options.DomainSuffix)
if err != nil {
return nil, err
}
rule.destinationAddressItems = append(rule.destinationAddressItems, item) rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }

View File

@ -93,10 +93,7 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 { if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
item, err := NewDomainItem(options.Domain, options.DomainSuffix) item := NewDomainItem(options.Domain, options.DomainSuffix)
if err != nil {
return nil, err
}
rule.destinationAddressItems = append(rule.destinationAddressItems, item) rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }

View File

@ -47,10 +47,7 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 { if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
item, err := NewDomainItem(options.Domain, options.DomainSuffix) item := NewDomainItem(options.Domain, options.DomainSuffix)
if err != nil {
return nil, err
}
rule.destinationAddressItems = append(rule.destinationAddressItems, item) rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} else if options.DomainMatcher != nil { } else if options.DomainMatcher != nil {

View File

@ -5,7 +5,6 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common/domain" "github.com/sagernet/sing/common/domain"
E "github.com/sagernet/sing/common/exceptions"
) )
var _ RuleItem = (*DomainItem)(nil) var _ RuleItem = (*DomainItem)(nil)
@ -15,17 +14,7 @@ type DomainItem struct {
description string description string
} }
func NewDomainItem(domains []string, domainSuffixes []string) (*DomainItem, error) { func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem {
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 var description string
if dLen := len(domains); dLen > 0 { if dLen := len(domains); dLen > 0 {
if dLen == 1 { if dLen == 1 {
@ -51,7 +40,7 @@ func NewDomainItem(domains []string, domainSuffixes []string) (*DomainItem, erro
return &DomainItem{ return &DomainItem{
domain.NewMatcher(domains, domainSuffixes, false), domain.NewMatcher(domains, domainSuffixes, false),
description, description,
}, nil }
} }
func NewRawDomainItem(matcher *domain.Matcher) *DomainItem { func NewRawDomainItem(matcher *domain.Matcher) *DomainItem {

View File

@ -91,7 +91,10 @@ func (c *Client) dialContext(ctx context.Context, requestURL *url.URL, headers h
} else { } else {
deadlineConn = conn deadlineConn = conn
} }
deadlineConn.SetDeadline(time.Now().Add(C.TCPTimeout)) err = deadlineConn.SetDeadline(time.Now().Add(C.TCPTimeout))
if err != nil {
return nil, E.Cause(err, "set read deadline")
}
var protocols []string var protocols []string
if protocolHeader := headers.Get("Sec-WebSocket-Protocol"); protocolHeader != "" { if protocolHeader := headers.Get("Sec-WebSocket-Protocol"); protocolHeader != "" {
protocols = []string{protocolHeader} protocols = []string{protocolHeader}

View File

@ -141,7 +141,7 @@ func (e *Endpoint) Start(resolve bool) error {
return nil return nil
} }
var bind conn.Bind var bind conn.Bind
wgListener, isWgListener := common.Cast[conn.Listener](e.options.Dialer) wgListener, isWgListener := e.options.Dialer.(conn.Listener)
if isWgListener { if isWgListener {
bind = conn.NewStdNetBind(wgListener) bind = conn.NewStdNetBind(wgListener)
} else { } else {