Compare commits

..

67 Commits

Author SHA1 Message Date
世界
9b73222314 documentation: Bump version 2025-01-27 10:53:14 +08:00
世界
3923b57abf release: Update NDK to r28-beta3 2025-01-27 10:53:14 +08:00
世界
4807e64609 documentation: Fix typo 2025-01-27 10:04:07 +08:00
世界
eeb37d89f1 Fix rule-set upgrade command 2025-01-27 09:50:27 +08:00
世界
08c1ec4b7e Fix legacy routes 2025-01-27 09:50:27 +08:00
HystericalDragon
6b4cf67add Fix endpoints not close 2025-01-27 09:50:27 +08:00
世界
e65926fd08 Fix tests 2025-01-13 15:14:30 +08:00
世界
f2ec319fe1 Fix system time 2025-01-13 15:14:30 +08:00
世界
32377a61b7 Add port hopping for hysteria2 2025-01-13 15:14:30 +08:00
世界
7aac801ccd tun: Set address sets to routes 2025-01-13 15:14:30 +08:00
世界
96fdf59ee4 Fix default dialer on legacy xiaomi systems 2025-01-13 15:14:30 +08:00
世界
50b8f3ab94 Add rule-set merge command 2025-01-13 15:14:30 +08:00
世界
ff7aaf977b Fix DNS match 2025-01-13 15:14:30 +08:00
世界
9a1efbe54d Fix domain strategy 2025-01-13 15:14:30 +08:00
世界
906c21f458 Fix time service 2025-01-13 15:14:30 +08:00
世界
d5e7af7a7e Fix socks5 UDP implementation 2025-01-13 15:14:30 +08:00
世界
4d41f03bd5 clash-api: Fix missing endpoints 2025-01-13 15:14:30 +08:00
世界
30704a15a7 hysteria2: Add more masquerade options 2025-01-13 15:14:30 +08:00
世界
83889178ed Improve timeouts 2025-01-13 15:14:30 +08:00
世界
1d2720bf5e Add UDP timeout route option 2025-01-13 15:14:30 +08:00
世界
c4b6d0eadb Make GSO adaptive 2025-01-13 15:14:30 +08:00
世界
0c66888691 Fix lint 2025-01-13 15:14:30 +08:00
世界
68781387fe refactor: WireGuard endpoint 2025-01-13 15:14:30 +08:00
世界
fd299a0961 refactor: connection manager 2025-01-13 15:14:30 +08:00
世界
285a82050c documentation: Fix typo 2025-01-13 15:14:30 +08:00
世界
2dbb8c55c9 Add override destination to route options 2025-01-13 15:14:30 +08:00
世界
effcf39469 Add dns.cache_capacity 2025-01-13 15:14:30 +08:00
世界
9db9484863 Refactor multi networks strategy 2025-01-13 15:14:30 +08:00
世界
ca813f461b documentation: Remove unused titles 2025-01-13 15:14:30 +08:00
世界
bb46cdb2b3 Add multi network dialing 2025-01-13 15:14:30 +08:00
世界
dcb10c21a1 documentation: Merge route options to route actions 2025-01-13 15:14:29 +08:00
世界
05ea0ca00e Add network_[type/is_expensive/is_constrained] rule items 2025-01-13 15:14:29 +08:00
世界
c098f282b1 Merge route options to route actions 2025-01-13 15:14:29 +08:00
世界
ecf82d197c refactor: Platform Interfaces 2025-01-13 15:14:29 +08:00
世界
9afe75586a refactor: Extract services form router 2025-01-13 15:14:29 +08:00
世界
a1be455202 refactor: Modular network manager 2025-01-13 15:14:29 +08:00
世界
19fb214226 refactor: Modular inbound/outbound manager 2025-01-13 15:14:29 +08:00
世界
28ec898a8c documentation: Add rule action 2025-01-13 15:14:29 +08:00
世界
467b1bbeeb documentation: Update the scheduled removal time of deprecated features 2025-01-13 15:14:29 +08:00
世界
02ab8ce806 documentation: Remove outdated icons 2025-01-13 15:14:29 +08:00
世界
ce69e620e9 Migrate bad options to library 2025-01-13 15:14:29 +08:00
世界
1133cf3ef5 Implement udp connect 2025-01-13 15:14:29 +08:00
世界
59a607e303 Implement new deprecated warnings 2025-01-13 15:14:29 +08:00
世界
313be3d7a4 Improve rule actions 2025-01-13 15:14:29 +08:00
世界
4fe40fcee0 Remove unused reject methods 2025-01-13 15:14:29 +08:00
世界
e233fd4fe5 refactor: Modular inbounds/outbounds 2025-01-13 15:14:29 +08:00
世界
9f7683818f Implement dns-hijack 2025-01-13 15:14:29 +08:00
世界
179e3cb2f5 Implement resolve(server) 2025-01-13 15:14:29 +08:00
世界
41b960552d Implement TCP and ICMP rejects 2025-01-13 15:14:29 +08:00
世界
8304295c48 Crazy sekai overturns the small pond 2025-01-13 15:14:29 +08:00
世界
253b41936e Bump version 2025-01-11 20:58:00 +08:00
世界
ce5b4b06b5 auto-redirect: Fix fetch interfaces 2025-01-07 21:24:52 +08:00
世界
50f5006c43 Fix leak in reality server 2025-01-07 17:27:10 +08:00
世界
e42ff22c2e quic: Fix source not unwrapped 2025-01-07 17:27:10 +08:00
世界
578571b972 Bump version 2025-01-02 15:21:13 +08:00
世界
935beca45d Fix check interface 2025-01-01 12:19:56 +08:00
世界
3e246f1173 Fix vmess mux leak 2025-01-01 12:18:56 +08:00
世界
1bc27a32c2 Fix linter configuration 2025-01-01 12:18:56 +08:00
世界
bc2e3960e4 Enable fix stack for Android 7 and 9 2024-12-28 15:06:57 +08:00
世界
9c4ab0bf33 Bump version 2024-12-21 17:20:56 +08:00
世界
27bdef34c7 release: Fix create app store version 2024-12-21 17:20:56 +08:00
世界
3c00099ed4 Fix process rule check 2024-12-21 17:15:41 +08:00
世界
2babf07f9a Fix wireguard 2024-12-21 16:07:19 +08:00
世界
4795ed712b release: Fix android version 2024-12-21 16:07:19 +08:00
世界
d4cd564dbe release: Fix check tag 2024-12-21 16:07:19 +08:00
世界
1676e13d3e Bump version 2024-12-17 13:43:17 +08:00
世界
50576084c6 release: Add publish testflight 2024-12-17 13:43:17 +08:00
66 changed files with 1165 additions and 822 deletions

View File

@@ -144,7 +144,7 @@ jobs:
~/go/go1.20.14
key: go120
- name: Setup legacy Go
if: matrix.require_legacy_go == 'true' && steps.cache-legacy-go.outputs.cache-hit != 'true'
if: matrix.require_legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true'
run: |-
wget https://dl.google.com/go/go1.20.14.linux-amd64.tar.gz
tar -xzf go1.20.14.linux-amd64.tar.gz
@@ -159,7 +159,7 @@ jobs:
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser-pro
version: latest
version: 2.5.1
install-only: true
- name: Extract signing key
run: |-
@@ -170,7 +170,8 @@ jobs:
echo "HOME=$HOME" >> "$GITHUB_ENV"
- name: Set tag
run: |-
git tag v${{ needs.calculate_version.outputs.version }}
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
git tag v${{ needs.calculate_version.outputs.version }} -f
- name: Build
if: matrix.goos != 'android'
run: |-
@@ -223,14 +224,15 @@ jobs:
id: setup-ndk
uses: nttld/setup-ndk@v1
with:
ndk-version: r28-beta2
ndk-version: r28-beta3
- name: Setup OpenJDK
run: |-
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
/usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
- name: Set tag
run: |-
git tag v${{ needs.calculate_version.outputs.version }}
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
git tag v${{ needs.calculate_version.outputs.version }} -f
- name: Build library
run: |-
make lib_install
@@ -297,14 +299,15 @@ jobs:
id: setup-ndk
uses: nttld/setup-ndk@v1
with:
ndk-version: r28-beta2
ndk-version: r28-beta3
- name: Setup OpenJDK
run: |-
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
/usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
- name: Set tag
run: |-
git tag v${{ needs.calculate_version.outputs.version }}
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
git tag v${{ needs.calculate_version.outputs.version }} -f
- name: Build library
run: |-
make lib_install
@@ -393,7 +396,7 @@ jobs:
- name: Setup Xcode stable
if: matrix.if && github.ref == 'refs/heads/main-next'
run: |-
sudo xcode-select -s /Applications/Xcode_16.1.app
sudo xcode-select -s /Applications/Xcode_16.2.app
- name: Setup Xcode beta
if: matrix.if && github.ref == 'refs/heads/dev-next'
run: |-
@@ -401,7 +404,8 @@ jobs:
- name: Set tag
if: matrix.if
run: |-
git tag v${{ needs.calculate_version.outputs.version }}
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
git tag v${{ needs.calculate_version.outputs.version }} -f
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
- name: Checkout main branch
if: matrix.if && github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
@@ -491,6 +495,10 @@ jobs:
-authenticationKeyPath $ASC_KEY_PATH \
-authenticationKeyID $ASC_KEY_ID \
-authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
- name: Publish to TestFlight
if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' && github.ref =='refs/heads/dev-next'
run: |-
go run -v ./cmd/internal/app_store_connect publish_testflight ${{ matrix.platform }}
- name: Build image
if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
run: |-
@@ -540,7 +548,7 @@ jobs:
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser-pro
version: latest
version: 2.5.1
install-only: true
- name: Cache ghr
uses: actions/cache@v4
@@ -558,7 +566,8 @@ jobs:
go install -v .
- name: Set tag
run: |-
git tag v${{ needs.calculate_version.outputs.version }}
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
git tag v${{ needs.calculate_version.outputs.version }} -f
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
- name: Download builds
uses: actions/download-artifact@v4
@@ -575,8 +584,16 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
- name: Upload builds
if: ${{ env.PUBLISHED == 'false' }}
run: |-
export PATH="$PATH:$HOME/go/bin"
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Replace builds
if: ${{ env.PUBLISHED != 'false' }}
run: |-
export PATH="$PATH:$HOME/go/bin"
ghr --replace -p 5 "v${VERSION}" dist/release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -22,6 +22,16 @@ linters-settings:
run:
go: "1.23"
build-tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
- with_ech
- with_utls
- with_reality_server
- with_acme
- with_clash_api
issues:
exclude-dirs:

View File

@@ -52,7 +52,7 @@ builds:
env:
- CGO_ENABLED=0
- GOROOT={{ .Env.GOPATH }}/go1.20.14
gobinary: "{{ .Env.GOPATH }}/go1.20.14/bin/go"
tool: "{{ .Env.GOPATH }}/go1.20.14/bin/go"
targets:
- windows_amd64_v1
- windows_386

View File

@@ -28,7 +28,7 @@ ci_build:
go build $(MAIN_PARAMS) $(MAIN)
generate_completions:
go run -v --tags generate,generate_completions $(MAIN)
go run -v --tags $(TAGS),generate,generate_completions $(MAIN)
install:
go build -o $(PREFIX)/bin/$(NAME) $(MAIN_PARAMS) $(MAIN)

2
box.go
View File

@@ -399,7 +399,7 @@ func (s *Box) Close() error {
close(s.done)
}
err := common.Close(
s.inbound, s.outbound, s.router, s.connection, s.network,
s.inbound, s.outbound, s.endpoint, s.router, s.connection, s.network,
)
for _, lifecycleService := range s.services {
err = E.Append(err, lifecycleService.Close(), func(err error) error {

View File

@@ -1,30 +0,0 @@
package main
import (
"context"
"fmt"
_ "unsafe"
"github.com/cidertool/asc-go/asc"
)
type Client struct {
*asc.Client
}
func (c *Client) UpdateBuildForAppStoreVersion(ctx context.Context, id string, buildID *string) (*asc.Response, error) {
linkage := newRelationshipDeclaration(buildID, "builds")
url := fmt.Sprintf("appStoreVersions/%s/relationships/build", id)
return c.patch(ctx, url, newRequestBody(linkage), nil)
}
func newRelationshipDeclaration(id *string, relationshipType string) *asc.RelationshipData {
if id == nil {
return nil
}
return &asc.RelationshipData{
ID: *id,
Type: relationshipType,
}
}

View File

@@ -1,140 +0,0 @@
package main
import (
"context"
"net/http"
"net/url"
"reflect"
_ "unsafe"
"github.com/cidertool/asc-go/asc"
"github.com/google/go-querystring/query"
)
func (c *Client) newRequest(ctx context.Context, method string, path string, body *requestBody, options ...requestOption) (*http.Request, error) {
return clientNewRequest(c.Client, ctx, method, path, body, options...)
}
//go:linkname clientNewRequest github.com/cidertool/asc-go/asc.(*Client).newRequest
func clientNewRequest(c *asc.Client, ctx context.Context, method string, path string, body *requestBody, options ...requestOption) (*http.Request, error)
func (c *Client) do(ctx context.Context, req *http.Request, v interface{}) (*asc.Response, error) {
return clientDo(c.Client, ctx, req, v)
}
//go:linkname clientDo github.com/cidertool/asc-go/asc.(*Client).do
func clientDo(c *asc.Client, ctx context.Context, req *http.Request, v interface{}) (*asc.Response, error)
// get sends a GET request to the API as configured.
func (c *Client) get(ctx context.Context, url string, query interface{}, v interface{}, options ...requestOption) (*asc.Response, error) {
var err error
if query != nil {
url, err = appendingQueryOptions(url, query)
if err != nil {
return nil, err
}
}
req, err := c.newRequest(ctx, "GET", url, nil, options...)
if err != nil {
return nil, err
}
resp, err := c.do(ctx, req, v)
if err != nil {
return resp, err
}
return resp, err
}
// post sends a POST request to the API as configured.
func (c *Client) post(ctx context.Context, url string, body *requestBody, v interface{}) (*asc.Response, error) {
req, err := c.newRequest(ctx, "POST", url, body, withContentType("application/json"))
if err != nil {
return nil, err
}
resp, err := c.do(ctx, req, v)
if err != nil {
return resp, err
}
return resp, err
}
// patch sends a PATCH request to the API as configured.
func (c *Client) patch(ctx context.Context, url string, body *requestBody, v interface{}) (*asc.Response, error) {
req, err := c.newRequest(ctx, "PATCH", url, body, withContentType("application/json"))
if err != nil {
return nil, err
}
resp, err := c.do(ctx, req, v)
if err != nil {
return resp, err
}
return resp, err
}
// delete sends a DELETE request to the API as configured.
func (c *Client) delete(ctx context.Context, url string, body *requestBody) (*asc.Response, error) {
req, err := c.newRequest(ctx, "DELETE", url, body, withContentType("application/json"))
if err != nil {
return nil, err
}
return c.do(ctx, req, nil)
}
// request is a common structure for a request body sent to the API.
type requestBody struct {
Data interface{} `json:"data"`
Included interface{} `json:"included,omitempty"`
}
func newRequestBody(data interface{}) *requestBody {
return newRequestBodyWithIncluded(data, nil)
}
func newRequestBodyWithIncluded(data interface{}, included interface{}) *requestBody {
return &requestBody{Data: data, Included: included}
}
type requestOption func(*http.Request)
func withAccept(typ string) requestOption {
return func(req *http.Request) {
req.Header.Set("Accept", typ)
}
}
func withContentType(typ string) requestOption {
return func(req *http.Request) {
req.Header.Set("Content-Type", typ)
}
}
// AddOptions adds the parameters in opt as URL query parameters to s. opt
// must be a struct whose fields may contain "url" tags.
func appendingQueryOptions(s string, opt interface{}) (string, error) {
v := reflect.ValueOf(opt)
if v.Kind() == reflect.Ptr && v.IsNil() {
return s, nil
}
u, err := url.Parse(s)
if err != nil {
return s, err
}
qs, err := query.Values(opt)
if err != nil {
return s, err
}
u.RawQuery = qs.Encode()
return u.String(), nil
}

View File

@@ -7,13 +7,12 @@ import (
"strconv"
"time"
"github.com/sagernet/asc-go/asc"
"github.com/sagernet/sing-box/cmd/internal/build_shared"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/cidertool/asc-go/asc"
)
func main() {
@@ -54,20 +53,20 @@ const (
groupID = "5c5f3b78-b7a0-40c0-bcad-e6ef87bbefda"
)
func createClient() *Client {
func createClient(expireDuration time.Duration) *asc.Client {
privateKey, err := os.ReadFile(os.Getenv("ASC_KEY_PATH"))
if err != nil {
log.Fatal(err)
}
tokenConfig, err := asc.NewTokenConfig(os.Getenv("ASC_KEY_ID"), os.Getenv("ASC_KEY_ISSUER_ID"), time.Minute, privateKey)
tokenConfig, err := asc.NewTokenConfig(os.Getenv("ASC_KEY_ID"), os.Getenv("ASC_KEY_ISSUER_ID"), expireDuration, privateKey)
if err != nil {
log.Fatal(err)
}
return &Client{asc.NewClient(tokenConfig.Client())}
return asc.NewClient(tokenConfig.Client())
}
func fetchMacOSVersion(ctx context.Context) error {
client := createClient()
client := createClient(time.Minute)
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
FilterPlatform: []string{"MAC_OS"},
})
@@ -105,71 +104,101 @@ func publishTestflight(ctx context.Context) error {
return err
}
tag := tagVersion.VersionString()
client := createClient()
client := createClient(10 * time.Minute)
log.Info(tag, " list build IDs")
buildIDsResponse, _, err := client.TestFlight.ListBuildIDsForBetaGroup(ctx, groupID, nil)
if err != nil {
return err
}
buildIDS := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string {
buildIDs := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string {
return it.ID
})
for _, platform := range []asc.Platform{
asc.PlatformIOS,
asc.PlatformMACOS,
asc.PlatformTVOS,
} {
var platforms []asc.Platform
if len(os.Args) == 3 {
switch os.Args[2] {
case "ios":
platforms = []asc.Platform{asc.PlatformIOS}
case "macos":
platforms = []asc.Platform{asc.PlatformMACOS}
case "tvos":
platforms = []asc.Platform{asc.PlatformTVOS}
default:
return E.New("unknown platform: ", os.Args[2])
}
} else {
platforms = []asc.Platform{
asc.PlatformIOS,
asc.PlatformMACOS,
asc.PlatformTVOS,
}
}
for _, platform := range platforms {
log.Info(string(platform), " list builds")
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
FilterApp: []string{appID},
FilterPreReleaseVersionPlatform: []string{string(platform)},
})
if err != nil {
return err
}
log.Info(string(platform), " ", tag, " list localizations")
localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, builds.Data[0].ID, nil)
if err != nil {
return err
}
localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool {
return *it.Attributes.Locale == "en-US"
})
if localization.ID == "" {
log.Fatal(string(platform), " ", tag, " no en-US localization found")
}
if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" {
log.Info(string(platform), " ", tag, " update localization")
_, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr(
F.ToString("sing-box ", tag),
))
for {
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
FilterApp: []string{appID},
FilterPreReleaseVersionPlatform: []string{string(platform)},
})
if err != nil {
return err
}
}
if !common.Contains(buildIDS, builds.Data[0].ID) {
build := builds.Data[0]
if common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 5*time.Minute {
log.Info(string(platform), " ", tag, " waiting for process")
time.Sleep(15 * time.Second)
continue
}
if *build.Attributes.ProcessingState != "VALID" {
log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState)
time.Sleep(15 * time.Second)
continue
}
log.Info(string(platform), " ", tag, " list localizations")
localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, build.ID, nil)
if err != nil {
return err
}
localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool {
return *it.Attributes.Locale == "en-US"
})
if localization.ID == "" {
log.Fatal(string(platform), " ", tag, " no en-US localization found")
}
if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" {
log.Info(string(platform), " ", tag, " update localization")
_, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr(
F.ToString("sing-box ", tagVersion.String()),
))
if err != nil {
return err
}
}
log.Info(string(platform), " ", tag, " publish")
_, err = client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{builds.Data[0].ID})
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
if response != nil && response.StatusCode == http.StatusUnprocessableEntity {
log.Info("waiting for process")
time.Sleep(15 * time.Second)
continue
} else if err != nil {
return err
}
log.Info(string(platform), " ", tag, " list submissions")
betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{
FilterBuild: []string{build.ID},
})
if err != nil {
return err
}
}
log.Info(string(platform), " ", tag, " list submissions")
betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{
FilterBuild: []string{builds.Data[0].ID},
})
if err != nil {
return err
}
if len(betaSubmissions.Data) == 0 {
log.Info(string(platform), " ", tag, " create submission")
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, builds.Data[0].ID)
if err != nil {
return err
if len(betaSubmissions.Data) == 0 {
log.Info(string(platform), " ", tag, " create submission")
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
if err != nil {
return err
}
}
continue
break
}
}
return nil
}
@@ -187,34 +216,40 @@ func cancelAppStore(ctx context.Context, platform string) error {
if err != nil {
return err
}
client := createClient()
log.Info(platform, " list versions")
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
FilterPlatform: []string{string(platform)},
})
if err != nil {
return err
}
version := common.Find(versions.Data, func(it asc.AppStoreVersion) bool {
return *it.Attributes.VersionString == tag
})
if version.ID == "" {
client := createClient(time.Minute)
for {
log.Info(platform, " list versions")
versions, response, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
FilterPlatform: []string{string(platform)},
})
if isRetryable(response) {
continue
} else if err != nil {
return err
}
version := common.Find(versions.Data, func(it asc.AppStoreVersion) bool {
return *it.Attributes.VersionString == tag
})
if version.ID == "" {
return nil
}
log.Info(platform, " ", tag, " get submission")
submission, response, err := client.Submission.GetAppStoreVersionSubmissionForAppStoreVersion(ctx, version.ID, nil)
if response != nil && response.StatusCode == http.StatusNotFound {
return nil
}
if isRetryable(response) {
continue
} else if err != nil {
return err
}
log.Info(platform, " ", tag, " delete submission")
_, err = client.Submission.DeleteSubmission(ctx, submission.Data.ID)
if err != nil {
return err
}
return nil
}
log.Info(string(platform), " ", tag, " get submission")
submission, response, err := client.Submission.GetAppStoreVersionSubmissionForAppStoreVersion(ctx, version.ID, nil)
if response != nil && response.StatusCode == http.StatusNotFound {
return nil
}
if err != nil {
return err
}
log.Info(platform, " ", tag, " delete submission")
_, err = client.Submission.DeleteSubmission(ctx, submission.Data.ID)
if err != nil {
return err
}
return nil
}
func prepareAppStore(ctx context.Context) error {
@@ -222,7 +257,7 @@ func prepareAppStore(ctx context.Context) error {
if err != nil {
return err
}
client := createClient()
client := createClient(time.Minute)
for _, platform := range []asc.Platform{
asc.PlatformIOS,
asc.PlatformMACOS,
@@ -291,7 +326,7 @@ func prepareAppStore(ctx context.Context) error {
log.Fatal(string(platform), " ", tag, " unknown state ", string(*version.Attributes.AppStoreState))
}
log.Info(string(platform), " ", tag, " update build")
response, err = client.UpdateBuildForAppStoreVersion(ctx, version.ID, buildID)
response, err = client.Apps.UpdateBuildForAppStoreVersion(ctx, version.ID, buildID)
if err != nil {
return err
}
@@ -324,7 +359,7 @@ func prepareAppStore(ctx context.Context) error {
if localization.ID == "" {
log.Info(string(platform), " ", tag, " no en-US localization found")
}
if localization.Attributes.WhatsNew == nil && *localization.Attributes.WhatsNew == "" {
if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" {
log.Info(string(platform), " ", tag, " update localization")
_, _, err = client.Apps.UpdateAppStoreVersionLocalization(ctx, localization.ID, &asc.AppStoreVersionLocalizationUpdateRequestAttributes{
PromotionalText: common.Ptr("Yet another distribution for sing-box, the universal proxy platform."),
@@ -343,16 +378,14 @@ func prepareAppStore(ctx context.Context) error {
case http.StatusInternalServerError:
continue
default:
response.Write(os.Stderr)
log.Info(string(platform), " ", tag, " unexpected response: ", response.Status)
return err
}
}
switch response.StatusCode {
case http.StatusCreated:
break fixSubmit
default:
response.Write(os.Stderr)
log.Info(string(platform), " ", tag, " unexpected response: ", response.Status)
return err
}
}
}
@@ -364,7 +397,7 @@ func publishAppStore(ctx context.Context) error {
if err != nil {
return err
}
client := createClient()
client := createClient(time.Minute)
for _, platform := range []asc.Platform{
asc.PlatformIOS,
asc.PlatformMACOS,
@@ -398,3 +431,15 @@ func publishAppStore(ctx context.Context) error {
}
return nil
}
func isRetryable(response *asc.Response) bool {
if response == nil {
return false
}
switch response.StatusCode {
case http.StatusInternalServerError, http.StatusUnprocessableEntity:
return true
default:
return false
}
}

View File

@@ -48,7 +48,7 @@ func FindSDK() {
}
func findNDK() bool {
const fixedVersion = "28.0.12674087"
const fixedVersion = "28.0.12916984"
const versionFile = "source.properties"
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
androidNDKPath = fixedPath

View File

@@ -18,7 +18,7 @@ import (
)
var commandMerge = &cobra.Command{
Use: "merge <output>",
Use: "merge <output-path>",
Short: "Merge configurations",
Run: func(cmd *cobra.Command, args []string) {
err := merge(args[0])

View File

@@ -0,0 +1,162 @@
package main
import (
"bytes"
"io"
"os"
"path/filepath"
"sort"
"strings"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
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/rw"
"github.com/spf13/cobra"
)
var (
ruleSetPaths []string
ruleSetDirectories []string
)
var commandRuleSetMerge = &cobra.Command{
Use: "merge <output-path>",
Short: "Merge rule-set source files",
Run: func(cmd *cobra.Command, args []string) {
err := mergeRuleSet(args[0])
if err != nil {
log.Fatal(err)
}
},
Args: cobra.ExactArgs(1),
}
func init() {
commandRuleSetMerge.Flags().StringArrayVarP(&ruleSetPaths, "config", "c", nil, "set input rule-set file path")
commandRuleSetMerge.Flags().StringArrayVarP(&ruleSetDirectories, "config-directory", "C", nil, "set input rule-set directory path")
commandRuleSet.AddCommand(commandRuleSetMerge)
}
type RuleSetEntry struct {
content []byte
path string
options option.PlainRuleSetCompat
}
func readRuleSetAt(path string) (*RuleSetEntry, error) {
var (
configContent []byte
err error
)
if path == "stdin" {
configContent, err = io.ReadAll(os.Stdin)
} else {
configContent, err = os.ReadFile(path)
}
if err != nil {
return nil, E.Cause(err, "read config at ", path)
}
options, err := json.UnmarshalExtendedContext[option.PlainRuleSetCompat](globalCtx, configContent)
if err != nil {
return nil, E.Cause(err, "decode config at ", path)
}
return &RuleSetEntry{
content: configContent,
path: path,
options: options,
}, nil
}
func readRuleSet() ([]*RuleSetEntry, error) {
var optionsList []*RuleSetEntry
for _, path := range ruleSetPaths {
optionsEntry, err := readRuleSetAt(path)
if err != nil {
return nil, err
}
optionsList = append(optionsList, optionsEntry)
}
for _, directory := range ruleSetDirectories {
entries, err := os.ReadDir(directory)
if err != nil {
return nil, E.Cause(err, "read rule-set directory at ", directory)
}
for _, entry := range entries {
if !strings.HasSuffix(entry.Name(), ".json") || entry.IsDir() {
continue
}
optionsEntry, err := readRuleSetAt(filepath.Join(directory, entry.Name()))
if err != nil {
return nil, err
}
optionsList = append(optionsList, optionsEntry)
}
}
sort.Slice(optionsList, func(i, j int) bool {
return optionsList[i].path < optionsList[j].path
})
return optionsList, nil
}
func readRuleSetAndMerge() (option.PlainRuleSetCompat, error) {
optionsList, err := readRuleSet()
if err != nil {
return option.PlainRuleSetCompat{}, err
}
if len(optionsList) == 1 {
return optionsList[0].options, nil
}
var optionVersion uint8
for _, options := range optionsList {
if optionVersion < options.options.Version {
optionVersion = options.options.Version
}
}
var mergedMessage json.RawMessage
for _, options := range optionsList {
mergedMessage, err = badjson.MergeJSON(globalCtx, options.options.RawMessage, mergedMessage, false)
if err != nil {
return option.PlainRuleSetCompat{}, E.Cause(err, "merge config at ", options.path)
}
}
mergedOptions, err := json.UnmarshalExtendedContext[option.PlainRuleSetCompat](globalCtx, mergedMessage)
if err != nil {
return option.PlainRuleSetCompat{}, E.Cause(err, "unmarshal merged config")
}
mergedOptions.Version = optionVersion
return mergedOptions, nil
}
func mergeRuleSet(outputPath string) error {
mergedOptions, err := readRuleSetAndMerge()
if err != nil {
return err
}
buffer := new(bytes.Buffer)
encoder := json.NewEncoder(buffer)
encoder.SetIndent("", " ")
err = encoder.Encode(mergedOptions)
if err != nil {
return E.Cause(err, "encode config")
}
if existsContent, err := os.ReadFile(outputPath); err != nil {
if string(existsContent) == buffer.String() {
return nil
}
}
err = rw.MkdirParent(outputPath)
if err != nil {
return err
}
err = os.WriteFile(outputPath, buffer.Bytes(), 0o644)
if err != nil {
return err
}
outputPath, _ = filepath.Abs(outputPath)
os.Stderr.WriteString(outputPath + "\n")
return nil
}

View File

@@ -61,14 +61,15 @@ func upgradeRuleSet(sourcePath string) error {
log.Info("already up-to-date")
return nil
}
plainRuleSet, err := plainRuleSetCompat.Upgrade()
plainRuleSetCompat.Options, err = plainRuleSetCompat.Upgrade()
if err != nil {
return err
}
plainRuleSetCompat.Version = C.RuleSetVersionCurrent
buffer := new(bytes.Buffer)
encoder := json.NewEncoder(buffer)
encoder.SetIndent("", " ")
err = encoder.Encode(plainRuleSet)
err = encoder.Encode(plainRuleSetCompat)
if err != nil {
return E.Cause(err, "encode config")
}

View File

@@ -21,7 +21,7 @@ func initializeHTTP3Client(instance *box.Box) error {
return err
}
http3Client = &http.Client{
Transport: &http3.RoundTripper{
Transport: &http3.Transport{
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
destination := M.ParseSocksaddr(addr)
udpConn, dErr := dialer.DialContext(ctx, N.NetworkUDP, destination)

View File

@@ -4,7 +4,6 @@ import (
"context"
"os"
"github.com/sagernet/sing-box/common/settings"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
E "github.com/sagernet/sing/common/exceptions"
@@ -58,7 +57,7 @@ func syncTime() error {
return err
}
if commandSyncTimeWrite {
err = settings.SetSystemTime(response.Time)
err = ntp.SetSystemTime(response.Time)
if err != nil {
return E.Cause(err, "write time to system")
}

View File

@@ -2,8 +2,10 @@ package dialer
import (
"context"
"errors"
"net"
"net/netip"
"syscall"
"time"
"github.com/sagernet/sing-box/adapter"
@@ -26,20 +28,21 @@ var (
)
type DefaultDialer struct {
dialer4 tcpDialer
dialer6 tcpDialer
udpDialer4 net.Dialer
udpDialer6 net.Dialer
udpListener net.ListenConfig
udpAddr4 string
udpAddr6 string
isWireGuardListener bool
networkManager adapter.NetworkManager
networkStrategy *C.NetworkStrategy
networkType []C.InterfaceType
fallbackNetworkType []C.InterfaceType
networkFallbackDelay time.Duration
networkLastFallback atomic.TypedValue[time.Time]
dialer4 tcpDialer
dialer6 tcpDialer
udpDialer4 net.Dialer
udpDialer6 net.Dialer
udpListener net.ListenConfig
udpAddr4 string
udpAddr6 string
isWireGuardListener bool
networkManager adapter.NetworkManager
networkStrategy *C.NetworkStrategy
defaultNetworkStrategy bool
networkType []C.InterfaceType
fallbackNetworkType []C.InterfaceType
networkFallbackDelay time.Duration
networkLastFallback atomic.TypedValue[time.Time]
}
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
@@ -47,13 +50,14 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
platformInterface := service.FromContext[platform.Interface](ctx)
var (
dialer net.Dialer
listener net.ListenConfig
interfaceFinder control.InterfaceFinder
networkStrategy *C.NetworkStrategy
networkType []C.InterfaceType
fallbackNetworkType []C.InterfaceType
networkFallbackDelay time.Duration
dialer net.Dialer
listener net.ListenConfig
interfaceFinder control.InterfaceFinder
networkStrategy *C.NetworkStrategy
defaultNetworkStrategy bool
networkType []C.InterfaceType
fallbackNetworkType []C.InterfaceType
networkFallbackDelay time.Duration
)
if networkManager != nil {
interfaceFinder = networkManager.InterfaceFinder()
@@ -79,7 +83,8 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
listener.Control = control.Append(listener.Control, control.RoutingMark(autoRedirectOutputMark))
}
}
if options.BindInterface != "" || options.Inet4BindAddress != nil || options.Inet6BindAddress != nil || options.TCPFastOpen {
disableDefaultBind := options.BindInterface != "" || options.Inet4BindAddress != nil || options.Inet6BindAddress != nil
if disableDefaultBind || options.TCPFastOpen {
if options.NetworkStrategy != nil || len(options.NetworkType) > 0 && options.FallbackNetworkType == nil && options.FallbackDelay == 0 {
return nil, E.New("`network_strategy` is conflict with `bind_interface`, `inet4_bind_address`, `inet6_bind_address` and `tcp_fast_open`")
}
@@ -87,34 +92,37 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
if networkManager != nil {
defaultOptions := networkManager.DefaultOptions()
if defaultOptions.BindInterface != "" {
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else if networkManager.AutoDetectInterface() {
if platformInterface != nil {
networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy)
if networkStrategy == nil {
networkStrategy = common.Ptr(C.NetworkStrategyDefault)
}
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
if networkStrategy == nil && len(networkType) == 0 && len(fallbackNetworkType) == 0 {
networkStrategy = defaultOptions.NetworkStrategy
networkType = defaultOptions.NetworkType
fallbackNetworkType = defaultOptions.FallbackNetworkType
}
networkFallbackDelay = time.Duration(options.FallbackDelay)
if networkFallbackDelay == 0 && defaultOptions.FallbackDelay != 0 {
networkFallbackDelay = defaultOptions.FallbackDelay
}
bindFunc := networkManager.ProtectFunc()
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else {
bindFunc := networkManager.AutoDetectInterfaceFunc()
if !disableDefaultBind {
if defaultOptions.BindInterface != "" {
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else if networkManager.AutoDetectInterface() {
if platformInterface != nil {
networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy)
if networkStrategy == nil {
networkStrategy = common.Ptr(C.NetworkStrategyDefault)
defaultNetworkStrategy = true
}
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
if networkStrategy == nil && len(networkType) == 0 && len(fallbackNetworkType) == 0 {
networkStrategy = defaultOptions.NetworkStrategy
networkType = defaultOptions.NetworkType
fallbackNetworkType = defaultOptions.FallbackNetworkType
}
networkFallbackDelay = time.Duration(options.FallbackDelay)
if networkFallbackDelay == 0 && defaultOptions.FallbackDelay != 0 {
networkFallbackDelay = defaultOptions.FallbackDelay
}
bindFunc := networkManager.ProtectFunc()
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else {
bindFunc := networkManager.AutoDetectInterfaceFunc()
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
}
}
}
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
@@ -189,19 +197,20 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
return nil, err
}
return &DefaultDialer{
dialer4: tcpDialer4,
dialer6: tcpDialer6,
udpDialer4: udpDialer4,
udpDialer6: udpDialer6,
udpListener: listener,
udpAddr4: udpAddr4,
udpAddr6: udpAddr6,
isWireGuardListener: options.IsWireGuardListener,
networkManager: networkManager,
networkStrategy: networkStrategy,
networkType: networkType,
fallbackNetworkType: fallbackNetworkType,
networkFallbackDelay: networkFallbackDelay,
dialer4: tcpDialer4,
dialer6: tcpDialer6,
udpDialer4: udpDialer4,
udpDialer6: udpDialer6,
udpListener: listener,
udpAddr4: udpAddr4,
udpAddr6: udpAddr6,
isWireGuardListener: options.IsWireGuardListener,
networkManager: networkManager,
networkStrategy: networkStrategy,
defaultNetworkStrategy: defaultNetworkStrategy,
networkType: networkType,
fallbackNetworkType: fallbackNetworkType,
networkFallbackDelay: networkFallbackDelay,
}, nil
}
@@ -262,7 +271,13 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
conn, isPrimary, err = d.dialParallelInterfaceFastFallback(ctx, dialer, network, address.String(), *strategy, interfaceType, fallbackInterfaceType, fallbackDelay, d.networkLastFallback.Store)
}
if err != nil {
return nil, err
// bind interface failed on legacy xiaomi systems
if d.defaultNetworkStrategy && errors.Is(err, syscall.EPERM) {
d.networkStrategy = nil
return d.DialContext(ctx, network, address)
} else {
return nil, err
}
}
if !fastFallback && !isPrimary {
d.networkLastFallback.Store(time.Now())
@@ -300,14 +315,21 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
if fallbackDelay == 0 {
fallbackDelay = d.networkFallbackDelay
}
if !d.networkManager.AutoDetectInterface() {
return nil, E.New("`route.auto_detect_interface` is require by `network_strategy`")
}
network := N.NetworkUDP
if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
network += "4"
}
return trackPacketConn(d.listenSerialInterfacePacket(ctx, d.udpListener, network, "", *strategy, interfaceType, fallbackInterfaceType, fallbackDelay))
packetConn, err := d.listenSerialInterfacePacket(ctx, d.udpListener, network, "", *strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
if err != nil {
// bind interface failed on legacy xiaomi systems
if d.defaultNetworkStrategy && errors.Is(err, syscall.EPERM) {
d.networkStrategy = nil
return d.ListenPacket(ctx, destination)
} else {
return nil, err
}
}
return trackPacketConn(packetConn, nil)
}
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {

View File

@@ -35,7 +35,7 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
conn, err := perNetDialer.DialContext(ctx, network, addr)
if err != nil {
select {
case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Name, ")"), primary: primary}:
case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")"), primary: primary}:
case <-returned:
}
} else {
@@ -107,7 +107,7 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
conn, err := perNetDialer.DialContext(ctx, network, addr)
if err != nil {
select {
case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Name, ")"), primary: primary}:
case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")"), primary: primary}:
case <-returned:
}
} else {
@@ -157,7 +157,7 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
if err == nil {
return conn, nil
}
errors = append(errors, E.Cause(err, "listen ", primaryInterface.Name, " (", primaryInterface.Name, ")"))
errors = append(errors, E.Cause(err, "listen ", primaryInterface.Name, " (", primaryInterface.Index, ")"))
}
for _, fallbackInterface := range fallbackInterfaces {
perNetListener := listener
@@ -166,7 +166,7 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
if err == nil {
return conn, nil
}
errors = append(errors, E.Cause(err, "listen ", fallbackInterface.Name, " (", fallbackInterface.Name, ")"))
errors = append(errors, E.Cause(err, "listen ", fallbackInterface.Name, " (", fallbackInterface.Index, ")"))
}
return nil, E.Errors(errors...)
}
@@ -177,10 +177,14 @@ func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkS
case C.NetworkStrategyDefault:
if len(interfaceType) == 0 {
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
for _, iif := range interfaces {
if iif.Index == defaultIf.Index {
primaryInterfaces = append(primaryInterfaces, iif)
if defaultIf != nil {
for _, iif := range interfaces {
if iif.Index == defaultIf.Index {
primaryInterfaces = append(primaryInterfaces, iif)
}
}
} else {
primaryInterfaces = interfaces
}
} else {
primaryInterfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
@@ -198,11 +202,15 @@ func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkS
case C.NetworkStrategyFallback:
if len(interfaceType) == 0 {
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
for _, iif := range interfaces {
if iif.Index == defaultIf.Index {
primaryInterfaces = append(primaryInterfaces, iif)
break
if defaultIf != nil {
for _, iif := range interfaces {
if iif.Index == defaultIf.Index {
primaryInterfaces = append(primaryInterfaces, iif)
break
}
}
} else {
primaryInterfaces = interfaces
}
} else {
primaryInterfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {

View File

@@ -1,12 +0,0 @@
//go:build !(windows || linux || darwin)
package settings
import (
"os"
"time"
)
func SetSystemTime(nowTime time.Time) error {
return os.ErrInvalid
}

View File

@@ -1,14 +0,0 @@
//go:build linux || darwin
package settings
import (
"time"
"golang.org/x/sys/unix"
)
func SetSystemTime(nowTime time.Time) error {
timeVal := unix.NsecToTimeval(nowTime.UnixNano())
return unix.Settimeofday(&timeVal)
}

View File

@@ -1,32 +0,0 @@
package settings
import (
"time"
"unsafe"
"golang.org/x/sys/windows"
)
func SetSystemTime(nowTime time.Time) error {
var systemTime windows.Systemtime
systemTime.Year = uint16(nowTime.Year())
systemTime.Month = uint16(nowTime.Month())
systemTime.Day = uint16(nowTime.Day())
systemTime.Hour = uint16(nowTime.Hour())
systemTime.Minute = uint16(nowTime.Minute())
systemTime.Second = uint16(nowTime.Second())
systemTime.Milliseconds = uint16(nowTime.UnixMilli() - nowTime.Unix()*1000)
dllKernel32 := windows.NewLazySystemDLL("kernel32.dll")
proc := dllKernel32.NewProc("SetSystemTime")
_, _, err := proc.Call(
uintptr(unsafe.Pointer(&systemTime)),
)
if err != nil && err.Error() != "The operation completed successfully." {
return err
}
return nil
}

View File

@@ -64,6 +64,7 @@ type echConnWrapper struct {
func (c *echConnWrapper) ConnectionState() tls.ConnectionState {
state := c.Conn.ConnectionState()
//nolint:staticcheck
return tls.ConnectionState{
Version: state.Version,
HandshakeComplete: state.HandshakeComplete,

View File

@@ -147,6 +147,9 @@ func echKeygen(version uint16, serverName string, conf []myECHKeyConfig, suite [
pair.rawConf = b
secBuf, err := sec.MarshalBinary()
if err != nil {
return nil, E.Cause(err, "serialize ECH private key")
}
sk := []byte{}
sk = be.AppendUint16(sk, uint16(len(secBuf)))
sk = append(sk, secBuf...)

View File

@@ -28,7 +28,7 @@ func (c *echClientConfig) DialEarly(ctx context.Context, conn net.PacketConn, ad
}
func (c *echClientConfig) CreateTransport(conn net.PacketConn, quicConnPtr *quic.EarlyConnection, serverAddr M.Socksaddr, quicConfig *quic.Config) http.RoundTripper {
return &http3.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) {

View File

@@ -184,7 +184,7 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
return nil, E.New("reality verification failed")
}
return &utlsConnWrapper{uConn}, nil
return &realityClientConnWrapper{uConn}, nil
}
func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) {
@@ -249,3 +249,36 @@ func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChain
}
return nil
}
type realityClientConnWrapper struct {
*utls.UConn
}
func (c *realityClientConnWrapper) 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 *realityClientConnWrapper) Upstream() any {
return c.UConn
}
// Due to low implementation quality, the reality server intercepted half close and caused memory leaks.
// We fixed it by calling Close() directly.
func (c *realityClientConnWrapper) CloseWrite() error {
return c.Close()
}

View File

@@ -174,6 +174,7 @@ type realityConnWrapper struct {
func (c *realityConnWrapper) ConnectionState() ConnectionState {
state := c.Conn.ConnectionState()
//nolint:staticcheck
return tls.ConnectionState{
Version: state.Version,
HandshakeComplete: state.HandshakeComplete,
@@ -193,3 +194,9 @@ func (c *realityConnWrapper) ConnectionState() ConnectionState {
func (c *realityConnWrapper) Upstream() any {
return c.Conn
}
// Due to low implementation quality, the reality server intercepted half close and caused memory leaks.
// We fixed it by calling Close() directly.
func (c *realityConnWrapper) CloseWrite() error {
return c.Close()
}

View File

@@ -69,6 +69,7 @@ type utlsConnWrapper struct {
func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
state := c.Conn.ConnectionState()
//nolint:staticcheck
return tls.ConnectionState{
Version: state.Version,
HandshakeComplete: state.HandshakeComplete,

View File

@@ -1,8 +0,0 @@
//go:build android && debug
package constant
// TODO: remove after fixed
// https://github.com/golang/go/issues/68760
const FixAndroidStack = true

View File

@@ -1,5 +0,0 @@
//go:build !(android && debug)
package constant
const FixAndroidStack = false

View File

@@ -2,14 +2,56 @@
icon: material/alert-decagram
---
#### 1.11.0-beta.10
#### 1.11.0-rc.1
* Fixes and improvements
### 1.10.4
### 1.10.7
* Fixes and improvements
#### 1.11.0-beta.20
* Hysteria2 `ignore_client_bandwidth` behavior update **1**
* Fixes and improvements
**1**:
When `up_mbps` and `down_mbps` are set, `ignore_client_bandwidth` instead denies clients from using BBR CC.
See [Hysteria2](/configuration/inbound/hysteria2/#ignore_client_bandwidth).
#### 1.11.0-beta.17
* Add port hopping support for Hysteria2 **1**
* Fixes and improvements
**1**:
See [Hysteria2](/configuration/outbound/hysteria2/).
#### 1.11.0-beta.14
* Allow adding route (exclude) address sets to routes **1**
* Fixes and improvements
**1**:
When `auto_redirect` is not enabled, directly add `route[_exclude]_address_set`
to tun routes (equivalent to `route[_exclude]_address`).
Note that it **doesn't work on the Android graphical client** due to
the Android VpnService not being able to handle a large number of routes (DeadSystemException),
but otherwise it works fine on all command line clients and Apple platforms.
See [route_address_set](/configuration/inbound/tun/#route_address_set) and
[route_exclude_address_set](/configuration/inbound/tun/#route_exclude_address_set).
#### 1.11.0-beta.12
* Add `rule-set merge` command
* Fixes and improvements
#### 1.11.0-beta.3
* Add more masquerade options for hysteria2 **1**

View File

@@ -4,7 +4,8 @@ icon: material/alert-decagram
!!! quote "Changes in sing-box 1.11.0"
:material-alert: [masquerade](#masquerade)
:material-alert: [masquerade](#masquerade)
:material-alert: [ignore_client_bandwidth](#ignore_client_bandwidth)
### Structure
@@ -75,9 +76,13 @@ Authentication password
#### ignore_client_bandwidth
Commands the client to use the BBR flow control algorithm instead of Hysteria CC.
*When `up_mbps` and `down_mbps` are not set*:
Conflict with `up_mbps` and `down_mbps`.
Commands clients to use the BBR CC instead of Hysteria CC.
*When `up_mbps` and `down_mbps` are set*:
Deny clients to use the BBR CC.
#### tls

View File

@@ -4,7 +4,8 @@ icon: material/alert-decagram
!!! quote "sing-box 1.11.0 中的更改"
:material-alert: [masquerade](#masquerade)
:material-alert: [masquerade](#masquerade)
:material-alert: [ignore_client_bandwidth](#ignore_client_bandwidth)
### 结构
@@ -72,9 +73,13 @@ Hysteria 用户
#### ignore_client_bandwidth
*当 `up_mbps` 和 `down_mbps` 未设定时*:
命令客户端使用 BBR 拥塞控制算法而不是 Hysteria CC。
`up_mbps``down_mbps` 冲突。
*`up_mbps` 和 `down_mbps` 已设定时*:
禁止客户端使用 BBR 拥塞控制算法。
#### tls

View File

@@ -4,7 +4,9 @@ icon: material/alert-decagram
!!! quote "Changes in sing-box 1.11.0"
:material-delete-alert: [gso](#gso)
:material-delete-alert: [gso](#gso)
:material-alert-decagram: [route_address_set](#stack)
:material-alert-decagram: [route_exclude_address_set](#stack)
!!! quote "Changes in sing-box 1.10.0"
@@ -248,7 +250,7 @@ use [VPNHotspot](https://github.com/Mygod/VPNHotspot).
!!! question "Since sing-box 1.10.0"
Connection input mark used by `route_address_set` and `route_exclude_address_set`.
Connection input mark used by `route[_exclude]_address_set` with `auto_redirect`.
`0x2023` is used by default.
@@ -256,7 +258,7 @@ Connection input mark used by `route_address_set` and `route_exclude_address_set
!!! question "Since sing-box 1.10.0"
Connection output mark used by `route_address_set` and `route_exclude_address_set`.
Connection input mark used by `route[_exclude]_address_set` with `auto_redirect`.
`0x2024` is used by default.
@@ -329,29 +331,55 @@ Exclude custom routes when `auto_route` is enabled.
#### route_address_set
!!! question "Since sing-box 1.10.0"
=== "With `auto_redirect` enabled"
!!! quote ""
!!! question "Since sing-box 1.10.0"
Only supported on Linux with nftables and requires `auto_route` and `auto_redirect` enabled.
!!! quote ""
Only supported on Linux with nftables and requires `auto_route` and `auto_redirect` enabled.
Add the destination IP CIDR rules in the specified rule-sets to the firewall.
Unmatched traffic will bypass the sing-box routes.
Conflict with `route.default_mark` and `[dialOptions].routing_mark`.
Add the destination IP CIDR rules in the specified rule-sets to the firewall.
Unmatched traffic will bypass the sing-box routes.
=== "Without `auto_redirect` enabled"
Conflict with `route.default_mark` and `[dialOptions].routing_mark`.
!!! question "Since sing-box 1.11.0"
Add the destination IP CIDR rules in the specified rule-sets to routes, equivalent to adding to `route_address`.
Unmatched traffic will bypass the sing-box routes.
Note that it **doesn't work on the Android graphical client** due to
the Android VpnService not being able to handle a large number of routes (DeadSystemException),
but otherwise it works fine on all command line clients and Apple platforms.
#### route_exclude_address_set
!!! question "Since sing-box 1.10.0"
=== "With `auto_redirect` enabled"
!!! quote ""
!!! question "Since sing-box 1.10.0"
!!! quote ""
Only supported on Linux with nftables and requires `auto_route` and `auto_redirect` enabled.
Add the destination IP CIDR rules in the specified rule-sets to the firewall.
Matched traffic will bypass the sing-box routes.
Add the destination IP CIDR rules in the specified rule-sets to the firewall.
Matched traffic will bypass the sing-box routes.
Conflict with `route.default_mark` and `[dialOptions].routing_mark`.
Conflict with `route.default_mark` and `[dialOptions].routing_mark`.
=== "Without `auto_redirect` enabled"
!!! question "Since sing-box 1.11.0"
Add the destination IP CIDR rules in the specified rule-sets to routes, equivalent to adding to `route_exclude_address`.
Matched traffic will bypass the sing-box routes.
Note that it **doesn't work on the Android graphical client** due to
the Android VpnService not being able to handle a large number of routes (DeadSystemException),
but otherwise it works fine on all command line clients and Apple platforms.
#### endpoint_independent_nat

View File

@@ -4,7 +4,9 @@ icon: material/alert-decagram
!!! quote "sing-box 1.11.0 中的更改"
:material-delete-alert: [gso](#gso)
:material-delete-alert: [gso](#gso)
:material-alert-decagram: [route_address_set](#stack)
:material-alert-decagram: [route_exclude_address_set](#stack)
!!! quote "sing-box 1.10.0 中的更改"
@@ -329,29 +331,53 @@ tun 接口的 IPv6 前缀。
#### route_address_set
!!! question "自 sing-box 1.10.0 起"
=== "`auto_redirect` 已启用"
!!! quote ""
!!! question "自 sing-box 1.10.0 起"
!!! quote ""
仅支持 Linux且需要 nftables`auto_route``auto_redirect` 已启用。
将指定规则集中的目标 IP CIDR 规则添加到防火墙。
不匹配的流量将绕过 sing-box 路由。
`route.default_mark``[dialOptions].routing_mark` 冲突。
仅支持 Linux且需要 nftables`auto_route``auto_redirect` 启用
=== "`auto_redirect` 启用"
将指定规则集中的目标 IP CIDR 规则添加到防火墙。
不匹配的流量将绕过 sing-box 路由。
!!! question "自 sing-box 1.11.0 起"
`route.default_mark``[dialOptions].routing_mark` 冲突
将指定规则集中的目标 IP CIDR 规则添加到路由,相当于添加到 `route_address`
不匹配的流量将绕过 sing-box 路由。
请注意,由于 Android VpnService 无法处理大量路由DeadSystemException
因此它**在 Android 图形客户端上不起作用**,但除此之外,它在所有命令行客户端和 Apple 平台上都可以正常工作。
#### route_exclude_address_set
!!! question "自 sing-box 1.10.0 起"
=== "`auto_redirect` 已启用"
!!! quote ""
!!! question "自 sing-box 1.10.0 起"
!!! quote ""
仅支持 Linux且需要 nftables`auto_route``auto_redirect` 已启用。
仅支持 Linux且需要 nftables`auto_route``auto_redirect` 已启用
将指定规则集中的目标 IP CIDR 规则添加到防火墙
匹配的流量将绕过 sing-box 路由。
将指定规则集中的目标 IP CIDR 规则添加到防火墙
匹配的流量将绕过 sing-box 路由。
`route.default_mark``[dialOptions].routing_mark` 冲突
`route.default_mark``[dialOptions].routing_mark` 冲突。
=== "`auto_redirect` 未启用"
!!! question "自 sing-box 1.11.0 起"
将指定规则集中的目标 IP CIDR 规则添加到路由,相当于添加到 `route_exclude_address`
匹配的流量将绕过 sing-box 路由。
请注意,由于 Android VpnService 无法处理大量路由DeadSystemException
因此它**在 Android 图形客户端上不起作用**,但除此之外,它在所有命令行客户端和 Apple 平台上都可以正常工作。
#### endpoint_independent_nat

View File

@@ -1,3 +1,12 @@
---
icon: material/new-box
---
!!! quote "Changes in sing-box 1.11.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_mbps": 100,
"down_mbps": 100,
"obfs": {
@@ -22,6 +35,10 @@
}
```
!!! note ""
You can ignore the JSON Array [] tag when the content is only one item
!!! warning "Difference from official Hysteria2"
The official Hysteria2 supports an authentication method called **userpass**,
@@ -44,6 +61,24 @@ The server address.
The server port.
Ignored if `server_ports` is set.
#### server_ports
!!! question "Since sing-box 1.11.0"
Server port range list.
Conflicts with `server_port`.
#### hop_interval
!!! question "Since sing-box 1.11.0"
Port hopping interval.
`30s` is used by default.
#### up_mbps, down_mbps
Max bandwidth, in Mbps.

View File

@@ -1,3 +1,12 @@
---
icon: material/new-box
---
!!! quote "sing-box 1.11.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_mbps": 100,
"down_mbps": 100,
"obfs": {
@@ -22,6 +35,10 @@
}
```
!!! note ""
当内容只有一项时,可以忽略 JSON 数组 [] 标签
!!! warning "与官方 Hysteria2 的区别"
官方程序支持一种名为 **userpass** 的验证方式,
@@ -42,6 +59,24 @@
服务器端口。
如果设置了 `server_ports`,则忽略此项。
#### server_ports
!!! question "自 sing-box 1.11.0 起"
服务器端口范围列表。
`server_port` 冲突。
#### hop_interval
!!! question "自 sing-box 1.11.0 起"
端口跳跃间隔。
默认使用 `30s`
#### up_mbps, down_mbps
最大带宽。

View File

@@ -4,7 +4,7 @@ icon: material/delete-clock
!!! failure "已在 sing-box 1.11.0 废弃"
WireGuard 出站已被用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-wireguard-outbound-to-endpoint)。
WireGuard 出站已被用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-wireguard-outbound-to-endpoint)。
!!! quote "sing-box 1.11.0 中的更改"

View File

@@ -31,6 +31,45 @@ Tag of target outbound.
See `route-options` fields below.
### reject
```json
{
"action": "reject",
"method": "default", // default
"no_drop": false
}
```
`reject` reject connections
The specified method is used for reject tun connections if `sniff` action has not been performed yet.
For non-tun connections and already established connections, will just be closed.
#### method
- `default`: Reply with TCP RST for TCP connections, and ICMP port unreachable for UDP packets.
- `drop`: Drop packets.
#### 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.
### hijack-dns
```json
{
"action": "hijack-dns"
}
```
`hijack-dns` hijack DNS requests to the sing-box DNS module.
## Non-final actions
### route-options
```json
@@ -109,45 +148,6 @@ If no protocol is sniffed, the following ports will be recognized as protocols b
| 443 | `quic` |
| 3478 | `stun` |
### reject
```json
{
"action": "reject",
"method": "default", // default
"no_drop": false
}
```
`reject` reject connections
The specified method is used for reject tun connections if `sniff` action has not been performed yet.
For non-tun connections and already established connections, will just be closed.
#### method
- `default`: Reply with TCP RST for TCP connections, and ICMP port unreachable for UDP packets.
- `drop`: Drop packets.
#### 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.
### hijack-dns
```json
{
"action": "hijack-dns"
}
```
`hijack-dns` hijack DNS requests to the sing-box DNS module.
## Non-final actions
### sniff
```json

View File

@@ -27,6 +27,45 @@ icon: material/new-box
参阅下方的 `route-options` 字段。
### reject
```json
{
"action": "reject",
"method": "default", // 默认
"no_drop": false
}
```
`reject` 拒绝连接。
如果尚未执行 `sniff` 操作,则将使用指定方法拒绝 tun 连接。
对于非 tun 连接和已建立的连接,将直接关闭。
#### method
- `default`: 对于 TCP 连接回复 RST对于 UDP 包回复 ICMP 端口不可达。
- `drop`: 丢弃数据包。
#### no_drop
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`
`method` 设为 `drop` 时不可用。
### hijack-dns
```json
{
"action": "hijack-dns"
}
```
`hijack-dns` 劫持 DNS 请求至 sing-box DNS 模块。
## 非最终动作
### route-options
```json
@@ -107,45 +146,6 @@ UDP 连接超时时间。
| 443 | `quic` |
| 3478 | `stun` |
### reject
```json
{
"action": "reject",
"method": "default", // 默认
"no_drop": false
}
```
`reject` 拒绝连接。
如果尚未执行 `sniff` 操作,则将使用指定方法拒绝 tun 连接。
对于非 tun 连接和已建立的连接,将直接关闭。
#### method
- `default`: 对于 TCP 连接回复 RST对于 UDP 包回复 ICMP 端口不可达。
- `drop`: 丢弃数据包。
#### no_drop
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`
`method` 设为 `drop` 时不可用。
### hijack-dns
```json
{
"action": "hijack-dns"
}
```
`hijack-dns` 劫持 DNS 请求至 sing-box DNS 模块。
## 非最终动作
### sniff
```json

View File

@@ -7,7 +7,6 @@ import (
"path/filepath"
"time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
)
@@ -114,7 +113,7 @@ func (c *CommandClient) Connect() error {
if err != nil {
return err
}
if C.FixAndroidStack {
if sFixAndroidStack {
go func() {
c.handler.Connected()
c.handler.InitializeClashMode(newIterator(modeList), currentMode)

View File

@@ -66,6 +66,10 @@ func (s *platformInterfaceStub) OpenTun(options *tun.Options, platformOptions op
return nil, os.ErrInvalid
}
func (s *platformInterfaceStub) UpdateRouteOptions(options *tun.Options, platformInterface option.TunPlatformOptions) error {
return os.ErrInvalid
}
func (s *platformInterfaceStub) UsePlatformDefaultInterfaceMonitor() bool {
return true
}

View File

@@ -1,7 +1,6 @@
package libbox
import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
@@ -56,7 +55,7 @@ func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Eleme
}
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
if C.FixAndroidStack {
if sFixAndroidStack {
go m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
} else {
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)

View File

@@ -9,6 +9,7 @@ type PlatformInterface interface {
UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl(fd int32) error
OpenTun(options TunOptions) (int32, error)
UpdateRouteOptions(options TunOptions) error
WriteLog(message string)
UseProcFS() bool
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)

View File

@@ -13,6 +13,7 @@ type Interface interface {
UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl(fd int) error
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
UpdateRouteOptions(options *tun.Options, platformOptions option.TunPlatformOptions) error
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
Interfaces() ([]adapter.NetworkInterface, error)
UnderNetworkExtension() bool

View File

@@ -81,7 +81,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
}
func (s *BoxService) Start() error {
if C.FixAndroidStack {
if sFixAndroidStack {
var err error
done := make(chan struct{})
go func() {
@@ -148,10 +148,10 @@ func (w *platformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error {
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
return nil, E.New("android: unsupported uid options")
return nil, E.New("platform: unsupported uid options")
}
if len(options.IncludeAndroidUser) > 0 {
return nil, E.New("android: unsupported android_user option")
return nil, E.New("platform: unsupported android_user option")
}
routeRanges, err := options.BuildAutoRouteRanges(true)
if err != nil {
@@ -174,6 +174,20 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
return tun.New(*options)
}
func (w *platformInterfaceWrapper) UpdateRouteOptions(options *tun.Options, platformOptions option.TunPlatformOptions) error {
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
return E.New("android: unsupported uid options")
}
if len(options.IncludeAndroidUser) > 0 {
return E.New("android: unsupported android_user option")
}
routeRanges, err := options.BuildAutoRouteRanges(true)
if err != nil {
return err
}
return w.iif.UpdateRouteOptions(&tunOptions{options, routeRanges, platformOptions})
}
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
return &platformDefaultInterfaceMonitor{
platformInterfaceWrapper: w,

View File

@@ -14,43 +14,55 @@ import (
)
var (
sBasePath string
sWorkingPath string
sTempPath string
sUserID int
sGroupID int
sTVOS bool
sBasePath string
sWorkingPath string
sTempPath string
sUserID int
sGroupID int
sTVOS bool
sFixAndroidStack bool
)
func init() {
debug.SetPanicOnFault(true)
}
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
sBasePath = basePath
sWorkingPath = workingPath
sTempPath = tempPath
sUserID = os.Getuid()
sGroupID = os.Getgid()
sTVOS = isTVOS
os.MkdirAll(sWorkingPath, 0o777)
os.MkdirAll(sTempPath, 0o777)
type SetupOptions struct {
BasePath string
WorkingPath string
TempPath string
Username string
IsTVOS bool
FixAndroidStack bool
}
func SetupWithUsername(basePath string, workingPath string, tempPath string, username string) error {
sBasePath = basePath
sWorkingPath = workingPath
sTempPath = tempPath
sUser, err := user.Lookup(username)
if err != nil {
return err
func Setup(options *SetupOptions) error {
sBasePath = options.BasePath
sWorkingPath = options.WorkingPath
sTempPath = options.TempPath
if options.Username != "" {
sUser, err := user.Lookup(options.Username)
if err != nil {
return err
}
sUserID, _ = strconv.Atoi(sUser.Uid)
sGroupID, _ = strconv.Atoi(sUser.Gid)
} else {
sUserID = os.Getuid()
sGroupID = os.Getgid()
}
sUserID, _ = strconv.Atoi(sUser.Uid)
sGroupID, _ = strconv.Atoi(sUser.Gid)
sTVOS = options.IsTVOS
// TODO: remove after fixed
// https://github.com/golang/go/issues/68760
sFixAndroidStack = options.FixAndroidStack
os.MkdirAll(sWorkingPath, 0o777)
os.MkdirAll(sTempPath, 0o777)
os.Chown(sWorkingPath, sUserID, sGroupID)
os.Chown(sTempPath, sUserID, sGroupID)
if options.Username != "" {
os.Chown(sWorkingPath, sUserID, sGroupID)
os.Chown(sTempPath, sUserID, sGroupID)
}
return nil
}

24
go.mod
View File

@@ -4,13 +4,11 @@ go 1.20
require (
github.com/caddyserver/certmagic v0.20.0
github.com/cidertool/asc-go v0.5.1
github.com/cloudflare/circl v1.3.7
github.com/cretz/bine v0.2.0
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/render v1.0.3
github.com/gofrs/uuid/v5 v5.3.0
github.com/google/go-querystring v1.0.0
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
github.com/libdns/alidns v1.0.3
github.com/libdns/cloudflare v0.1.1
@@ -19,6 +17,7 @@ require (
github.com/mholt/acmez v1.2.0
github.com/miekg/dns v1.1.62
github.com/oschwald/maxminddb-golang v1.12.0
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
@@ -27,15 +26,15 @@ require (
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
github.com/sagernet/quic-go v0.48.2-beta.1
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.6.0-beta.8
github.com/sagernet/sing-dns v0.4.0-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-mux v0.3.0-alpha.1
github.com/sagernet/sing-quic v0.4.0-alpha.4
github.com/sagernet/sing-quic v0.4.0-beta.4
github.com/sagernet/sing-shadowsocks v0.2.7
github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2
github.com/sagernet/sing-tun v0.6.0-beta.6
github.com/sagernet/sing-vmess v0.2.0-beta.1
github.com/sagernet/sing-tun v0.6.0-beta.8
github.com/sagernet/sing-vmess v0.2.0-beta.2
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
@@ -44,11 +43,11 @@ require (
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.29.0
golang.org/x/crypto v0.31.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/mod v0.20.0
golang.org/x/net v0.31.0
golang.org/x/sys v0.27.0
golang.org/x/sys v0.28.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
@@ -60,7 +59,7 @@ require (
require (
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
@@ -70,6 +69,7 @@ require (
github.com/gobwas/pool v0.2.1 // 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/hashicorp/yamux v0.1.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -92,8 +92,8 @@ require (
github.com/vishvananda/netns v0.0.4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/sync v0.10.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

51
go.sum
View File

@@ -4,10 +4,8 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx
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/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cidertool/asc-go v0.5.1 h1:KYki2Y8IXJMOkOXy9y1sdr8tz6IdW2ti770K4bk7WY0=
github.com/cidertool/asc-go v0.5.1/go.mod h1:LyrZWU7DeCh8cWrFwXcpl93ixRUUL2aEZV7/0h07FxA=
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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -38,10 +36,11 @@ github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
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=
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.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
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/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
@@ -97,6 +96,8 @@ github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1
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/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=
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=
@@ -118,24 +119,24 @@ github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/
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.8 h1:PoxDdN7y8D4oImT3cQ05Sq1ZYnYsJberkUkIEHIGwWE=
github.com/sagernet/sing v0.6.0-beta.8/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY=
github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
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-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ=
github.com/sagernet/sing-quic v0.4.0-alpha.4/go.mod h1:h5RkKTmUhudJKzK7c87FPXD5w1bJjVyxMN9+opZcctA=
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-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.6 h1:xaIHoH78MqTSvZqQ4SQto8pC1A+X4qXReDRNaC8DQeI=
github.com/sagernet/sing-tun v0.6.0-beta.6/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-vmess v0.2.0-beta.1 h1:5sXQ23uwNlZuDvygzi0dFtnG0Csm/SNqTjAHXJkpuj4=
github.com/sagernet/sing-vmess v0.2.0-beta.1/go.mod h1:fLyE1emIcvQ5DV8reFWnufquZ7MkCSYM5ThodsR9NrQ=
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/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=
@@ -171,8 +172,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
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.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
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/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/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
@@ -181,8 +182,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
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/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-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=
@@ -190,14 +191,14 @@ 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.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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
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/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=

View File

@@ -74,6 +74,7 @@ func (m *Hysteria2Masquerade) UnmarshalJSON(bytes []byte) error {
default:
return E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
}
return nil
}
err = json.Unmarshal(bytes, (*_Hysteria2Masquerade)(m))
if err != nil {
@@ -111,11 +112,13 @@ type Hysteria2MasqueradeString struct {
type Hysteria2OutboundOptions struct {
DialerOptions
ServerOptions
UpMbps int `json:"up_mbps,omitempty"`
DownMbps int `json:"down_mbps,omitempty"`
Obfs *Hysteria2Obfs `json:"obfs,omitempty"`
Password string `json:"password,omitempty"`
Network NetworkList `json:"network,omitempty"`
ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"`
HopInterval badoption.Duration `json:"hop_interval,omitempty"`
UpMbps int `json:"up_mbps,omitempty"`
DownMbps int `json:"down_mbps,omitempty"`
Obfs *Hysteria2Obfs `json:"obfs,omitempty"`
Password string `json:"password,omitempty"`
Network NetworkList `json:"network,omitempty"`
OutboundTLSOptionsContainer
BrutalDebug bool `json:"brutal_debug,omitempty"`
}

View File

@@ -194,8 +194,9 @@ func (r LogicalHeadlessRule) IsValid() bool {
}
type _PlainRuleSetCompat struct {
Version uint8 `json:"version"`
Options PlainRuleSet `json:"-"`
Version uint8 `json:"version"`
Options PlainRuleSet `json:"-"`
RawMessage json.RawMessage `json:"-"`
}
type PlainRuleSetCompat _PlainRuleSetCompat
@@ -229,6 +230,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
if err != nil {
return err
}
r.RawMessage = bytes
return nil
}

View File

@@ -80,34 +80,22 @@ func (i *Inbound) Close() error {
}
func (i *Inbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
var destination M.Socksaddr
switch i.overrideOption {
case 1:
destination = i.overrideDestination
case 2:
destination = i.overrideDestination
destination.Port = i.listener.UDPAddr().Port
case 3:
destination = source
destination.Port = i.overrideDestination.Port
}
i.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, destination, nil)
i.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, M.Socksaddr{}, nil)
}
func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
metadata.Inbound = i.Tag()
metadata.InboundType = i.Type()
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
destination := metadata.OriginDestination
switch i.overrideOption {
case 1:
metadata.Destination = i.overrideDestination
destination = i.overrideDestination
case 2:
destination := i.overrideDestination
destination.Port = metadata.Destination.Port
metadata.Destination = destination
destination.Addr = i.overrideDestination.Addr
case 3:
metadata.Destination.Port = i.overrideDestination.Port
destination.Port = metadata.Destination.Port
}
metadata.Destination = destination
if i.overrideOption != 0 {
i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
}
@@ -125,6 +113,16 @@ func (i *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
//nolint:staticcheck
metadata.InboundOptions = i.listener.ListenOptions().InboundOptions
metadata.Source = source
destination = i.listener.UDPAddr()
switch i.overrideOption {
case 1:
destination = i.overrideDestination
case 2:
destination.Addr = i.overrideDestination.Addr
case 3:
destination.Port = i.overrideDestination.Port
default:
}
metadata.Destination = destination
metadata.OriginDestination = i.listener.UDPAddr()
i.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)

View File

@@ -67,7 +67,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
if len(options.Down) > 0 {
receiveBps, err = humanize.ParseBytes(options.Down)
if err != nil {
return nil, E.New("invalid down speed format: ", options.Down)
return nil, E.Cause(err, "invalid down speed format: ", options.Down)
}
} else {
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps

View File

@@ -4,6 +4,7 @@ import (
"context"
"net"
"os"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/outbound"
@@ -70,6 +71,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
Logger: logger,
BrutalDebug: options.BrutalDebug,
ServerAddress: options.ServerOptions.Build(),
ServerPorts: options.ServerPorts,
HopInterval: time.Duration(options.HopInterval),
SendBPS: uint64(options.UpMbps * hysteria.MbpsToBps),
ReceiveBPS: uint64(options.DownMbps * hysteria.MbpsToBps),
SalamanderPassword: salamanderPassword,

View File

@@ -209,6 +209,22 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
platformInterface: service.FromContext[platform.Interface](ctx),
platformOptions: common.PtrValueOrDefault(options.Platform),
}
for _, routeAddressSet := range options.RouteAddressSet {
ruleSet, loaded := router.RuleSet(routeAddressSet)
if !loaded {
return nil, E.New("parse route_address_set: rule-set not found: ", routeAddressSet)
}
ruleSet.IncRef()
inbound.routeRuleSet = append(inbound.routeRuleSet, ruleSet)
}
for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet {
ruleSet, loaded := router.RuleSet(routeExcludeAddressSet)
if !loaded {
return nil, E.New("parse route_exclude_address_set: rule-set not found: ", routeExcludeAddressSet)
}
ruleSet.IncRef()
inbound.routeExcludeRuleSet = append(inbound.routeExcludeRuleSet, ruleSet)
}
if options.AutoRedirect {
if !options.AutoRoute {
return nil, E.New("`auto_route` is required by `auto_redirect`")
@@ -229,32 +245,11 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
if err != nil {
return nil, E.Cause(err, "initialize auto-redirect")
}
if runtime.GOOS != "android" {
var markMode bool
for _, routeAddressSet := range options.RouteAddressSet {
ruleSet, loaded := router.RuleSet(routeAddressSet)
if !loaded {
return nil, E.New("parse route_address_set: rule-set not found: ", routeAddressSet)
}
ruleSet.IncRef()
inbound.routeRuleSet = append(inbound.routeRuleSet, ruleSet)
markMode = true
}
for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet {
ruleSet, loaded := router.RuleSet(routeExcludeAddressSet)
if !loaded {
return nil, E.New("parse route_exclude_address_set: rule-set not found: ", routeExcludeAddressSet)
}
ruleSet.IncRef()
inbound.routeExcludeRuleSet = append(inbound.routeExcludeRuleSet, ruleSet)
markMode = true
}
if markMode {
inbound.tunOptions.AutoRedirectMarkMode = true
err = networkManager.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark)
if err != nil {
return nil, err
}
if !C.IsAndroid && (len(inbound.routeRuleSet) > 0 || len(inbound.routeExcludeRuleSet) > 0) {
inbound.tunOptions.AutoRedirectMarkMode = true
err = networkManager.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark)
if err != nil {
return nil, err
}
}
}
@@ -310,18 +305,62 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
if t.tunOptions.Name == "" {
t.tunOptions.Name = tun.CalculateInterfaceName("")
}
if t.platformInterface == nil || runtime.GOOS != "android" {
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
for _, routeRuleSet := range t.routeRuleSet {
ipSets := routeRuleSet.ExtractIPSet()
if len(ipSets) == 0 {
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeRuleSet.Name())
}
t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet))
routeRuleSet.DecRef()
t.routeAddressSet = append(t.routeAddressSet, ipSets...)
}
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
ipSets := routeExcludeRuleSet.ExtractIPSet()
if len(ipSets) == 0 {
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeExcludeRuleSet.Name())
}
t.routeExcludeRuleSetCallback = append(t.routeExcludeRuleSetCallback, routeExcludeRuleSet.RegisterCallback(t.updateRouteAddressSet))
routeExcludeRuleSet.DecRef()
t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ipSets...)
}
}
var (
tunInterface tun.Tun
err error
)
monitor := taskmonitor.New(t.logger, C.StartTimeout)
monitor.Start("open tun interface")
tunOptions := t.tunOptions
if t.autoRedirect == nil && !(runtime.GOOS == "android" && t.platformInterface != nil) {
for _, ipSet := range t.routeAddressSet {
for _, prefix := range ipSet.Prefixes() {
if prefix.Addr().Is4() {
tunOptions.Inet4RouteAddress = append(tunOptions.Inet4RouteAddress, prefix)
} else {
tunOptions.Inet6RouteAddress = append(tunOptions.Inet6RouteAddress, prefix)
}
}
}
for _, ipSet := range t.routeExcludeAddressSet {
for _, prefix := range ipSet.Prefixes() {
if prefix.Addr().Is4() {
tunOptions.Inet4RouteExcludeAddress = append(tunOptions.Inet4RouteExcludeAddress, prefix)
} else {
tunOptions.Inet6RouteExcludeAddress = append(tunOptions.Inet6RouteExcludeAddress, prefix)
}
}
}
}
monitor.Start("open interface")
if t.platformInterface != nil {
tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions)
tunInterface, err = t.platformInterface.OpenTun(&tunOptions, t.platformOptions)
} else {
tunInterface, err = tun.New(t.tunOptions)
tunInterface, err = tun.New(tunOptions)
}
monitor.Finish()
t.tunOptions.Name = tunOptions.Name
if err != nil {
return E.Cause(err, "configure tun interface")
}
@@ -366,39 +405,15 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
return E.Cause(err, "starting TUN interface")
}
if t.autoRedirect != nil {
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
for _, routeRuleSet := range t.routeRuleSet {
ipSets := routeRuleSet.ExtractIPSet()
if len(ipSets) == 0 {
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeRuleSet.Name())
}
t.routeAddressSet = append(t.routeAddressSet, ipSets...)
}
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
ipSets := routeExcludeRuleSet.ExtractIPSet()
if len(ipSets) == 0 {
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeExcludeRuleSet.Name())
}
t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ipSets...)
}
monitor.Start("initialize auto-redirect")
err := t.autoRedirect.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "auto-redirect")
}
for _, routeRuleSet := range t.routeRuleSet {
t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet))
routeRuleSet.DecRef()
}
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
t.routeExcludeRuleSetCallback = append(t.routeExcludeRuleSetCallback, routeExcludeRuleSet.RegisterCallback(t.updateRouteAddressSet))
routeExcludeRuleSet.DecRef()
}
t.routeAddressSet = nil
t.routeExcludeAddressSet = nil
}
t.routeAddressSet = nil
t.routeExcludeAddressSet = nil
}
return nil
}
@@ -406,7 +421,41 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
func (t *Inbound) updateRouteAddressSet(it adapter.RuleSet) {
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
t.autoRedirect.UpdateRouteAddressSet()
if t.autoRedirect != nil {
t.autoRedirect.UpdateRouteAddressSet()
} else {
tunOptions := t.tunOptions
for _, ipSet := range t.routeAddressSet {
for _, prefix := range ipSet.Prefixes() {
if prefix.Addr().Is4() {
tunOptions.Inet4RouteAddress = append(tunOptions.Inet4RouteAddress, prefix)
} else {
tunOptions.Inet6RouteAddress = append(tunOptions.Inet6RouteAddress, prefix)
}
}
}
for _, ipSet := range t.routeExcludeAddressSet {
for _, prefix := range ipSet.Prefixes() {
if prefix.Addr().Is4() {
tunOptions.Inet4RouteExcludeAddress = append(tunOptions.Inet4RouteExcludeAddress, prefix)
} else {
tunOptions.Inet6RouteExcludeAddress = append(tunOptions.Inet6RouteExcludeAddress, prefix)
}
}
}
if t.platformInterface != nil {
err := t.platformInterface.UpdateRouteOptions(&tunOptions, t.platformOptions)
if err != nil {
t.logger.Error("update route addresses: ", err)
}
} else {
err := t.tunIf.UpdateRouteOptions(tunOptions)
if err != nil {
t.logger.Error("update route addresses: ", err)
}
}
t.logger.Info("updated route addresses")
}
t.routeAddressSet = nil
t.routeExcludeAddressSet = nil
}

View File

@@ -1179,6 +1179,36 @@ _sing-box_rule-set_match()
noun_aliases=()
}
_sing-box_rule-set_merge()
{
last_command="sing-box_rule-set_merge"
command_aliases=()
commands=()
flags=()
two_word_flags=()
local_nonpersistent_flags=()
flags_with_completion=()
flags_completion=()
flags+=("--config=")
two_word_flags+=("--config")
two_word_flags+=("-c")
flags+=("--config-directory=")
two_word_flags+=("--config-directory")
two_word_flags+=("-C")
flags+=("--directory=")
two_word_flags+=("--directory")
two_word_flags+=("-D")
flags+=("--disable-color")
must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}
_sing-box_rule-set_upgrade()
{
last_command="sing-box_rule-set_upgrade"
@@ -1225,6 +1255,7 @@ _sing-box_rule-set()
commands+=("decompile")
commands+=("format")
commands+=("match")
commands+=("merge")
commands+=("upgrade")
flags=()

View File

@@ -97,12 +97,19 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
err error
)
if metadata.UDPConnect {
if len(metadata.DestinationAddresses) > 0 || metadata.Destination.IsIP() {
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer)
if len(metadata.DestinationAddresses) > 0 {
if isParallelDialer {
remoteConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
} else {
remoteConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses)
}
} else if metadata.Destination.IsIP() {
if isParallelDialer {
remoteConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
} else {
remoteConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination)
}
} else {
remoteConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination)
}

View File

@@ -90,9 +90,6 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
return nil, E.Cause(err, "create network monitor")
}
nm.networkMonitor = networkMonitor
networkMonitor.RegisterCallback(func() {
_ = nm.interfaceFinder.Update()
})
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(nm.networkMonitor, logger, tun.DefaultInterfaceMonitorOptions{
InterfaceFinder: nm.interfaceFinder,
OverrideAndroidVPN: routeOptions.OverrideAndroidVPN,

View File

@@ -33,7 +33,18 @@ import (
// Deprecated: use RouteConnectionEx instead.
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return r.routeConnection(ctx, conn, metadata, nil)
done := make(chan interface{})
err := r.routeConnection(ctx, conn, metadata, N.OnceClose(func(it error) {
close(done)
}))
if err != nil {
return err
}
select {
case <-done:
case <-r.ctx.Done():
}
return nil
}
func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
@@ -141,7 +152,10 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
}
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
err := r.routePacketConnection(ctx, conn, metadata, nil)
done := make(chan interface{})
err := r.routePacketConnection(ctx, conn, metadata, N.OnceClose(func(it error) {
close(done)
}))
if err != nil {
conn.Close()
if E.IsClosedOrCanceled(err) {
@@ -150,6 +164,10 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
r.logger.ErrorContext(ctx, err)
}
}
select {
case <-done:
case <-r.ctx.Done():
}
return nil
}

View File

@@ -45,28 +45,22 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
panic("no context")
}
var options dns.QueryOptions
var (
currentRuleIndex int
currentRule adapter.DNSRule
)
var currentRuleIndex int
if ruleIndex != -1 {
currentRuleIndex = ruleIndex + 1
}
for currentRuleIndex, currentRule = range r.dnsRules[currentRuleIndex:] {
for ; currentRuleIndex < len(r.dnsRules); currentRuleIndex++ {
currentRule := r.dnsRules[currentRuleIndex]
if currentRule.WithAddressLimit() && !isAddressQuery {
continue
}
metadata.ResetRuleCache()
if currentRule.Match(metadata) {
displayRuleIndex := currentRuleIndex
if ruleIndex != -1 {
displayRuleIndex += ruleIndex + 1
}
ruleDescription := currentRule.String()
if ruleDescription != "" {
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] ", currentRule, " => ", currentRule.Action())
r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action())
} else {
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] => ", currentRule.Action())
}
switch action := currentRule.Action().(type) {
case *R.RuleActionDNSRoute:
@@ -93,7 +87,7 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
} else {
options.Strategy = r.defaultDomainStrategy
}
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] => ", currentRule.Action())
return transport, options, currentRule, currentRuleIndex
case *R.RuleActionDNSRouteOptions:
if action.DisableCache {
@@ -105,9 +99,9 @@ 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())
r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] => ", currentRule.Action())
case *R.RuleActionReject:
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] => ", currentRule.Action())
return nil, options, currentRule, currentRuleIndex
}
}
@@ -133,7 +127,6 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
}
return &responseMessage, nil
}
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
var (
response *mDNS.Msg
cached bool
@@ -158,6 +151,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
rule adapter.DNSRule
ruleIndex int
)
ruleIndex = -1
for {
dnsCtx := adapter.OverrideContext(ctx)
var addressLimit bool
@@ -173,14 +167,11 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
}
}
}
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(response *mDNS.Msg) bool {
addresses, addrErr := dns.MessageToAddresses(response)
if addrErr != nil {
return false
}
metadata.DestinationAddresses = addresses
response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, options, func(responseAddrs []netip.Addr) bool {
metadata.DestinationAddresses = responseAddrs
return rule.MatchAddressLimit(metadata)
})
} else {

View File

@@ -363,7 +363,6 @@ func (r *Router) Start(stage adapter.StartStage) error {
return E.Cause(err, "initialize DNS server[", i, "]")
}
}
case adapter.StartStatePostStart:
var cacheContext *adapter.HTTPStartContext
if len(r.ruleSets) > 0 {
monitor.Start("initialize rule-set")
@@ -419,6 +418,7 @@ func (r *Router) Start(stage adapter.StartStage) error {
}
}
}
case adapter.StartStatePostStart:
for i, rule := range r.rules {
monitor.Start("initialize rule[", i, "]")
err := rule.Start()

View File

@@ -59,7 +59,7 @@ func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultH
}
func isProcessHeadlessRule(rule option.DefaultHeadlessRule) bool {
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.ProcessPathRegex) > 0 || len(rule.PackageName) > 0
}
func isWIFIHeadlessRule(rule option.DefaultHeadlessRule) bool {

View File

@@ -55,15 +55,11 @@ func isGeositeDNSRule(rule option.DefaultDNSRule) bool {
}
func isProcessRule(rule option.DefaultRule) bool {
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0
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 isProcessDNSRule(rule option.DefaultDNSRule) bool {
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0
}
func isProcessHeadlessRule(rule option.DefaultHeadlessRule) bool {
return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0
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 {

View File

@@ -13,9 +13,9 @@ require (
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.5
github.com/sagernet/sing-dns v0.4.0-beta.1
github.com/sagernet/sing-quic v0.4.0-alpha.4
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/sagernet/sing-shadowsocks v0.2.7
github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/spyzhov/ajson v0.9.4
@@ -85,8 +85,8 @@ require (
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.2 // indirect
github.com/sagernet/sing-vmess v0.2.0-beta.1 // 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/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
github.com/sagernet/utls v1.6.7 // indirect
github.com/sagernet/wireguard-go v0.0.1-beta.5 // indirect
@@ -103,12 +103,12 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/crypto v0.31.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.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text 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/time v0.7.0 // indirect
golang.org/x/tools v0.24.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect

View File

@@ -146,24 +146,24 @@ github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/
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.5 h1:RD2j8WmJsvAbbBkAlJWaiYmnd+v/JohBiweoew7kMwo=
github.com/sagernet/sing v0.6.0-beta.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY=
github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
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-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ=
github.com/sagernet/sing-quic v0.4.0-alpha.4/go.mod h1:h5RkKTmUhudJKzK7c87FPXD5w1bJjVyxMN9+opZcctA=
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-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.2 h1:GK7r2jWKm7RhlJGTq4QadgFcebQia1c3BO3OlYMcQJ0=
github.com/sagernet/sing-tun v0.6.0-beta.2/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-vmess v0.2.0-beta.1 h1:5sXQ23uwNlZuDvygzi0dFtnG0Csm/SNqTjAHXJkpuj4=
github.com/sagernet/sing-vmess v0.2.0-beta.1/go.mod h1:fLyE1emIcvQ5DV8reFWnufquZ7MkCSYM5ThodsR9NrQ=
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/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=
@@ -221,8 +221,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
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.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
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/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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -240,8 +240,8 @@ golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
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.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -252,16 +252,16 @@ 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.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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
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/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=

View File

@@ -3,24 +3,36 @@ package main
import (
"net/netip"
"testing"
"time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-quic/hysteria2"
"github.com/sagernet/sing/common"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json/badoption"
)
func TestHysteria2Self(t *testing.T) {
t.Run("self", func(t *testing.T) {
testHysteria2Self(t, "")
testHysteria2Self(t, "", false)
})
t.Run("self-salamander", func(t *testing.T) {
testHysteria2Self(t, "password")
testHysteria2Self(t, "password", false)
})
t.Run("self-hop", func(t *testing.T) {
testHysteria2Self(t, "", true)
})
t.Run("self-hop-salamander", func(t *testing.T) {
testHysteria2Self(t, "password", true)
})
}
func testHysteria2Self(t *testing.T, salamanderPassword string) {
func TestHysteria2Hop(t *testing.T) {
testHysteria2Self(t, "password", true)
}
func testHysteria2Self(t *testing.T, salamanderPassword string, portHop bool) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
var obfs *option.Hysteria2Obfs
if salamanderPassword != "" {
@@ -29,6 +41,14 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) {
Password: salamanderPassword,
}
}
var (
serverPorts []string
hopInterval time.Duration
)
if portHop {
serverPorts = []string{F.ToString(serverPort, ":", serverPort)}
hopInterval = 5 * time.Second
}
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
@@ -77,10 +97,12 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) {
Server: "127.0.0.1",
ServerPort: serverPort,
},
UpMbps: 100,
DownMbps: 100,
Obfs: obfs,
Password: "password",
ServerPorts: serverPorts,
HopInterval: badoption.Duration(hopInterval),
UpMbps: 100,
DownMbps: 100,
Obfs: obfs,
Password: "password",
OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{
TLS: &option.OutboundTLSOptions{
Enabled: true,
@@ -112,6 +134,10 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) {
},
})
testSuitLargeUDP(t, clientPort, testPort)
if portHop {
time.Sleep(5 * time.Second)
testSuitLargeUDP(t, clientPort, testPort)
}
}
func TestHysteria2Inbound(t *testing.T) {

View File

@@ -1,53 +0,0 @@
package main
import (
"net/netip"
"testing"
"time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json/badoption"
)
func _TestWireGuard(t *testing.T) {
startDockerContainer(t, DockerOptions{
Image: ImageBoringTun,
Cap: []string{"MKNOD", "NET_ADMIN", "NET_RAW"},
Ports: []uint16{serverPort, testPort},
Bind: map[string]string{
"wireguard.conf": "/etc/wireguard/wg0.conf",
},
Cmd: []string{"wg0"},
})
time.Sleep(5 * time.Second)
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Options: &option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
ListenPort: clientPort,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeWireGuard,
Options: &option.WireGuardEndpointOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
Address: []netip.Prefix{netip.MustParsePrefix("10.0.0.2/32")},
PrivateKey: "qGnwlkZljMxeECW8fbwAWdvgntnbK7B8UmMFl3zM0mk=",
PeerPublicKey: "QsdcBm+oJw2oNv0cIFXLIq1E850lgTBonup4qnKEQBg=",
},
},
},
})
testSuitWg(t, clientPort, testPort)
}