mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-10-15 02:23:59 +00:00
Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcbb8aef53 | ||
|
|
8670ab241a | ||
|
|
e453343a56 | ||
|
|
6881f35805 | ||
|
|
8214da9697 | ||
|
|
dfb8117364 | ||
|
|
3fac02de5c | ||
|
|
ca4639eb00 | ||
|
|
aa9bfbf5b4 | ||
|
|
048293075c | ||
|
|
00155d61fc | ||
|
|
8f2273a2b4 | ||
|
|
0d0526afa2 | ||
|
|
ac2d07b61a | ||
|
|
d35487f422 | ||
|
|
2749f4a013 | ||
|
|
45c679648e | ||
|
|
5f2f7fc8b9 | ||
|
|
83c79102cf | ||
|
|
8b95292e53 | ||
|
|
3de7a2ddd3 | ||
|
|
8437a6cb4e | ||
|
|
9c4d08c6e1 | ||
|
|
e26096085e | ||
|
|
2f1b2199c5 | ||
|
|
af791db01f | ||
|
|
abcf030d89 | ||
|
|
7840dc73e3 | ||
|
|
df9050400e | ||
|
|
fdd38d6cf8 | ||
|
|
9891fd672f | ||
|
|
92a84ee112 | ||
|
|
992331f17e | ||
|
|
4fb227ed86 | ||
|
|
5a1ddea100 | ||
|
|
fbaa2f9de9 | ||
|
|
97ab9bb194 | ||
|
|
61ac141124 | ||
|
|
d4d49d9df5 | ||
|
|
c60a944aac | ||
|
|
17584c245f | ||
|
|
6e84b694a4 | ||
|
|
34a93171f0 | ||
|
|
678f6ef72f | ||
|
|
ae8187ed15 | ||
|
|
12dd1ac87f | ||
|
|
85c8f00885 | ||
|
|
e7b7ae811f | ||
|
|
a9743b77f6 | ||
|
|
4068871d97 | ||
|
|
f05afcea39 | ||
|
|
688e9daef4 | ||
|
|
64edacffb7 | ||
|
|
743df5373b | ||
|
|
e80084316d | ||
|
|
9dcd427743 | ||
|
|
d17e93384b | ||
|
|
c1ffcf365e | ||
|
|
3040e97222 | ||
|
|
5f063fb0b5 | ||
|
|
a7dadd8671 | ||
|
|
c320be75a7 | ||
|
|
bd7adcbb7e | ||
|
|
1d6d3edec5 |
@@ -11,18 +11,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Setup QEMU for Docker Buildx
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Docker metadata
|
||||
id: metadata
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/sagernet/sing-box
|
||||
- name: Get tag to build
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
echo "versioned=ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Build and release Docker images
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
platforms: linux/386,linux/amd64,linux/arm64,linux/s390x
|
||||
target: dist
|
||||
|
||||
@@ -28,7 +28,7 @@ ci_build:
|
||||
go build $(MAIN_PARAMS) $(MAIN)
|
||||
|
||||
install:
|
||||
go build -o $(PREFIX)/bin/$(NAME) $(PARAMS) $(MAIN)
|
||||
go build -o $(PREFIX)/bin/$(NAME) $(MAIN_PARAMS) $(MAIN)
|
||||
|
||||
fmt:
|
||||
@gofumpt -l -w .
|
||||
@@ -73,7 +73,7 @@ update_android_version:
|
||||
go run ./cmd/internal/update_android_version
|
||||
|
||||
build_android:
|
||||
cd ../sing-box-for-android && ./gradlew :app:assembleRelease
|
||||
cd ../sing-box-for-android && ./gradlew :app:assembleRelease && ./gradlew --stop
|
||||
|
||||
upload_android:
|
||||
mkdir -p dist/release_android
|
||||
@@ -137,7 +137,6 @@ release_macos_independent: build_macos_independent notarize_macos_independent wa
|
||||
build_tvos:
|
||||
cd ../sing-box-for-apple && \
|
||||
rm -rf build/SFT.xcarchive && \
|
||||
export DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer && \
|
||||
xcodebuild archive -scheme SFT -configuration Release -archivePath build/SFT.xcarchive
|
||||
|
||||
upload_tvos_app_store:
|
||||
@@ -179,8 +178,8 @@ lib:
|
||||
|
||||
lib_install:
|
||||
go get -v -d
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230728014906-3de089147f59
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230728014906-3de089147f59
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230915142329-c6740b6d2950
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230915142329-c6740b6d2950
|
||||
|
||||
clean:
|
||||
rm -rf bin dist sing-box
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
@@ -19,7 +18,6 @@ type V2RayServerTransport interface {
|
||||
type V2RayServerTransportHandler interface {
|
||||
N.TCPConnectionHandler
|
||||
E.Handler
|
||||
FallbackConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error
|
||||
}
|
||||
|
||||
type V2RayClientTransport interface {
|
||||
|
||||
@@ -30,9 +30,16 @@ func main() {
|
||||
newContent, updated1 := findAndReplace(objectsMap, newContent, []string{"io.nekohasekai.sfa.independent", "io.nekohasekai.sfa.system"}, newVersion.String())
|
||||
if updated0 || updated1 {
|
||||
log.Info("updated version to ", newVersion.VersionString(), " (", newVersion.String(), ")")
|
||||
}
|
||||
var updated2 bool
|
||||
if macProjectVersion := os.Getenv("MACOS_PROJECT_VERSION"); macProjectVersion != "" {
|
||||
newContent, updated2 = findAndReplaceProjectVersion(objectsMap, newContent, []string{"SFM"}, macProjectVersion)
|
||||
if updated2 {
|
||||
log.Info("updated macos project version to ", macProjectVersion)
|
||||
}
|
||||
}
|
||||
if updated0 || updated1 || updated2 {
|
||||
common.Must(os.WriteFile("sing-box.xcodeproj/project.pbxproj", []byte(newContent), 0o644))
|
||||
} else {
|
||||
log.Info("version not changed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +67,30 @@ func findAndReplace(objectsMap map[string]any, projectContent string, bundleIDLi
|
||||
return projectContent, updated
|
||||
}
|
||||
|
||||
func findAndReplaceProjectVersion(objectsMap map[string]any, projectContent string, directoryList []string, newVersion string) (string, bool) {
|
||||
objectKeyList := findObjectKeyByDirectory(objectsMap, directoryList)
|
||||
var updated bool
|
||||
for _, objectKey := range objectKeyList {
|
||||
matchRegexp := common.Must1(regexp.Compile(objectKey + ".*= \\{"))
|
||||
indexes := matchRegexp.FindStringIndex(projectContent)
|
||||
if len(indexes) < 2 {
|
||||
println(projectContent)
|
||||
log.Fatal("failed to find object key ", objectKey, ": ", strings.Index(projectContent, objectKey))
|
||||
}
|
||||
indexStart := indexes[1]
|
||||
indexEnd := indexStart + strings.Index(projectContent[indexStart:], "}")
|
||||
versionStart := indexStart + strings.Index(projectContent[indexStart:indexEnd], "CURRENT_PROJECT_VERSION = ") + 26
|
||||
versionEnd := versionStart + strings.Index(projectContent[versionStart:indexEnd], ";")
|
||||
version := projectContent[versionStart:versionEnd]
|
||||
if version == newVersion {
|
||||
continue
|
||||
}
|
||||
updated = true
|
||||
projectContent = projectContent[:versionStart] + newVersion + projectContent[versionEnd:]
|
||||
}
|
||||
return projectContent, updated
|
||||
}
|
||||
|
||||
func findObjectKey(objectsMap map[string]any, bundleIDList []string) []string {
|
||||
var objectKeyList []string
|
||||
for objectKey, object := range objectsMap {
|
||||
@@ -77,3 +108,24 @@ func findObjectKey(objectsMap map[string]any, bundleIDList []string) []string {
|
||||
}
|
||||
return objectKeyList
|
||||
}
|
||||
|
||||
func findObjectKeyByDirectory(objectsMap map[string]any, directoryList []string) []string {
|
||||
var objectKeyList []string
|
||||
for objectKey, object := range objectsMap {
|
||||
buildSettings := object.(map[string]any)["buildSettings"]
|
||||
if buildSettings == nil {
|
||||
continue
|
||||
}
|
||||
infoPListFile := buildSettings.(map[string]any)["INFOPLIST_FILE"]
|
||||
if infoPListFile == nil {
|
||||
continue
|
||||
}
|
||||
for _, searchDirectory := range directoryList {
|
||||
if strings.HasPrefix(infoPListFile.(string), searchDirectory+"/") {
|
||||
objectKeyList = append(objectKeyList, objectKey)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return objectKeyList
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandMerge = &cobra.Command{
|
||||
Use: "merge [output]",
|
||||
Short: "Merge configurations",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := merge(args[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
|
||||
func init() {
|
||||
mainCommand.AddCommand(commandMerge)
|
||||
}
|
||||
|
||||
func merge(outputPath string) error {
|
||||
mergedOptions, err := readConfigAndMerge()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mergePathResources(&mergedOptions)
|
||||
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.WriteFile(outputPath, buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outputPath, _ = filepath.Abs(outputPath)
|
||||
os.Stderr.WriteString(outputPath + "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergePathResources(options *option.Options) error {
|
||||
for index, inbound := range options.Inbounds {
|
||||
switch inbound.Type {
|
||||
case C.TypeHTTP:
|
||||
inbound.HTTPOptions.TLS = mergeTLSInboundOptions(inbound.HTTPOptions.TLS)
|
||||
case C.TypeMixed:
|
||||
inbound.MixedOptions.TLS = mergeTLSInboundOptions(inbound.MixedOptions.TLS)
|
||||
case C.TypeVMess:
|
||||
inbound.VMessOptions.TLS = mergeTLSInboundOptions(inbound.VMessOptions.TLS)
|
||||
case C.TypeTrojan:
|
||||
inbound.TrojanOptions.TLS = mergeTLSInboundOptions(inbound.TrojanOptions.TLS)
|
||||
case C.TypeNaive:
|
||||
inbound.NaiveOptions.TLS = mergeTLSInboundOptions(inbound.NaiveOptions.TLS)
|
||||
case C.TypeHysteria:
|
||||
inbound.HysteriaOptions.TLS = mergeTLSInboundOptions(inbound.HysteriaOptions.TLS)
|
||||
case C.TypeVLESS:
|
||||
inbound.VLESSOptions.TLS = mergeTLSInboundOptions(inbound.VLESSOptions.TLS)
|
||||
case C.TypeTUIC:
|
||||
inbound.TUICOptions.TLS = mergeTLSInboundOptions(inbound.TUICOptions.TLS)
|
||||
case C.TypeHysteria2:
|
||||
inbound.Hysteria2Options.TLS = mergeTLSInboundOptions(inbound.Hysteria2Options.TLS)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
options.Inbounds[index] = inbound
|
||||
}
|
||||
for index, outbound := range options.Outbounds {
|
||||
switch outbound.Type {
|
||||
case C.TypeHTTP:
|
||||
outbound.HTTPOptions.TLS = mergeTLSOutboundOptions(outbound.HTTPOptions.TLS)
|
||||
case C.TypeVMess:
|
||||
outbound.VMessOptions.TLS = mergeTLSOutboundOptions(outbound.VMessOptions.TLS)
|
||||
case C.TypeTrojan:
|
||||
outbound.TrojanOptions.TLS = mergeTLSOutboundOptions(outbound.TrojanOptions.TLS)
|
||||
case C.TypeHysteria:
|
||||
outbound.HysteriaOptions.TLS = mergeTLSOutboundOptions(outbound.HysteriaOptions.TLS)
|
||||
case C.TypeSSH:
|
||||
outbound.SSHOptions = mergeSSHOutboundOptions(outbound.SSHOptions)
|
||||
case C.TypeVLESS:
|
||||
outbound.VLESSOptions.TLS = mergeTLSOutboundOptions(outbound.VLESSOptions.TLS)
|
||||
case C.TypeTUIC:
|
||||
outbound.TUICOptions.TLS = mergeTLSOutboundOptions(outbound.TUICOptions.TLS)
|
||||
case C.TypeHysteria2:
|
||||
outbound.Hysteria2Options.TLS = mergeTLSOutboundOptions(outbound.Hysteria2Options.TLS)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
options.Outbounds[index] = outbound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeTLSInboundOptions(options *option.InboundTLSOptions) *option.InboundTLSOptions {
|
||||
if options == nil {
|
||||
return nil
|
||||
}
|
||||
if options.CertificatePath != "" {
|
||||
if content, err := os.ReadFile(options.CertificatePath); err == nil {
|
||||
options.Certificate = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
if options.KeyPath != "" {
|
||||
if content, err := os.ReadFile(options.KeyPath); err == nil {
|
||||
options.Key = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
if options.ECH != nil {
|
||||
if options.ECH.KeyPath != "" {
|
||||
if content, err := os.ReadFile(options.ECH.KeyPath); err == nil {
|
||||
options.ECH.Key = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func mergeTLSOutboundOptions(options *option.OutboundTLSOptions) *option.OutboundTLSOptions {
|
||||
if options == nil {
|
||||
return nil
|
||||
}
|
||||
if options.CertificatePath != "" {
|
||||
if content, err := os.ReadFile(options.CertificatePath); err == nil {
|
||||
options.Certificate = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
if options.ECH != nil {
|
||||
if options.ECH.ConfigPath != "" {
|
||||
if content, err := os.ReadFile(options.ECH.ConfigPath); err == nil {
|
||||
options.ECH.Config = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func mergeSSHOutboundOptions(options option.SSHOutboundOptions) option.SSHOutboundOptions {
|
||||
if options.PrivateKeyPath != "" {
|
||||
if content, err := os.ReadFile(os.ExpandEnv(options.PrivateKeyPath)); err == nil {
|
||||
options.PrivateKey = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func trimStringArray(array []string) []string {
|
||||
return common.Filter(array, func(it string) bool {
|
||||
return strings.TrimSpace(it) != ""
|
||||
})
|
||||
}
|
||||
@@ -143,14 +143,16 @@ func create() (*box.Box, context.CancelFunc, error) {
|
||||
signal.Stop(osSignals)
|
||||
close(osSignals)
|
||||
}()
|
||||
|
||||
startCtx, finishStart := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
_, loaded := <-osSignals
|
||||
if loaded {
|
||||
cancel()
|
||||
closeMonitor(startCtx)
|
||||
}
|
||||
}()
|
||||
err = instance.Start()
|
||||
finishStart()
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, nil, E.Cause(err, "start service")
|
||||
|
||||
@@ -17,7 +17,7 @@ func NewConn(conn net.Conn) (net.Conn, error) {
|
||||
element := openConnection.PushBack(conn)
|
||||
connAccess.Unlock()
|
||||
if KillerEnabled {
|
||||
err := killerCheck()
|
||||
err := KillerCheck()
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
@@ -1,20 +1,20 @@
|
||||
package conntrack
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
runtimeDebug "runtime/debug"
|
||||
"time"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/memory"
|
||||
)
|
||||
|
||||
var (
|
||||
KillerEnabled bool
|
||||
MemoryLimit int64
|
||||
MemoryLimit uint64
|
||||
killerLastCheck time.Time
|
||||
)
|
||||
|
||||
func killerCheck() error {
|
||||
func KillerCheck() error {
|
||||
if !KillerEnabled {
|
||||
return nil
|
||||
}
|
||||
@@ -23,10 +23,7 @@ func killerCheck() error {
|
||||
return nil
|
||||
}
|
||||
killerLastCheck = nowTime
|
||||
var memStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memStats)
|
||||
inuseMemory := int64(memStats.StackInuse + memStats.HeapInuse + memStats.HeapIdle - memStats.HeapReleased)
|
||||
if inuseMemory > MemoryLimit {
|
||||
if memory.Total() > MemoryLimit {
|
||||
Close()
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
@@ -18,7 +18,7 @@ func NewPacketConn(conn net.PacketConn) (net.PacketConn, error) {
|
||||
element := openConnection.PushBack(conn)
|
||||
connAccess.Unlock()
|
||||
if KillerEnabled {
|
||||
err := killerCheck()
|
||||
err := KillerCheck()
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
@@ -137,10 +137,12 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if !destination.IsIPv6() {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
||||
} else {
|
||||
if destination.IsIPv6() {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
|
||||
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4))
|
||||
} else {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,12 @@ func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error)
|
||||
}
|
||||
domainStrategy := dns.DomainStrategy(options.DomainStrategy)
|
||||
if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" {
|
||||
dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay))
|
||||
dialer = NewResolveDialer(
|
||||
router,
|
||||
dialer,
|
||||
options.Detour == "" && !options.TCPFastOpen,
|
||||
domainStrategy,
|
||||
time.Duration(options.FallbackDelay))
|
||||
}
|
||||
return dialer, nil
|
||||
}
|
||||
|
||||
@@ -16,14 +16,16 @@ import (
|
||||
|
||||
type ResolveDialer struct {
|
||||
dialer N.Dialer
|
||||
parallel bool
|
||||
router adapter.Router
|
||||
strategy dns.DomainStrategy
|
||||
fallbackDelay time.Duration
|
||||
}
|
||||
|
||||
func NewResolveDialer(router adapter.Router, dialer N.Dialer, strategy dns.DomainStrategy, fallbackDelay time.Duration) *ResolveDialer {
|
||||
func NewResolveDialer(router adapter.Router, dialer N.Dialer, parallel bool, strategy dns.DomainStrategy, fallbackDelay time.Duration) *ResolveDialer {
|
||||
return &ResolveDialer{
|
||||
dialer,
|
||||
parallel,
|
||||
router,
|
||||
strategy,
|
||||
fallbackDelay,
|
||||
@@ -48,7 +50,11 @@ func (d *ResolveDialer) DialContext(ctx context.Context, network string, destina
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == dns.DomainStrategyPreferIPv6, d.fallbackDelay)
|
||||
if d.parallel {
|
||||
return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == dns.DomainStrategyPreferIPv6, d.fallbackDelay)
|
||||
} else {
|
||||
return N.DialSerial(ctx, d.dialer, network, destination, addresses)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package humanize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// IEC Sizes.
|
||||
// kibis of bits
|
||||
const (
|
||||
Byte = 1 << (iota * 10)
|
||||
KiByte
|
||||
MiByte
|
||||
GiByte
|
||||
TiByte
|
||||
PiByte
|
||||
EiByte
|
||||
)
|
||||
|
||||
// SI Sizes.
|
||||
const (
|
||||
IByte = 1
|
||||
KByte = IByte * 1000
|
||||
MByte = KByte * 1000
|
||||
GByte = MByte * 1000
|
||||
TByte = GByte * 1000
|
||||
PByte = TByte * 1000
|
||||
EByte = PByte * 1000
|
||||
)
|
||||
|
||||
var defaultSizeTable = map[string]uint64{
|
||||
"b": Byte,
|
||||
"kib": KiByte,
|
||||
"kb": KByte,
|
||||
"mib": MiByte,
|
||||
"mb": MByte,
|
||||
"gib": GiByte,
|
||||
"gb": GByte,
|
||||
"tib": TiByte,
|
||||
"tb": TByte,
|
||||
"pib": PiByte,
|
||||
"pb": PByte,
|
||||
"eib": EiByte,
|
||||
"eb": EByte,
|
||||
// Without suffix
|
||||
"": Byte,
|
||||
"ki": KiByte,
|
||||
"k": KByte,
|
||||
"mi": MiByte,
|
||||
"m": MByte,
|
||||
"gi": GiByte,
|
||||
"g": GByte,
|
||||
"ti": TiByte,
|
||||
"t": TByte,
|
||||
"pi": PiByte,
|
||||
"p": PByte,
|
||||
"ei": EiByte,
|
||||
"e": EByte,
|
||||
}
|
||||
|
||||
var memorysSizeTable = map[string]uint64{
|
||||
"b": Byte,
|
||||
"kb": KiByte,
|
||||
"mb": MiByte,
|
||||
"gb": GiByte,
|
||||
"tb": TiByte,
|
||||
"pb": PiByte,
|
||||
"eb": EiByte,
|
||||
"": Byte,
|
||||
"k": KiByte,
|
||||
"m": MiByte,
|
||||
"g": GiByte,
|
||||
"t": TiByte,
|
||||
"p": PiByte,
|
||||
"e": EiByte,
|
||||
}
|
||||
|
||||
var (
|
||||
defaultSizes = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
|
||||
iSizes = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
|
||||
)
|
||||
|
||||
func Bytes(s uint64) string {
|
||||
return humanateBytes(s, 1000, defaultSizes)
|
||||
}
|
||||
|
||||
func MemoryBytes(s uint64) string {
|
||||
return humanateBytes(s, 1024, defaultSizes)
|
||||
}
|
||||
|
||||
func IBytes(s uint64) string {
|
||||
return humanateBytes(s, 1024, iSizes)
|
||||
}
|
||||
|
||||
func logn(n, b float64) float64 {
|
||||
return math.Log(n) / math.Log(b)
|
||||
}
|
||||
|
||||
func humanateBytes(s uint64, base float64, sizes []string) string {
|
||||
if s < 10 {
|
||||
return fmt.Sprintf("%d B", s)
|
||||
}
|
||||
e := math.Floor(logn(float64(s), base))
|
||||
suffix := sizes[int(e)]
|
||||
val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
|
||||
f := "%.0f %s"
|
||||
if val < 10 {
|
||||
f = "%.1f %s"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(f, val, suffix)
|
||||
}
|
||||
|
||||
func ParseBytes(s string) (uint64, error) {
|
||||
return parseBytes0(s, defaultSizeTable)
|
||||
}
|
||||
|
||||
func ParseMemoryBytes(s string) (uint64, error) {
|
||||
return parseBytes0(s, memorysSizeTable)
|
||||
}
|
||||
|
||||
func parseBytes0(s string, sizeTable map[string]uint64) (uint64, error) {
|
||||
lastDigit := 0
|
||||
hasComma := false
|
||||
for _, r := range s {
|
||||
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
||||
break
|
||||
}
|
||||
if r == ',' {
|
||||
hasComma = true
|
||||
}
|
||||
lastDigit++
|
||||
}
|
||||
|
||||
num := s[:lastDigit]
|
||||
if hasComma {
|
||||
num = strings.Replace(num, ",", "", -1)
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(num, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
|
||||
if m, ok := sizeTable[extra]; ok {
|
||||
f *= float64(m)
|
||||
if f >= math.MaxUint64 {
|
||||
return 0, fmt.Errorf("too large: %v", s)
|
||||
}
|
||||
return uint64(f), nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unhandled size name: %v", extra)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package interrupt
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
/*type GroupedConn interface {
|
||||
MarkAsInternal()
|
||||
}
|
||||
|
||||
func MarkAsInternal(conn any) {
|
||||
if groupedConn, isGroupConn := common.Cast[GroupedConn](conn); isGroupConn {
|
||||
groupedConn.MarkAsInternal()
|
||||
}
|
||||
}*/
|
||||
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
group *Group
|
||||
element *list.Element[*groupConnItem]
|
||||
}
|
||||
|
||||
/*func (c *Conn) MarkAsInternal() {
|
||||
c.element.Value.internal = true
|
||||
}*/
|
||||
|
||||
func (c *Conn) Close() error {
|
||||
c.group.access.Lock()
|
||||
defer c.group.access.Unlock()
|
||||
c.group.connections.Remove(c.element)
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func (c *Conn) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Conn) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Conn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
type PacketConn struct {
|
||||
net.PacketConn
|
||||
group *Group
|
||||
element *list.Element[*groupConnItem]
|
||||
}
|
||||
|
||||
/*func (c *PacketConn) MarkAsInternal() {
|
||||
c.element.Value.internal = true
|
||||
}*/
|
||||
|
||||
func (c *PacketConn) Close() error {
|
||||
c.group.access.Lock()
|
||||
defer c.group.access.Unlock()
|
||||
c.group.connections.Remove(c.element)
|
||||
return c.PacketConn.Close()
|
||||
}
|
||||
|
||||
func (c *PacketConn) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return c.PacketConn
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package interrupt
|
||||
|
||||
import "context"
|
||||
|
||||
type contextKeyIsExternalConnection struct{}
|
||||
|
||||
func ContextWithIsExternalConnection(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, contextKeyIsExternalConnection{}, true)
|
||||
}
|
||||
|
||||
func IsExternalConnectionFromContext(ctx context.Context) bool {
|
||||
return ctx.Value(contextKeyIsExternalConnection{}) != nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package interrupt
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
type Group struct {
|
||||
access sync.Mutex
|
||||
connections list.List[*groupConnItem]
|
||||
}
|
||||
|
||||
type groupConnItem struct {
|
||||
conn io.Closer
|
||||
isExternal bool
|
||||
}
|
||||
|
||||
func NewGroup() *Group {
|
||||
return &Group{}
|
||||
}
|
||||
|
||||
func (g *Group) NewConn(conn net.Conn, isExternal bool) net.Conn {
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
item := g.connections.PushBack(&groupConnItem{conn, isExternal})
|
||||
return &Conn{Conn: conn, group: g, element: item}
|
||||
}
|
||||
|
||||
func (g *Group) NewPacketConn(conn net.PacketConn, isExternal bool) net.PacketConn {
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
item := g.connections.PushBack(&groupConnItem{conn, isExternal})
|
||||
return &PacketConn{PacketConn: conn, group: g, element: item}
|
||||
}
|
||||
|
||||
func (g *Group) Interrupt(interruptExternalConnections bool) {
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
var toDelete []*list.Element[*groupConnItem]
|
||||
for element := g.connections.Front(); element != nil; element = element.Next() {
|
||||
if !element.Value.isExternal || interruptExternalConnections {
|
||||
element.Value.conn.Close()
|
||||
toDelete = append(toDelete, element)
|
||||
}
|
||||
}
|
||||
for _, element := range toDelete {
|
||||
g.connections.Remove(element)
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package proxyproto
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/pires/go-proxyproto"
|
||||
)
|
||||
|
||||
var _ N.Dialer = (*Dialer)(nil)
|
||||
|
||||
type Dialer struct {
|
||||
N.Dialer
|
||||
}
|
||||
|
||||
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
conn, err := d.Dialer.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var source M.Socksaddr
|
||||
metadata := adapter.ContextFrom(ctx)
|
||||
if metadata != nil {
|
||||
source = metadata.Source
|
||||
}
|
||||
if !source.IsValid() {
|
||||
source = M.SocksaddrFromNet(conn.LocalAddr())
|
||||
}
|
||||
if destination.Addr.Is6() {
|
||||
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
|
||||
}
|
||||
h := proxyproto.HeaderProxyFromAddrs(1, source.TCPAddr(), destination.TCPAddr())
|
||||
_, err = h.WriteTo(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, E.Cause(err, "write proxy protocol header")
|
||||
}
|
||||
return conn, nil
|
||||
default:
|
||||
return d.Dialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package proxyproto
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"github.com/pires/go-proxyproto"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
net.Listener
|
||||
AcceptNoHeader bool
|
||||
}
|
||||
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bufReader := std_bufio.NewReader(conn)
|
||||
header, err := proxyproto.Read(bufReader)
|
||||
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) {
|
||||
return nil, &Error{err}
|
||||
}
|
||||
if bufReader.Buffered() > 0 {
|
||||
cache := buf.NewSize(bufReader.Buffered())
|
||||
_, err = cache.ReadFullFrom(bufReader, cache.FreeLen())
|
||||
if err != nil {
|
||||
return nil, &Error{err}
|
||||
}
|
||||
conn = bufio.NewCachedConn(conn, cache)
|
||||
}
|
||||
if header != nil {
|
||||
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
|
||||
Source: M.SocksaddrFromNet(header.SourceAddr).Unwrap(),
|
||||
Destination: M.SocksaddrFromNet(header.DestinationAddr).Unwrap(),
|
||||
}}, nil
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
var _ net.Error = (*Error)(nil)
|
||||
|
||||
type Error struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e *Error) Unwrap() error {
|
||||
return e.error
|
||||
}
|
||||
|
||||
func (e *Error) Timeout() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *Error) Temporary() bool {
|
||||
return true
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package qtls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
)
|
||||
|
||||
type QUICConfig interface {
|
||||
Dial(ctx context.Context, conn net.PacketConn, addr net.Addr, config *quic.Config) (quic.Connection, error)
|
||||
DialEarly(ctx context.Context, conn net.PacketConn, addr net.Addr, config *quic.Config) (quic.EarlyConnection, error)
|
||||
CreateTransport(conn net.PacketConn, quicConnPtr *quic.EarlyConnection, serverAddr M.Socksaddr, quicConfig *quic.Config, enableDatagrams bool) http.RoundTripper
|
||||
}
|
||||
|
||||
type QUICServerConfig interface {
|
||||
Listen(conn net.PacketConn, config *quic.Config) (QUICListener, error)
|
||||
ListenEarly(conn net.PacketConn, config *quic.Config) (QUICEarlyListener, error)
|
||||
ConfigureHTTP3()
|
||||
}
|
||||
|
||||
type QUICListener interface {
|
||||
Accept(ctx context.Context) (quic.Connection, error)
|
||||
Close() error
|
||||
Addr() net.Addr
|
||||
}
|
||||
|
||||
type QUICEarlyListener interface {
|
||||
Accept(ctx context.Context) (quic.EarlyConnection, error)
|
||||
Close() error
|
||||
Addr() net.Addr
|
||||
}
|
||||
|
||||
func Dial(ctx context.Context, conn net.PacketConn, addr net.Addr, config aTLS.Config, quicConfig *quic.Config) (quic.Connection, error) {
|
||||
if quicTLSConfig, isQUICConfig := config.(QUICConfig); isQUICConfig {
|
||||
return quicTLSConfig.Dial(ctx, conn, addr, quicConfig)
|
||||
}
|
||||
tlsConfig, err := config.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return quic.Dial(ctx, conn, addr, tlsConfig, quicConfig)
|
||||
}
|
||||
|
||||
func DialEarly(ctx context.Context, conn net.PacketConn, addr net.Addr, config aTLS.Config, quicConfig *quic.Config) (quic.EarlyConnection, error) {
|
||||
if quicTLSConfig, isQUICConfig := config.(QUICConfig); isQUICConfig {
|
||||
return quicTLSConfig.DialEarly(ctx, conn, addr, quicConfig)
|
||||
}
|
||||
tlsConfig, err := config.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return quic.DialEarly(ctx, conn, addr, tlsConfig, quicConfig)
|
||||
}
|
||||
|
||||
func CreateTransport(conn net.PacketConn, quicConnPtr *quic.EarlyConnection, serverAddr M.Socksaddr, config aTLS.Config, quicConfig *quic.Config, enableDatagrams bool) (http.RoundTripper, error) {
|
||||
if quicTLSConfig, isQUICConfig := config.(QUICConfig); isQUICConfig {
|
||||
return quicTLSConfig.CreateTransport(conn, quicConnPtr, serverAddr, quicConfig, enableDatagrams), nil
|
||||
}
|
||||
tlsConfig, err := config.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &http3.RoundTripper{
|
||||
TLSClientConfig: tlsConfig,
|
||||
QuicConfig: quicConfig,
|
||||
EnableDatagrams: enableDatagrams,
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||
quicConn, err := quic.DialEarly(ctx, conn, serverAddr.UDPAddr(), tlsCfg, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*quicConnPtr = quicConn
|
||||
return quicConn, nil
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Listen(conn net.PacketConn, config aTLS.ServerConfig, quicConfig *quic.Config) (QUICListener, error) {
|
||||
if quicTLSConfig, isQUICConfig := config.(QUICServerConfig); isQUICConfig {
|
||||
return quicTLSConfig.Listen(conn, quicConfig)
|
||||
}
|
||||
tlsConfig, err := config.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return quic.Listen(conn, tlsConfig, quicConfig)
|
||||
}
|
||||
|
||||
func ListenEarly(conn net.PacketConn, config aTLS.ServerConfig, quicConfig *quic.Config) (QUICEarlyListener, error) {
|
||||
if quicTLSConfig, isQUICConfig := config.(QUICServerConfig); isQUICConfig {
|
||||
return quicTLSConfig.ListenEarly(conn, quicConfig)
|
||||
}
|
||||
tlsConfig, err := config.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return quic.ListenEarly(conn, tlsConfig, quicConfig)
|
||||
}
|
||||
|
||||
func ConfigureHTTP3(config aTLS.ServerConfig) error {
|
||||
if len(config.NextProtos()) == 0 {
|
||||
config.SetNextProtos([]string{http3.NextProtoH3})
|
||||
}
|
||||
if quicTLSConfig, isQUICConfig := config.(QUICServerConfig); isQUICConfig {
|
||||
quicTLSConfig.ConfigureHTTP3()
|
||||
return nil
|
||||
}
|
||||
tlsConfig, err := config.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
http3.ConfigureTLSConfig(tlsConfig)
|
||||
return nil
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func (p *LinuxSystemProxy) Enable() error {
|
||||
}
|
||||
}
|
||||
if p.hasKWriteConfig5 {
|
||||
err := p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "'Proxy Settings'", "--key", "ProxyType", "1")
|
||||
err := p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func (p *LinuxSystemProxy) Enable() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "'Proxy Settings'", "--key", "Authmode", "0")
|
||||
err = p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "Authmode", "0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func (p *LinuxSystemProxy) Disable() error {
|
||||
}
|
||||
}
|
||||
if p.hasKWriteConfig5 {
|
||||
err := p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "'Proxy Settings'", "--key", "ProxyType", "0")
|
||||
err := p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,10 +9,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/libdns/alidns"
|
||||
"github.com/libdns/cloudflare"
|
||||
"github.com/mholt/acmez/acme"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
@@ -74,6 +77,24 @@ func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Con
|
||||
AltTLSALPNPort: int(options.AlternativeTLSPort),
|
||||
Logger: config.Logger,
|
||||
}
|
||||
if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" {
|
||||
var solver certmagic.DNS01Solver
|
||||
switch dnsOptions.Provider {
|
||||
case C.DNSProviderAliDNS:
|
||||
solver.DNSProvider = &alidns.Provider{
|
||||
AccKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
|
||||
AccKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
|
||||
RegionID: dnsOptions.AliDNSOptions.RegionID,
|
||||
}
|
||||
case C.DNSProviderCloudflare:
|
||||
solver.DNSProvider = &cloudflare.Provider{
|
||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||
}
|
||||
default:
|
||||
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
|
||||
}
|
||||
acmeConfig.DNS01Solver = &solver
|
||||
}
|
||||
if options.ExternalAccount != nil && options.ExternalAccount.KeyID != "" {
|
||||
acmeConfig.ExternalAccount = (*acme.EAB)(options.ExternalAccount)
|
||||
}
|
||||
|
||||
@@ -149,8 +149,8 @@ func NewECHClient(ctx context.Context, serverAddress string, options option.Outb
|
||||
}
|
||||
}
|
||||
var certificate []byte
|
||||
if options.Certificate != "" {
|
||||
certificate = []byte(options.Certificate)
|
||||
if len(options.Certificate) > 0 {
|
||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||
} else if options.CertificatePath != "" {
|
||||
content, err := os.ReadFile(options.CertificatePath)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,13 +10,13 @@ import (
|
||||
"github.com/sagernet/cloudflare-tls"
|
||||
"github.com/sagernet/quic-go/ech"
|
||||
"github.com/sagernet/quic-go/http3_ech"
|
||||
"github.com/sagernet/sing-box/common/qtls"
|
||||
"github.com/sagernet/sing-quic"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
var (
|
||||
_ qtls.QUICConfig = (*echClientConfig)(nil)
|
||||
_ qtls.QUICServerConfig = (*echServerConfig)(nil)
|
||||
_ qtls.Config = (*echClientConfig)(nil)
|
||||
_ qtls.ServerConfig = (*echServerConfig)(nil)
|
||||
)
|
||||
|
||||
func (c *echClientConfig) Dial(ctx context.Context, conn net.PacketConn, addr net.Addr, config *quic.Config) (quic.Connection, error) {
|
||||
@@ -43,11 +43,11 @@ func (c *echClientConfig) CreateTransport(conn net.PacketConn, quicConnPtr *quic
|
||||
}
|
||||
}
|
||||
|
||||
func (c *echServerConfig) Listen(conn net.PacketConn, config *quic.Config) (qtls.QUICListener, error) {
|
||||
func (c *echServerConfig) Listen(conn net.PacketConn, config *quic.Config) (qtls.Listener, error) {
|
||||
return quic.Listen(conn, c.config, config)
|
||||
}
|
||||
|
||||
func (c *echServerConfig) ListenEarly(conn net.PacketConn, config *quic.Config) (qtls.QUICEarlyListener, error) {
|
||||
func (c *echServerConfig) ListenEarly(conn net.PacketConn, config *quic.Config) (qtls.EarlyListener, error) {
|
||||
return quic.ListenEarly(conn, c.config, config)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -111,8 +112,8 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb
|
||||
}
|
||||
}
|
||||
var certificate []byte
|
||||
if options.Certificate != "" {
|
||||
certificate = []byte(options.Certificate)
|
||||
if len(options.Certificate) > 0 {
|
||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||
} else if options.CertificatePath != "" {
|
||||
content, err := os.ReadFile(options.CertificatePath)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -168,8 +169,8 @@ func NewUTLSClient(ctx context.Context, serverAddress string, options option.Out
|
||||
}
|
||||
}
|
||||
var certificate []byte
|
||||
if options.Certificate != "" {
|
||||
certificate = []byte(options.Certificate)
|
||||
if len(options.Certificate) > 0 {
|
||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||
} else if options.CertificatePath != "" {
|
||||
content, err := os.ReadFile(options.CertificatePath)
|
||||
if err != nil {
|
||||
|
||||
+10
-17
@@ -8,6 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
@@ -96,33 +97,25 @@ func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err e
|
||||
return
|
||||
}
|
||||
defer instance.Close()
|
||||
|
||||
if earlyConn, isEarlyConn := common.Cast[N.EarlyConn](instance); isEarlyConn && earlyConn.NeedHandshake() {
|
||||
start = time.Now()
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodHead, link, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
transport := &http.Transport{
|
||||
Dial: func(string, string) (net.Conn, error) {
|
||||
return instance, nil
|
||||
},
|
||||
// from http.DefaultTransport
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Transport: transport,
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return instance, nil
|
||||
},
|
||||
},
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
DNSProviderAliDNS = "alidns"
|
||||
DNSProviderCloudflare = "cloudflare"
|
||||
)
|
||||
+2
-2
@@ -5,7 +5,7 @@ package box
|
||||
import (
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ func applyDebugOptions(options option.DebugOptions) {
|
||||
}
|
||||
if options.MemoryLimit != 0 {
|
||||
// debug.SetMemoryLimit(int64(options.MemoryLimit))
|
||||
conntrack.MemoryLimit = int64(options.MemoryLimit)
|
||||
conntrack.MemoryLimit = uint64(options.MemoryLimit)
|
||||
}
|
||||
if options.OOMKiller != nil {
|
||||
conntrack.KillerEnabled = *options.OOMKiller
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@ package box
|
||||
import (
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
@@ -27,8 +27,8 @@ func applyDebugOptions(options option.DebugOptions) {
|
||||
debug.SetTraceback(options.TraceBack)
|
||||
}
|
||||
if options.MemoryLimit != 0 {
|
||||
debug.SetMemoryLimit(int64(options.MemoryLimit))
|
||||
conntrack.MemoryLimit = int64(options.MemoryLimit)
|
||||
debug.SetMemoryLimit(int64(float64(options.MemoryLimit) / 1.5))
|
||||
conntrack.MemoryLimit = uint64(options.MemoryLimit)
|
||||
}
|
||||
if options.OOMKiller != nil {
|
||||
conntrack.KillerEnabled = *options.OOMKiller
|
||||
|
||||
+4
-4
@@ -7,12 +7,12 @@ import (
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing-box/common/badjson"
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
@@ -37,9 +37,9 @@ func applyDebugListenOption(options option.DebugOptions) {
|
||||
runtime.ReadMemStats(&memStats)
|
||||
|
||||
var memObject badjson.JSONObject
|
||||
memObject.Put("heap", humanize.IBytes(memStats.HeapInuse))
|
||||
memObject.Put("stack", humanize.IBytes(memStats.StackInuse))
|
||||
memObject.Put("idle", humanize.IBytes(memStats.HeapIdle-memStats.HeapReleased))
|
||||
memObject.Put("heap", humanize.MemoryBytes(memStats.HeapInuse))
|
||||
memObject.Put("stack", humanize.MemoryBytes(memStats.StackInuse))
|
||||
memObject.Put("idle", humanize.MemoryBytes(memStats.HeapIdle-memStats.HeapReleased))
|
||||
memObject.Put("goroutines", runtime.NumGoroutine())
|
||||
memObject.Put("rss", rusageMaxRSS())
|
||||
|
||||
|
||||
+175
-1
@@ -1,3 +1,177 @@
|
||||
#### 1.6.0-alpha.4
|
||||
|
||||
* Add `brutal_debug` option for Hysteria2
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.5.2
|
||||
|
||||
* Our [Apple tvOS client](/installation/clients/sft) is now available in the App Store 🍎
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.6.0-alpha.3
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.6.0-alpha.2
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.5.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.6.0-alpha.1
|
||||
|
||||
* Update BBR congestion control for TUIC and Hysteria2 **1**
|
||||
* Update quic-go to v0.39.0
|
||||
* Update gVisor to 20230814.0
|
||||
* Remove [Deprecated Features](/deprecated) by agreement
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
None of the existing Golang BBR congestion control implementations have been reviewed or unit tested.
|
||||
This update is intended to address the multi-send defects of the old implementation and may introduce new issues.
|
||||
|
||||
#### 1.5.0
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
Important changes since 1.4:
|
||||
|
||||
* Add TLS [ECH server](/configuration/shared/tls) support
|
||||
* Improve TLS TCH client configuration
|
||||
* Add TLS ECH key pair generator **1**
|
||||
* Add TLS ECH support for QUIC based protocols **2**
|
||||
* Add KDE support for the `set_system_proxy` option in HTTP inbound
|
||||
* Add Hysteria2 protocol support **3**
|
||||
* Add `interrupt_exist_connections` option for `Selector` and `URLTest` outbounds **4**
|
||||
* Add DNS01 challenge support for ACME TLS certificate issuer **5**
|
||||
* Add `merge` command **6**
|
||||
* Mark [Deprecated Features](/deprecated)
|
||||
|
||||
**1**:
|
||||
|
||||
Command: `sing-box generate ech-keypair <plain_server_name> [--pq-signature-schemes-enabled]`
|
||||
|
||||
**2**:
|
||||
|
||||
All inbounds and outbounds are supported, including `Naiveproxy`, `Hysteria[/2]`, `TUIC` and `V2ray QUIC transport`.
|
||||
|
||||
**3**:
|
||||
|
||||
See [Hysteria2 inbound](/configuration/inbound/hysteria2) and [Hysteria2 outbound](/configuration/outbound/hysteria2)
|
||||
|
||||
For protocol description, please refer to [https://v2.hysteria.network](https://v2.hysteria.network)
|
||||
|
||||
**4**:
|
||||
|
||||
Interrupt existing connections when the selected outbound has changed.
|
||||
|
||||
Only inbound connections are affected by this setting, internal connections will always be interrupted.
|
||||
|
||||
**5**:
|
||||
|
||||
Only `Alibaba Cloud DNS` and `Cloudflare` are supported, see [ACME Fields](/configuration/shared/tls#acme-fields)
|
||||
and [DNS01 Challenge Fields](/configuration/shared/dns01_challenge).
|
||||
|
||||
**6**:
|
||||
|
||||
This command also parses path resources that appear in the configuration file and replaces them with embedded
|
||||
configuration, such as TLS certificates or SSH private keys.
|
||||
|
||||
#### 1.5.0-rc.6
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.4.6
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.5.0-rc.5
|
||||
|
||||
* Fixed an improper authentication vulnerability in the SOCKS5 inbound
|
||||
* Fixes and improvements
|
||||
|
||||
**Security Advisory**
|
||||
|
||||
This update fixes an improper authentication vulnerability in the sing-box SOCKS inbound. This vulnerability allows an
|
||||
attacker to craft special requests to bypass user authentication. All users exposing SOCKS servers with user
|
||||
authentication in an insecure environment are advised to update immediately.
|
||||
|
||||
此更新修复了 sing-box SOCKS 入站中的一个不正确身份验证漏洞。 该漏洞允许攻击者制作特殊请求来绕过用户身份验证。建议所有将使用用户认证的
|
||||
SOCKS 服务器暴露在不安全环境下的用户立更新。
|
||||
|
||||
#### 1.4.5
|
||||
|
||||
* Fixed an improper authentication vulnerability in the SOCKS5 inbound
|
||||
* Fixes and improvements
|
||||
|
||||
**Security Advisory**
|
||||
|
||||
This update fixes an improper authentication vulnerability in the sing-box SOCKS inbound. This vulnerability allows an
|
||||
attacker to craft special requests to bypass user authentication. All users exposing SOCKS servers with user
|
||||
authentication in an insecure environment are advised to update immediately.
|
||||
|
||||
此更新修复了 sing-box SOCKS 入站中的一个不正确身份验证漏洞。 该漏洞允许攻击者制作特殊请求来绕过用户身份验证。建议所有将使用用户认证的
|
||||
SOCKS 服务器暴露在不安全环境下的用户立更新。
|
||||
|
||||
#### 1.5.0-rc.3
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.5.0-beta.12
|
||||
|
||||
* Add `merge` command **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
This command also parses path resources that appear in the configuration file and replaces them with embedded
|
||||
configuration, such as TLS certificates or SSH private keys.
|
||||
|
||||
```
|
||||
Merge configurations
|
||||
|
||||
Usage:
|
||||
sing-box merge [output] [flags]
|
||||
|
||||
Flags:
|
||||
-h, --help help for merge
|
||||
|
||||
Global Flags:
|
||||
-c, --config stringArray set configuration file path
|
||||
-C, --config-directory stringArray set configuration directory path
|
||||
-D, --directory string set working directory
|
||||
--disable-color disable color output
|
||||
```
|
||||
|
||||
#### 1.5.0-beta.11
|
||||
|
||||
* Add DNS01 challenge support for ACME TLS certificate issuer **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Only `Alibaba Cloud DNS` and `Cloudflare` are supported,
|
||||
see [ACME Fields](/configuration/shared/tls#acme-fields)
|
||||
and [DNS01 Challenge Fields](/configuration/shared/dns01_challenge).
|
||||
|
||||
#### 1.5.0-beta.10
|
||||
|
||||
* Add `interrupt_exist_connections` option for `Selector` and `URLTest` outbounds **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Interrupt existing connections when the selected outbound has changed.
|
||||
|
||||
Only inbound connections are affected by this setting, internal connections will always be interrupted.
|
||||
|
||||
#### 1.4.3
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.5.0-beta.8
|
||||
|
||||
* Fixes and improvements
|
||||
@@ -43,7 +217,7 @@ For protocol description, please refer to [https://v2.hysteria.network](https://
|
||||
|
||||
**1**:
|
||||
|
||||
Command: `sing-box generate ech-keypair <plain_server_name> [-pq-signature-schemes-enabled]`
|
||||
Command: `sing-box generate ech-keypair <plain_server_name> [--pq-signature-schemes-enabled]`
|
||||
|
||||
**2**:
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ ALWAYS set a secret if RESTful API is listening on 0.0.0.0
|
||||
|
||||
#### default_mode
|
||||
|
||||
Default mode in clash, `rule` will be used if empty.
|
||||
Default mode in clash, `Rule` will be used if empty.
|
||||
|
||||
This setting has no direct effect, but can be used in routing and DNS rules via the `clash_mode` rule item.
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ RESTful API 的密钥(可选)
|
||||
|
||||
#### default_mode
|
||||
|
||||
Clash 中的默认模式,默认使用 `rule`。
|
||||
Clash 中的默认模式,默认使用 `Rule`。
|
||||
|
||||
此设置没有直接影响,但可以通过 `clash_mode` 规则项在路由和 DNS 规则中使用。
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
}
|
||||
],
|
||||
"ignore_client_bandwidth": false,
|
||||
"tls": {},
|
||||
"masquerade": "",
|
||||
"tls": {}
|
||||
"brutal_debug": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -67,6 +68,12 @@ Commands the client to use the BBR flow control algorithm instead of Hysteria CC
|
||||
|
||||
Conflict with `up_mbps` and `down_mbps`.
|
||||
|
||||
#### tls
|
||||
|
||||
==Required==
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||
|
||||
#### masquerade
|
||||
|
||||
HTTP3 server behavior when authentication fails.
|
||||
@@ -78,8 +85,6 @@ HTTP3 server behavior when authentication fails.
|
||||
|
||||
A 404 page will be returned if empty.
|
||||
|
||||
#### tls
|
||||
#### brutal_debug
|
||||
|
||||
==Required==
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||
Enable debug information logging for Hysteria Brutal CC.
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
}
|
||||
],
|
||||
"ignore_client_bandwidth": false,
|
||||
"tls": {},
|
||||
"masquerade": "",
|
||||
"tls": {}
|
||||
"brutal_debug": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -65,6 +66,12 @@ Hysteria 用户
|
||||
|
||||
与 `up_mbps` 和 `down_mbps` 冲突。
|
||||
|
||||
#### tls
|
||||
|
||||
==必填==
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||
|
||||
#### masquerade
|
||||
|
||||
HTTP3 服务器认证失败时的行为。
|
||||
@@ -76,8 +83,6 @@ HTTP3 服务器认证失败时的行为。
|
||||
|
||||
如果为空,则返回 404 页。
|
||||
|
||||
#### tls
|
||||
#### brutal_debug
|
||||
|
||||
==必填==
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||
启用 Hysteria Brutal CC 的调试信息日志记录。
|
||||
|
||||
@@ -31,11 +31,17 @@ sing-box uses JSON for configuration files.
|
||||
### Check
|
||||
|
||||
```bash
|
||||
$ sing-box check
|
||||
sing-box check
|
||||
```
|
||||
|
||||
### Format
|
||||
|
||||
```bash
|
||||
$ sing-box format -w
|
||||
sing-box format -w -c config.json -D config_directory
|
||||
```
|
||||
|
||||
### Merge
|
||||
|
||||
```bash
|
||||
sing-box merge output.json -c config.json -D config_directory
|
||||
```
|
||||
@@ -29,11 +29,17 @@ sing-box 使用 JSON 作为配置文件格式。
|
||||
### 检查
|
||||
|
||||
```bash
|
||||
$ sing-box check
|
||||
sing-box check
|
||||
```
|
||||
|
||||
### 格式化
|
||||
|
||||
```bash
|
||||
$ sing-box format -w
|
||||
sing-box format -w -c config.json -D config_directory
|
||||
```
|
||||
|
||||
### 合并
|
||||
|
||||
```bash
|
||||
sing-box merge output.json -c config.json -D config_directory
|
||||
```
|
||||
@@ -16,6 +16,7 @@
|
||||
"password": "goofy_ahh_password",
|
||||
"network": "tcp",
|
||||
"tls": {},
|
||||
"brutal_debug": false,
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
@@ -73,6 +74,10 @@ Both is enabled by default.
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
#### brutal_debug
|
||||
|
||||
Enable debug information logging for Hysteria Brutal CC.
|
||||
|
||||
### Dial Fields
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial) for details.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"password": "goofy_ahh_password",
|
||||
"network": "tcp",
|
||||
"tls": {},
|
||||
"brutal_debug": false,
|
||||
|
||||
... // 拨号字段
|
||||
}
|
||||
@@ -73,6 +74,9 @@ QUIC 流量混淆器密码.
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
#### brutal_debug
|
||||
|
||||
启用 Hysteria Brutal CC 的调试信息日志记录。
|
||||
|
||||
### 拨号字段
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"proxy-b",
|
||||
"proxy-c"
|
||||
],
|
||||
"default": "proxy-c"
|
||||
"default": "proxy-c",
|
||||
"interrupt_exist_connections": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -28,4 +29,10 @@ List of outbound tags to select.
|
||||
|
||||
#### default
|
||||
|
||||
The default outbound tag. The first outbound will be used if empty.
|
||||
The default outbound tag. The first outbound will be used if empty.
|
||||
|
||||
#### interrupt_exist_connections
|
||||
|
||||
Interrupt existing connections when the selected outbound has changed.
|
||||
|
||||
Only inbound connections are affected by this setting, internal connections will always be interrupted.
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"proxy-b",
|
||||
"proxy-c"
|
||||
],
|
||||
"default": "proxy-c"
|
||||
"default": "proxy-c",
|
||||
"interrupt_exist_connections": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -29,3 +30,9 @@
|
||||
#### default
|
||||
|
||||
默认的出站标签。默认使用第一个出站。
|
||||
|
||||
#### interrupt_exist_connections
|
||||
|
||||
当选定的出站发生更改时,中断现有连接。
|
||||
|
||||
仅入站连接受此设置影响,内部连接将始终被中断。
|
||||
@@ -12,7 +12,8 @@
|
||||
],
|
||||
"url": "https://www.gstatic.com/generate_204",
|
||||
"interval": "1m",
|
||||
"tolerance": 50
|
||||
"tolerance": 50,
|
||||
"interrupt_exist_connections": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -35,3 +36,9 @@ The test interval. `1m` will be used if empty.
|
||||
#### tolerance
|
||||
|
||||
The test tolerance in milliseconds. `50` will be used if empty.
|
||||
|
||||
#### interrupt_exist_connections
|
||||
|
||||
Interrupt existing connections when the selected outbound has changed.
|
||||
|
||||
Only inbound connections are affected by this setting, internal connections will always be interrupted.
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
],
|
||||
"url": "https://www.gstatic.com/generate_204",
|
||||
"interval": "1m",
|
||||
"tolerance": 50
|
||||
"tolerance": 50,
|
||||
"interrupt_exist_connections": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -35,3 +36,9 @@
|
||||
#### tolerance
|
||||
|
||||
以毫秒为单位的测试容差。 默认使用 `50`。
|
||||
|
||||
#### interrupt_exist_connections
|
||||
|
||||
当选定的出站发生更改时,中断现有连接。
|
||||
|
||||
仅入站连接受此设置影响,内部连接将始终被中断。
|
||||
@@ -0,0 +1,31 @@
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "",
|
||||
|
||||
... // Provider Fields
|
||||
}
|
||||
```
|
||||
|
||||
### Provider Fields
|
||||
|
||||
#### Alibaba Cloud DNS
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "alidns",
|
||||
"access_key_id": "",
|
||||
"access_key_secret": "",
|
||||
"region_id": ""
|
||||
}
|
||||
```
|
||||
|
||||
#### Cloudflare
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "cloudflare",
|
||||
"api_token": ""
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "",
|
||||
|
||||
... // 提供商字段
|
||||
}
|
||||
```
|
||||
|
||||
### 提供商字段
|
||||
|
||||
#### Alibaba Cloud DNS
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "alidns",
|
||||
"access_key_id": "",
|
||||
"access_key_secret": "",
|
||||
"region_id": ""
|
||||
}
|
||||
```
|
||||
|
||||
#### Cloudflare
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "cloudflare",
|
||||
"api_token": ""
|
||||
}
|
||||
```
|
||||
@@ -25,7 +25,8 @@
|
||||
"external_account": {
|
||||
"key_id": "",
|
||||
"mac_key": ""
|
||||
}
|
||||
},
|
||||
"dns01_challenge": {}
|
||||
},
|
||||
"ech": {
|
||||
"enabled": false,
|
||||
@@ -232,7 +233,7 @@ Chrome fingerprint will be used if empty.
|
||||
ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello
|
||||
message.
|
||||
|
||||
The ECH key and configuration can be generated by `sing-box generate ech-keypair [-pq-signature-schemes-enabled]`.
|
||||
The ECH key and configuration can be generated by `sing-box generate ech-keypair [--pq-signature-schemes-enabled]`.
|
||||
|
||||
#### pq_signature_schemes_enabled
|
||||
|
||||
@@ -348,6 +349,12 @@ The key identifier.
|
||||
|
||||
The MAC key.
|
||||
|
||||
#### dns01_challenge
|
||||
|
||||
ACME DNS01 challenge field. If configured, other challenge methods will be disabled.
|
||||
|
||||
See [DNS01 Challenge Fields](/configuration/shared/dns01_challenge) for details.
|
||||
|
||||
### Reality Fields
|
||||
|
||||
!!! warning ""
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"external_account": {
|
||||
"key_id": "",
|
||||
"mac_key": ""
|
||||
}
|
||||
},
|
||||
"dns01_challenge": {}
|
||||
},
|
||||
"ech": {
|
||||
"enabled": false,
|
||||
@@ -227,7 +228,7 @@ ECH (Encrypted Client Hello) 是一个 TLS 扩展,它允许客户端加密其
|
||||
信息。
|
||||
|
||||
|
||||
ECH 配置和密钥可以通过 `sing-box generate ech-keypair [-pq-signature-schemes-enabled]` 生成。
|
||||
ECH 配置和密钥可以通过 `sing-box generate ech-keypair [--pq-signature-schemes-enabled]` 生成。
|
||||
|
||||
#### pq_signature_schemes_enabled
|
||||
|
||||
@@ -339,6 +340,12 @@ EAB(外部帐户绑定)包含将 ACME 帐户绑定或映射到其他已知
|
||||
|
||||
MAC 密钥。
|
||||
|
||||
#### dns01_challenge
|
||||
|
||||
ACME DNS01 验证字段。如果配置,将禁用其他验证方法。
|
||||
|
||||
参阅 [DNS01 验证字段](/configuration/shared/dns01_challenge)。
|
||||
|
||||
### Reality 字段
|
||||
|
||||
!!! warning ""
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
# Deprecated Feature List
|
||||
|
||||
The following features will be marked deprecated in 1.5.0 and removed entirely in 1.6.0.
|
||||
### 1.6.0
|
||||
|
||||
The following features were marked deprecated in 1.5.0 and removed entirely in 1.6.0.
|
||||
|
||||
#### ShadowsocksR
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#### 注意事项
|
||||
|
||||
* 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 崩溃日志位于 `设置` -> `查看服务日志`
|
||||
* 崩溃日志位于 `Settings` -> `View Service Log`
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#### 注意事项
|
||||
|
||||
* 远程配置文件请求中的 User Agent 为 `SFM/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 崩溃日志位于 `设置` -> `查看服务日志`
|
||||
* 崩溃日志位于 `Settings` -> `View Service Log`
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ Experimental Apple tvOS client for sing-box.
|
||||
|
||||
#### Download
|
||||
|
||||
* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673)
|
||||
* [TestFlight](https://testflight.apple.com/join/AcqO44FH)
|
||||
|
||||
#### Features
|
||||
@@ -15,7 +16,7 @@ Experimental Apple tvOS client for sing-box.
|
||||
Full functionality, except for:
|
||||
|
||||
* Only remote configuration files can be created manually
|
||||
* You need to update SFI to the latest beta version to import profiles from iPhone/iPad
|
||||
* You need to update SFI to the latest version to import profiles from iPhone/iPad
|
||||
* No iCloud profile support
|
||||
|
||||
#### Note
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# SFI
|
||||
|
||||
实验性的 Apple tvOS sing-box 客户端。
|
||||
|
||||
#### 要求
|
||||
|
||||
* tvOS 17.0+
|
||||
* 一个非中国大陆地区的 Apple 账号
|
||||
|
||||
#### 下载
|
||||
|
||||
* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673)
|
||||
* [TestFlight](https://testflight.apple.com/join/AcqO44FH)
|
||||
|
||||
#### 特性
|
||||
|
||||
完整的功能,除了:
|
||||
|
||||
* 只能手动创建远程配置文件
|
||||
* 您需要将 SFI 更新到最新版本才能从 iPhone/iPad 导入配置文件
|
||||
* 没有 iCloud 配置文件支持
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* 远程配置文件请求中的 User Agent 为 `SFT/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 崩溃日志位于 `Settings` -> `View Service Log`
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
* SFT 不收集或共享个人数据。
|
||||
* 软件生成的数据始终在您的设备上。
|
||||
@@ -25,6 +25,7 @@ type CommandClientOptions struct {
|
||||
type CommandClientHandler interface {
|
||||
Connected()
|
||||
Disconnected(message string)
|
||||
ClearLog()
|
||||
WriteLog(message string)
|
||||
WriteStatus(message *StatusMessage)
|
||||
WriteGroups(message OutboundGroupIterator)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
runtimeDebug "runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
)
|
||||
|
||||
func (c *CommandClient) CloseConnections() error {
|
||||
|
||||
@@ -23,6 +23,9 @@ func readLog(reader io.Reader) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if messageLength == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
data := make([]byte, messageLength)
|
||||
_, err = io.ReadFull(reader, data)
|
||||
if err != nil {
|
||||
@@ -32,14 +35,24 @@ func readLog(reader io.Reader) ([]byte, error) {
|
||||
}
|
||||
|
||||
func writeLog(writer io.Writer, message []byte) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint16(len(message)))
|
||||
err := binary.Write(writer, binary.BigEndian, uint8(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(message)
|
||||
err = binary.Write(writer, binary.BigEndian, uint16(len(message)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(message) > 0 {
|
||||
_, err = writer.Write(message)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func writeClearLog(writer io.Writer) error {
|
||||
return binary.Write(writer, binary.BigEndian, uint8(1))
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
var savedLines []string
|
||||
s.access.Lock()
|
||||
@@ -69,6 +82,11 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case <-s.logReset:
|
||||
err = writeClearLog(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case <-done:
|
||||
return nil
|
||||
}
|
||||
@@ -77,12 +95,24 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
|
||||
func (c *CommandClient) handleLogConn(conn net.Conn) {
|
||||
for {
|
||||
message, err := readLog(conn)
|
||||
var messageType uint8
|
||||
err := binary.Read(conn, binary.BigEndian, &messageType)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
}
|
||||
c.handler.WriteLog(string(message))
|
||||
var message []byte
|
||||
switch messageType {
|
||||
case 0:
|
||||
message, err = readLog(conn)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
}
|
||||
c.handler.WriteLog(string(message))
|
||||
case 1:
|
||||
c.handler.ClearLog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,16 @@ type CommandServer struct {
|
||||
handler CommandServerHandler
|
||||
|
||||
access sync.Mutex
|
||||
savedLines *list.List[string]
|
||||
savedLines list.List[string]
|
||||
maxLines int
|
||||
subscriber *observable.Subscriber[string]
|
||||
observer *observable.Observer[string]
|
||||
service *BoxService
|
||||
|
||||
// These channels only work with a single client. if multi-client support is needed, replace with Subscriber/Observer
|
||||
urlTestUpdate chan struct{}
|
||||
modeUpdate chan struct{}
|
||||
logReset chan struct{}
|
||||
}
|
||||
|
||||
type CommandServerHandler interface {
|
||||
@@ -42,11 +44,11 @@ type CommandServerHandler interface {
|
||||
func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServer {
|
||||
server := &CommandServer{
|
||||
handler: handler,
|
||||
savedLines: new(list.List[string]),
|
||||
maxLines: int(maxLines),
|
||||
subscriber: observable.NewSubscriber[string](128),
|
||||
urlTestUpdate: make(chan struct{}, 1),
|
||||
modeUpdate: make(chan struct{}, 1),
|
||||
logReset: make(chan struct{}, 1),
|
||||
}
|
||||
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
||||
return server
|
||||
@@ -56,6 +58,11 @@ func (s *CommandServer) SetService(newService *BoxService) {
|
||||
if newService != nil {
|
||||
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
|
||||
newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
|
||||
s.savedLines.Init()
|
||||
select {
|
||||
case s.logReset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
s.service = newService
|
||||
s.notifyURLTestUpdate()
|
||||
|
||||
@@ -6,13 +6,15 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/memory"
|
||||
)
|
||||
|
||||
type StatusMessage struct {
|
||||
Memory int64
|
||||
MemoryInuse int64
|
||||
Goroutines int32
|
||||
ConnectionsIn int32
|
||||
ConnectionsOut int32
|
||||
@@ -24,10 +26,8 @@ type StatusMessage struct {
|
||||
}
|
||||
|
||||
func (s *CommandServer) readStatus() StatusMessage {
|
||||
var memStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memStats)
|
||||
var message StatusMessage
|
||||
message.Memory = int64(memStats.StackInuse + memStats.HeapInuse + memStats.HeapIdle - memStats.HeapReleased)
|
||||
message.Memory = int64(memory.Inuse())
|
||||
message.Goroutines = int32(runtime.NumGoroutine())
|
||||
message.ConnectionsOut = int32(conntrack.Count())
|
||||
|
||||
|
||||
@@ -4,14 +4,15 @@ import (
|
||||
"math"
|
||||
runtimeDebug "runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
)
|
||||
|
||||
func SetMemoryLimit(enabled bool) {
|
||||
const memoryLimit = 30 * 1024 * 1024
|
||||
const memoryLimit = 45 * 1024 * 1024
|
||||
const memoryLimitGo = memoryLimit / 1.5
|
||||
if enabled {
|
||||
runtimeDebug.SetGCPercent(10)
|
||||
runtimeDebug.SetMemoryLimit(memoryLimit)
|
||||
runtimeDebug.SetMemoryLimit(memoryLimitGo)
|
||||
conntrack.KillerEnabled = true
|
||||
conntrack.MemoryLimit = memoryLimit
|
||||
} else {
|
||||
|
||||
@@ -65,6 +65,17 @@ func (m *platformDefaultInterfaceMonitor) DefaultInterfaceIndex(destination neti
|
||||
return m.defaultInterfaceIndex
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterface(destination netip.Addr) (string, int) {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceName, address.interfaceIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.defaultInterfaceName, m.defaultInterfaceIndex
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -163,25 +163,29 @@ func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) {
|
||||
}
|
||||
|
||||
type ProfileContent struct {
|
||||
Name string
|
||||
Type int32
|
||||
Config string
|
||||
RemotePath string
|
||||
AutoUpdate bool
|
||||
LastUpdated int64
|
||||
Name string
|
||||
Type int32
|
||||
Config string
|
||||
RemotePath string
|
||||
AutoUpdate bool
|
||||
AutoUpdateInterval int32
|
||||
LastUpdated int64
|
||||
}
|
||||
|
||||
func (c *ProfileContent) Encode() []byte {
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.WriteByte(MessageTypeProfileContent)
|
||||
buffer.WriteByte(0)
|
||||
buffer.WriteByte(1)
|
||||
writer := gzip.NewWriter(buffer)
|
||||
rw.WriteVString(writer, c.Name)
|
||||
binary.Write(writer, binary.BigEndian, c.Type)
|
||||
rw.WriteVString(writer, c.Config)
|
||||
if c.Type != ProfileTypeLocal {
|
||||
rw.WriteVString(writer, c.RemotePath)
|
||||
}
|
||||
if c.Type == ProfileTypeRemote {
|
||||
binary.Write(writer, binary.BigEndian, c.AutoUpdate)
|
||||
binary.Write(writer, binary.BigEndian, c.AutoUpdateInterval)
|
||||
binary.Write(writer, binary.BigEndian, c.LastUpdated)
|
||||
}
|
||||
writer.Flush()
|
||||
@@ -202,12 +206,8 @@ func DecodeProfileContent(data []byte) (*ProfileContent, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if version == 0 {
|
||||
reader, err = gzip.NewReader(reader)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "unsupported profile")
|
||||
}
|
||||
} else {
|
||||
reader, err = gzip.NewReader(reader)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "unsupported profile")
|
||||
}
|
||||
var content ProfileContent
|
||||
@@ -228,10 +228,18 @@ func DecodeProfileContent(data []byte) (*ProfileContent, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if content.Type == ProfileTypeRemote || (version == 0 && content.Type != ProfileTypeLocal) {
|
||||
err = binary.Read(reader, binary.BigEndian, &content.AutoUpdate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if version >= 1 {
|
||||
err = binary.Read(reader, binary.BigEndian, &content.AutoUpdateInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = binary.Read(reader, binary.BigEndian, &content.LastUpdated)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -5,9 +5,8 @@ import (
|
||||
"os/user"
|
||||
"strconv"
|
||||
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -46,7 +45,11 @@ func Version() string {
|
||||
}
|
||||
|
||||
func FormatBytes(length int64) string {
|
||||
return humanize.IBytes(uint64(length))
|
||||
return humanize.Bytes(uint64(length))
|
||||
}
|
||||
|
||||
func FormatMemoryBytes(length int64) string {
|
||||
return humanize.MemoryBytes(uint64(length))
|
||||
}
|
||||
|
||||
func ProxyDisplayType(proxyType string) string {
|
||||
|
||||
@@ -4,36 +4,36 @@ go 1.20
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/Dreamacro/clash v1.17.0
|
||||
github.com/caddyserver/certmagic v0.19.2
|
||||
github.com/cloudflare/circl v1.3.3
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-chi/chi/v5 v5.0.10
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a
|
||||
github.com/libdns/alidns v1.0.3
|
||||
github.com/libdns/cloudflare v0.1.0
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.2.0
|
||||
github.com/miekg/dns v1.1.55
|
||||
github.com/miekg/dns v1.1.56
|
||||
github.com/ooni/go-libtor v1.1.8
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a
|
||||
github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2
|
||||
github.com/sagernet/quic-go v0.0.0-20230911082307-390b7c274032
|
||||
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950
|
||||
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab
|
||||
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.2.10-0.20230912050851-1453c7c8c20d
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230911082806-425022bdc92b
|
||||
github.com/sagernet/sing-mux v0.1.3-0.20230908032617-759a1886a400
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248
|
||||
github.com/sagernet/sing v0.2.14-0.20231008040725-e690cb9a7ad2
|
||||
github.com/sagernet/sing-dns v0.1.10
|
||||
github.com/sagernet/sing-mux v0.1.3
|
||||
github.com/sagernet/sing-quic v0.1.3-0.20231008043106-a107947d1ed5
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.4
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641
|
||||
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b
|
||||
github.com/sagernet/sing-tun v0.1.16-0.20231006112722-19cc8b9e81aa
|
||||
github.com/sagernet/sing-vmess v0.1.8
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
|
||||
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||
@@ -42,14 +42,14 @@ require (
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.uber.org/zap v1.25.0
|
||||
go.uber.org/zap v1.26.0
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
|
||||
golang.org/x/net v0.14.0
|
||||
golang.org/x/sys v0.11.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31
|
||||
golang.org/x/net v0.16.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
google.golang.org/grpc v1.57.0
|
||||
google.golang.org/grpc v1.58.2
|
||||
google.golang.org/protobuf v1.31.0
|
||||
howett.net/plist v1.0.0
|
||||
)
|
||||
@@ -57,7 +57,6 @@ require (
|
||||
//replace github.com/sagernet/sing => ../sing
|
||||
|
||||
require (
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
@@ -77,7 +76,7 @@ require (
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
|
||||
@@ -87,10 +86,10 @@ require (
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||
github.com/Dreamacro/clash v1.17.0 h1:LWtp6KcnrCiujY58ufI8pylI+hbCBgSCsLI90EWhpi4=
|
||||
github.com/Dreamacro/clash v1.17.0/go.mod h1:PtcAft7sdsK325BD6uwm8wvhOkMV3TCeED6dfZ/lnfE=
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 h1:JFnwKplz9hj8ubqYjm8HkgZS1Rvz9yW+u/XCNNTxr0k=
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0=
|
||||
github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@@ -23,8 +18,6 @@ github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbe
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
@@ -54,8 +47,8 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d h1:Ka64cclWedOkGzm9M2/XYuwJUdmWRUozmsxW0PyKA3A=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a h1:S33o3djA1nPRd+d/bf7jbbXytXuK/EoXow7+aa76grQ=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
@@ -68,14 +61,19 @@ github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
||||
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
|
||||
github.com/libdns/cloudflare v0.1.0 h1:93WkJaGaiXCe353LHEP36kAWCUw0YjFqwhkBkU2/iic=
|
||||
github.com/libdns/cloudflare v0.1.0/go.mod h1:a44IP6J1YH6nvcNl1PverfJviADgXUnsozR3a7vBKN8=
|
||||
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
|
||||
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||
@@ -87,47 +85,47 @@ github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq5
|
||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a h1:wZHruBxZCsQLXHAozWpnJBL3wJ/XufDpz0qKtgpSnA4=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a/go.mod h1:dNV1ZP9y3qx5ltULeKaQZTZWTLHflgW5DES+Ses7cMI=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59 h1:vN4divY6LYHcYmiTsCHNPmGZtEsEKJzh81LyvgAQfEQ=
|
||||
github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTSWt6hdPrARORfoYvuUczynvRLrueo=
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
|
||||
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 h1:hUz/2mJLgi7l2H36JGpDY+jou9FmI6kAm0ZkU+xPpgE=
|
||||
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab h1:u+xQoi/Yc6bNUvTfrDD6HhGRybn2lzrhf5vmS+wb4Ho=
|
||||
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab/go.mod h1:3akUhSHSVtLuJaYcW5JPepUraBOW06Ibz2HKwaK5rOk=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20230911082307-390b7c274032 h1:J900zKCRGU+0gnPLIj+qXdmun4/AQ3iUmNREJ9fNdHQ=
|
||||
github.com/sagernet/quic-go v0.0.0-20230911082307-390b7c274032/go.mod h1:O4Cj7TmMOvqD6S0XMqJRZfcYzA3m0H0ARbbaJFB0p7A=
|
||||
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 h1:dAe4OIJAtE0nHOzTHhAReQteh3+sa63rvXbuIpbeOTY=
|
||||
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460/go.mod h1:uJGpmJCOcMQqMlHKc3P1Vz6uygmpz4bPeVIoOhdVQnM=
|
||||
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.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.10-0.20230912050851-1453c7c8c20d h1:dWUNsHDX8EMeGj1XCnrVtydy5C+5D+Orc5JjZP7myHg=
|
||||
github.com/sagernet/sing v0.2.10-0.20230912050851-1453c7c8c20d/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA=
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230911082806-425022bdc92b h1:m/UWg2voyb94YCWAMInMXIQ91qQGZ6utPAwKCYrlciI=
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230911082806-425022bdc92b/go.mod h1:Kg98PBJEg/08jsNFtmZWmPomhskn9Ausn50ecNm4M+8=
|
||||
github.com/sagernet/sing-mux v0.1.3-0.20230908032617-759a1886a400 h1:LtpYd5c5AJtUSxjyH4KjUS8HT+2XgilyozjbCq/x3EM=
|
||||
github.com/sagernet/sing-mux v0.1.3-0.20230908032617-759a1886a400/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0 h1:9wHYWxH+fcs01PM2+DylA8LNNY3ElnZykQo9rysng8U=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0/go.mod h1:80fNKP0wnqlu85GZXV1H1vDPC/2t+dQbFggOw4XuFUM=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248 h1:JTFfy/LDmVFEK4KZJEujmC1iO8+aoF4unYhhZZRzRq4=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248/go.mod h1:DOhJc/cLeqRv0wuePrQso+iUmDxOnWF4eT/oMcRzYFw=
|
||||
github.com/sagernet/sing v0.2.14-0.20231008040725-e690cb9a7ad2 h1:fbrU4QJwN6XLLPDwSSLgyUru2niwJRvkpNuHsH51vhs=
|
||||
github.com/sagernet/sing v0.2.14-0.20231008040725-e690cb9a7ad2/go.mod h1:AhNEHu0GXrpqkuzvTwvC8+j2cQUU/dh+zLEmq4C99pg=
|
||||
github.com/sagernet/sing-dns v0.1.10 h1:iIU7nRBlUYj+fF2TaktGIvRiTFFrHwSMedLQsvlTZCI=
|
||||
github.com/sagernet/sing-dns v0.1.10/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c=
|
||||
github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA=
|
||||
github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0=
|
||||
github.com/sagernet/sing-quic v0.1.3-0.20231008043106-a107947d1ed5 h1:ENdA0N/fjXoK27FNIgJyFu4EkiaFAAkO5QNRCCq1LmE=
|
||||
github.com/sagernet/sing-quic v0.1.3-0.20231008043106-a107947d1ed5/go.mod h1:1M7xP4802K9Kz6BQ7LlA7UeCapWvWlH1Htmk2bAqkWc=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+MUhpQnAux728=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641 h1:a8lktNrCWZJisB+nPraW+qB73ZofgPtGmlfqNYcO79g=
|
||||
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641/go.mod h1:+YImslQMLgMQcVgZZ9IK4ue1o/605VSU90amHUcp4hA=
|
||||
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b h1:2ezfJtH5JosiEwJhVa+rimQ6ps/t2+7h+mOzMoiaZnA=
|
||||
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b/go.mod h1:1qkC1L1T2sxnS/NuO6HU72S8TkltV+EXoKGR29m/Yss=
|
||||
github.com/sagernet/sing-tun v0.1.16-0.20231006112722-19cc8b9e81aa h1:lQVFeYJ916CXunKB3JrsOQqCtT16OAitMXw8Z0lRNvY=
|
||||
github.com/sagernet/sing-tun v0.1.16-0.20231006112722-19cc8b9e81aa/go.mod h1:d5kKDDW3I8Tr1OSWNaHXzrnsg1LbOx9sYVx83cHPhm8=
|
||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q=
|
||||
@@ -165,22 +163,22 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
|
||||
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31 h1:9k5exFQKQglLo+RoP+4zMjOFE14P6+vyR0baDAi0Rcs=
|
||||
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
|
||||
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -192,26 +190,26 @@ golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
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.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
|
||||
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
|
||||
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
|
||||
+4
-2
@@ -99,14 +99,16 @@ func (a *myInboundAdapter) Start() error {
|
||||
} else {
|
||||
listenAddrString = listenAddr.String()
|
||||
}
|
||||
a.systemProxy, err = settings.NewSystemProxy(a.ctx, M.ParseSocksaddrHostPort(listenAddrString, listenPort), a.protocol == C.TypeMixed)
|
||||
var systemProxy settings.SystemProxy
|
||||
systemProxy, err = settings.NewSystemProxy(a.ctx, M.ParseSocksaddrHostPort(listenAddrString, listenPort), a.protocol == C.TypeMixed)
|
||||
if err != nil {
|
||||
return E.Cause(err, "initialize system proxy")
|
||||
}
|
||||
err = a.systemProxy.Enable()
|
||||
err = systemProxy.Enable()
|
||||
if err != nil {
|
||||
return E.Cause(err, "set system proxy")
|
||||
}
|
||||
a.systemProxy = systemProxy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/proxyproto"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -34,9 +33,8 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
|
||||
if err == nil {
|
||||
a.logger.Info("tcp server started at ", tcpListener.Addr())
|
||||
}
|
||||
if a.listenOptions.ProxyProtocol {
|
||||
a.logger.Warn("Proxy Protocol is deprecated, see https://sing-box.sagernet.org/deprecated")
|
||||
tcpListener = &proxyproto.Listener{Listener: tcpListener, AcceptNoHeader: a.listenOptions.ProxyProtocolAcceptNoHeader}
|
||||
if a.listenOptions.ProxyProtocol || a.listenOptions.ProxyProtocolAcceptNoHeader {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
}
|
||||
a.tcpListener = tcpListener
|
||||
return tcpListener, err
|
||||
|
||||
+4
-4
@@ -7,14 +7,14 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/congestion"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/qtls"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/hysteria"
|
||||
"github.com/sagernet/sing-quic"
|
||||
hyCC "github.com/sagernet/sing-quic/hysteria2/congestion"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -36,7 +36,7 @@ type Hysteria struct {
|
||||
xplusKey []byte
|
||||
sendBPS uint64
|
||||
recvBPS uint64
|
||||
listener qtls.QUICListener
|
||||
listener qtls.Listener
|
||||
udpAccess sync.RWMutex
|
||||
udpSessionId uint32
|
||||
udpSessions map[uint32]chan *hysteria.UDPMessage
|
||||
@@ -221,7 +221,7 @@ func (h *Hysteria) accept(ctx context.Context, conn quic.Connection) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.SetCongestionControl(hysteria.NewBrutalSender(congestion.ByteCount(serverSendBPS)))
|
||||
conn.SetCongestionControl(hyCC.NewBrutalSender(serverSendBPS, false, nil))
|
||||
go h.udpRecvLoop(conn)
|
||||
for {
|
||||
var stream quic.Stream
|
||||
|
||||
+40
-20
@@ -14,7 +14,8 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/hysteria2"
|
||||
"github.com/sagernet/sing-box/transport/hysteria"
|
||||
"github.com/sagernet/sing-quic/hysteria2"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -25,8 +26,9 @@ var _ adapter.Inbound = (*Hysteria2)(nil)
|
||||
|
||||
type Hysteria2 struct {
|
||||
myInboundAdapter
|
||||
tlsConfig tls.ServerConfig
|
||||
server *hysteria2.Server
|
||||
tlsConfig tls.ServerConfig
|
||||
service *hysteria2.Service[int]
|
||||
userNameList []string
|
||||
}
|
||||
|
||||
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (*Hysteria2, error) {
|
||||
@@ -84,16 +86,14 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||
},
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
server, err := hysteria2.NewServer(hysteria2.ServerOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
SendBPS: uint64(options.UpMbps * 1024 * 1024),
|
||||
ReceiveBPS: uint64(options.DownMbps * 1024 * 1024),
|
||||
SalamanderPassword: salamanderPassword,
|
||||
TLSConfig: tlsConfig,
|
||||
Users: common.Map(options.Users, func(it option.Hysteria2User) hysteria2.User {
|
||||
return hysteria2.User(it)
|
||||
}),
|
||||
service, err := hysteria2.NewService[int](hysteria2.ServiceOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
BrutalDebug: options.BrutalDebug,
|
||||
SendBPS: uint64(options.UpMbps * hysteria.MbpsToBps),
|
||||
ReceiveBPS: uint64(options.DownMbps * hysteria.MbpsToBps),
|
||||
SalamanderPassword: salamanderPassword,
|
||||
TLSConfig: tlsConfig,
|
||||
IgnoreClientBandwidth: options.IgnoreClientBandwidth,
|
||||
Handler: adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, nil),
|
||||
MasqueradeHandler: masqueradeHandler,
|
||||
@@ -101,23 +101,43 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.server = server
|
||||
userList := make([]int, 0, len(options.Users))
|
||||
userNameList := make([]string, 0, len(options.Users))
|
||||
userPasswordList := make([]string, 0, len(options.Users))
|
||||
for index, user := range options.Users {
|
||||
userList = append(userList, index)
|
||||
userNameList = append(userNameList, user.Name)
|
||||
userPasswordList = append(userPasswordList, user.Password)
|
||||
}
|
||||
service.UpdateUsers(userList, userPasswordList)
|
||||
inbound.service = service
|
||||
inbound.userNameList = userNameList
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
metadata.User, _ = auth.UserFromContext[string](ctx)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
}
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
metadata.User, _ = auth.UserFromContext[string](ctx)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
h.logger.InfoContext(ctx, "[", userName, "] inbound packet connection to ", metadata.Destination)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
}
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
@@ -132,13 +152,13 @@ func (h *Hysteria2) Start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.server.Start(packetConn)
|
||||
return h.service.Start(packetConn)
|
||||
}
|
||||
|
||||
func (h *Hysteria2) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.server),
|
||||
common.PtrOrNil(h.service),
|
||||
)
|
||||
}
|
||||
|
||||
+3
-9
@@ -2,7 +2,6 @@ package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/rand"
|
||||
@@ -139,14 +138,9 @@ func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
n.badRequest(ctx, request, E.New("missing naive padding"))
|
||||
return
|
||||
}
|
||||
var authOk bool
|
||||
var userName string
|
||||
authorization := request.Header.Get("Proxy-Authorization")
|
||||
if strings.HasPrefix(authorization, "BASIC ") || strings.HasPrefix(authorization, "Basic ") {
|
||||
userPassword, _ := base64.URLEncoding.DecodeString(authorization[6:])
|
||||
userPswdArr := strings.SplitN(string(userPassword), ":", 2)
|
||||
userName = userPswdArr[0]
|
||||
authOk = n.authenticator.Verify(userPswdArr[0], userPswdArr[1])
|
||||
userName, password, authOk := sHttp.ParseBasicAuth(request.Header.Get("Proxy-Authorization"))
|
||||
if authOk {
|
||||
authOk = n.authenticator.Verify(userName, password)
|
||||
}
|
||||
if !authOk {
|
||||
rejectHTTP(writer, http.StatusProxyAuthRequired)
|
||||
|
||||
@@ -5,7 +5,7 @@ package inbound
|
||||
import (
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing-box/common/qtls"
|
||||
"github.com/sagernet/sing-quic"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
|
||||
+12
-1
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowtls"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
@@ -66,7 +67,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
},
|
||||
HandshakeForServerName: handshakeForServerName,
|
||||
StrictMode: options.StrictMode,
|
||||
Handler: inbound.upstreamContextHandler(),
|
||||
Handler: adapter.NewUpstreamContextHandler(inbound.newConnection, nil, inbound),
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -80,3 +81,13 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
func (h *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *ShadowTLS) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
||||
metadata.User = userName
|
||||
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
}
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
@@ -227,10 +227,3 @@ func (t *trojanTransportHandler) NewConnection(ctx context.Context, conn net.Con
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *trojanTransportHandler) FallbackConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return (*Trojan)(t).fallbackConnection(ctx, conn, adapter.InboundContext{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
}
|
||||
|
||||
+39
-21
@@ -12,7 +12,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/tuic"
|
||||
"github.com/sagernet/sing-quic/tuic"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -25,8 +25,9 @@ var _ adapter.Inbound = (*TUIC)(nil)
|
||||
|
||||
type TUIC struct {
|
||||
myInboundAdapter
|
||||
server *tuic.Server
|
||||
tlsConfig tls.ServerConfig
|
||||
tlsConfig tls.ServerConfig
|
||||
server *tuic.Service[int]
|
||||
userNameList []string
|
||||
}
|
||||
|
||||
func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICInboundOptions) (*TUIC, error) {
|
||||
@@ -38,17 +39,6 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var users []tuic.User
|
||||
for index, user := range options.Users {
|
||||
if user.UUID == "" {
|
||||
return nil, E.New("missing uuid for user ", index)
|
||||
}
|
||||
userUUID, err := uuid.FromString(user.UUID)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "invalid uuid for user ", index)
|
||||
}
|
||||
users = append(users, tuic.User{Name: user.Name, UUID: userUUID, Password: user.Password})
|
||||
}
|
||||
inbound := &TUIC{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeTUIC,
|
||||
@@ -60,11 +50,10 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
}
|
||||
server, err := tuic.NewServer(tuic.ServerOptions{
|
||||
service, err := tuic.NewService[int](tuic.ServiceOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
TLSConfig: tlsConfig,
|
||||
Users: users,
|
||||
CongestionControl: options.CongestionControl,
|
||||
AuthTimeout: time.Duration(options.AuthTimeout),
|
||||
ZeroRTTHandshake: options.ZeroRTTHandshake,
|
||||
@@ -74,23 +63,52 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.server = server
|
||||
var userList []int
|
||||
var userNameList []string
|
||||
var userUUIDList [][16]byte
|
||||
var userPasswordList []string
|
||||
for index, user := range options.Users {
|
||||
if user.UUID == "" {
|
||||
return nil, E.New("missing uuid for user ", index)
|
||||
}
|
||||
userUUID, err := uuid.FromString(user.UUID)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "invalid uuid for user ", index)
|
||||
}
|
||||
userList = append(userList, index)
|
||||
userNameList = append(userNameList, user.Name)
|
||||
userUUIDList = append(userUUIDList, userUUID)
|
||||
userPasswordList = append(userPasswordList, user.Password)
|
||||
}
|
||||
service.UpdateUsers(userList, userUUIDList, userPasswordList)
|
||||
inbound.server = service
|
||||
inbound.userNameList = userNameList
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *TUIC) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
metadata.User, _ = auth.UserFromContext[string](ctx)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
}
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *TUIC) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
metadata.User, _ = auth.UserFromContext[string](ctx)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
h.logger.InfoContext(ctx, "[", userName, "] inbound packet connection to ", metadata.Destination)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
}
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,3 @@ func (t *vlessTransportHandler) NewConnection(ctx context.Context, conn net.Conn
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *vlessTransportHandler) FallbackConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -198,7 +198,3 @@ func (t *vmessTransportHandler) NewConnection(ctx context.Context, conn net.Conn
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *vmessTransportHandler) FallbackConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ nav:
|
||||
- Listen Fields: configuration/shared/listen.md
|
||||
- Dial Fields: configuration/shared/dial.md
|
||||
- TLS: configuration/shared/tls.md
|
||||
- DNS01 Challenge Fields: configuration/shared/dns01_challenge.md
|
||||
- Multiplex: configuration/shared/multiplex.md
|
||||
- V2Ray Transport: configuration/shared/v2ray-transport.md
|
||||
- UDP over TCP: configuration/shared/udp-over-tcp.md
|
||||
@@ -193,6 +194,7 @@ plugins:
|
||||
Shared: 通用
|
||||
Listen Fields: 监听字段
|
||||
Dial Fields: 拨号字段
|
||||
DNS01 Challenge Fields: DNS01 验证字段
|
||||
Multiplex: 多路复用
|
||||
V2Ray Transport: V2Ray 传输层
|
||||
|
||||
|
||||
+8
-6
@@ -17,13 +17,15 @@ type ClashAPIOptions struct {
|
||||
}
|
||||
|
||||
type SelectorOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
Default string `json:"default,omitempty"`
|
||||
Outbounds []string `json:"outbounds"`
|
||||
Default string `json:"default,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
}
|
||||
|
||||
type URLTestOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Interval Duration `json:"interval,omitempty"`
|
||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||
Outbounds []string `json:"outbounds"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Interval Duration `json:"interval,omitempty"`
|
||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
}
|
||||
|
||||
+9
-9
@@ -3,7 +3,7 @@ package option
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
)
|
||||
|
||||
type DebugOptions struct {
|
||||
@@ -13,21 +13,21 @@ type DebugOptions struct {
|
||||
MaxThreads *int `json:"max_threads,omitempty"`
|
||||
PanicOnFault *bool `json:"panic_on_fault,omitempty"`
|
||||
TraceBack string `json:"trace_back,omitempty"`
|
||||
MemoryLimit BytesLength `json:"memory_limit,omitempty"`
|
||||
MemoryLimit MemoryBytes `json:"memory_limit,omitempty"`
|
||||
OOMKiller *bool `json:"oom_killer,omitempty"`
|
||||
}
|
||||
|
||||
type BytesLength int64
|
||||
type MemoryBytes uint64
|
||||
|
||||
func (l BytesLength) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(humanize.IBytes(uint64(l)))
|
||||
func (l MemoryBytes) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(humanize.MemoryBytes(uint64(l)))
|
||||
}
|
||||
|
||||
func (l *BytesLength) UnmarshalJSON(bytes []byte) error {
|
||||
func (l *MemoryBytes) UnmarshalJSON(bytes []byte) error {
|
||||
var valueInteger int64
|
||||
err := json.Unmarshal(bytes, &valueInteger)
|
||||
if err == nil {
|
||||
*l = BytesLength(valueInteger)
|
||||
*l = MemoryBytes(valueInteger)
|
||||
return nil
|
||||
}
|
||||
var valueString string
|
||||
@@ -35,10 +35,10 @@ func (l *BytesLength) UnmarshalJSON(bytes []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parsedValue, err := humanize.ParseBytes(valueString)
|
||||
parsedValue, err := humanize.ParseMemoryBytes(valueString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*l = BytesLength(parsedValue)
|
||||
*l = MemoryBytes(parsedValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
+8
-6
@@ -9,6 +9,7 @@ type Hysteria2InboundOptions struct {
|
||||
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
||||
TLS *InboundTLSOptions `json:"tls,omitempty"`
|
||||
Masquerade string `json:"masquerade,omitempty"`
|
||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||
}
|
||||
|
||||
type Hysteria2Obfs struct {
|
||||
@@ -24,10 +25,11 @@ type Hysteria2User 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"`
|
||||
TLS *OutboundTLSOptions `json:"tls,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"`
|
||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ type SSHOutboundOptions struct {
|
||||
ServerOptions
|
||||
User string `json:"user,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
PrivateKey string `json:"private_key,omitempty"`
|
||||
PrivateKey Listable[string] `json:"private_key,omitempty"`
|
||||
PrivateKeyPath string `json:"private_key_path,omitempty"`
|
||||
PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"`
|
||||
HostKey Listable[string] `json:"host_key,omitempty"`
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ type OutboundTLSOptions struct {
|
||||
MinVersion string `json:"min_version,omitempty"`
|
||||
MaxVersion string `json:"max_version,omitempty"`
|
||||
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
||||
Certificate string `json:"certificate,omitempty"`
|
||||
Certificate Listable[string] `json:"certificate,omitempty"`
|
||||
CertificatePath string `json:"certificate_path,omitempty"`
|
||||
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
||||
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type InboundACMEOptions struct {
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
DataDirectory string `json:"data_directory,omitempty"`
|
||||
@@ -11,9 +17,62 @@ type InboundACMEOptions struct {
|
||||
AlternativeHTTPPort uint16 `json:"alternative_http_port,omitempty"`
|
||||
AlternativeTLSPort uint16 `json:"alternative_tls_port,omitempty"`
|
||||
ExternalAccount *ACMEExternalAccountOptions `json:"external_account,omitempty"`
|
||||
DNS01Challenge *ACMEDNS01ChallengeOptions `json:"dns01_challenge,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEExternalAccountOptions struct {
|
||||
KeyID string `json:"key_id,omitempty"`
|
||||
MACKey string `json:"mac_key,omitempty"`
|
||||
}
|
||||
|
||||
type _ACMEDNS01ChallengeOptions struct {
|
||||
Provider string `json:"provider,omitempty"`
|
||||
AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"`
|
||||
CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"`
|
||||
}
|
||||
|
||||
type ACMEDNS01ChallengeOptions _ACMEDNS01ChallengeOptions
|
||||
|
||||
func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
|
||||
var v any
|
||||
switch o.Provider {
|
||||
case C.DNSProviderAliDNS:
|
||||
v = o.AliDNSOptions
|
||||
case C.DNSProviderCloudflare:
|
||||
v = o.CloudflareOptions
|
||||
default:
|
||||
return nil, E.New("unknown provider type: " + o.Provider)
|
||||
}
|
||||
return MarshallObjects((_ACMEDNS01ChallengeOptions)(o), v)
|
||||
}
|
||||
|
||||
func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_ACMEDNS01ChallengeOptions)(o))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var v any
|
||||
switch o.Provider {
|
||||
case C.DNSProviderAliDNS:
|
||||
v = &o.AliDNSOptions
|
||||
case C.DNSProviderCloudflare:
|
||||
v = &o.CloudflareOptions
|
||||
default:
|
||||
return E.New("unknown provider type: " + o.Provider)
|
||||
}
|
||||
err = UnmarshallExcluded(bytes, (*_ACMEDNS01ChallengeOptions)(o), v)
|
||||
if err != nil {
|
||||
return E.Cause(err, "DNS01 challenge options")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ACMEDNS01AliDNSOptions struct {
|
||||
AccessKeyID string `json:"access_key_id,omitempty"`
|
||||
AccessKeySecret string `json:"access_key_secret,omitempty"`
|
||||
RegionID string `json:"region_id,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEDNS01CloudflareOptions struct {
|
||||
APIToken string `json:"api_token,omitempty"`
|
||||
}
|
||||
|
||||
+27
-28
@@ -2,7 +2,6 @@ package option
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
@@ -50,60 +49,60 @@ func (a *ListenAddress) Build() netip.Addr {
|
||||
return (netip.Addr)(*a)
|
||||
}
|
||||
|
||||
type NetworkList string
|
||||
type NetworkList []string
|
||||
|
||||
func (v *NetworkList) UnmarshalJSON(content []byte) error {
|
||||
var networkList []string
|
||||
err := json.Unmarshal(content, &networkList)
|
||||
var (
|
||||
networkList []string
|
||||
err error
|
||||
)
|
||||
if len(content) > 0 && content[0] != '[' {
|
||||
networkList = make([]string, 1)
|
||||
err = json.Unmarshal(content, &networkList[0])
|
||||
} else {
|
||||
err = json.Unmarshal(content, &networkList)
|
||||
}
|
||||
if err != nil {
|
||||
var networkItem string
|
||||
err = json.Unmarshal(content, &networkItem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkList = []string{networkItem}
|
||||
return err
|
||||
}
|
||||
for _, networkName := range networkList {
|
||||
switch networkName {
|
||||
case N.NetworkTCP, N.NetworkUDP:
|
||||
break
|
||||
default:
|
||||
return E.New("unknown network: " + networkName)
|
||||
return E.Extend(N.ErrUnknownNetwork, networkName)
|
||||
}
|
||||
}
|
||||
*v = NetworkList(strings.Join(networkList, "\n"))
|
||||
*v = networkList
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v NetworkList) Build() []string {
|
||||
if v == "" {
|
||||
if len(v) == 0 {
|
||||
return []string{N.NetworkTCP, N.NetworkUDP}
|
||||
}
|
||||
return strings.Split(string(v), "\n")
|
||||
return v
|
||||
}
|
||||
|
||||
type Listable[T comparable] []T
|
||||
|
||||
func (l Listable[T]) MarshalJSON() ([]byte, error) {
|
||||
arrayList := []T(l)
|
||||
if len(arrayList) == 1 {
|
||||
return json.Marshal(arrayList[0])
|
||||
if len(l) == 1 {
|
||||
return json.Marshal(l[0])
|
||||
}
|
||||
return json.Marshal(arrayList)
|
||||
return json.Marshal([]T(l))
|
||||
}
|
||||
|
||||
func (l *Listable[T]) UnmarshalJSON(content []byte) error {
|
||||
err := json.Unmarshal(content, (*[]T)(l))
|
||||
if err == nil {
|
||||
return nil
|
||||
if len(content) > 0 && content[0] != '[' {
|
||||
var element T
|
||||
err := json.Unmarshal(content, &element)
|
||||
if err == nil {
|
||||
*l = []T{element}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var singleItem T
|
||||
newError := json.Unmarshal(content, &singleItem)
|
||||
if newError != nil {
|
||||
return E.Errors(err, newError)
|
||||
}
|
||||
*l = []T{singleItem}
|
||||
return nil
|
||||
return json.Unmarshal(content, (*[]T)(l))
|
||||
}
|
||||
|
||||
type DomainStrategy dns.DomainStrategy
|
||||
|
||||
+4
-1
@@ -178,6 +178,7 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
|
||||
payload := cachedReader.ReadCached()
|
||||
if payload != nil && !payload.IsEmpty() {
|
||||
_, err := serverConn.Write(payload.Bytes())
|
||||
payload.Release()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -189,10 +190,12 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
|
||||
err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
|
||||
if err != os.ErrInvalid {
|
||||
if err != nil {
|
||||
payload.Release()
|
||||
return err
|
||||
}
|
||||
_, err = payload.ReadOnceFrom(conn)
|
||||
if err != nil && !E.IsTimeout(err) {
|
||||
payload.Release()
|
||||
return E.Cause(err, "read payload")
|
||||
}
|
||||
err = conn.SetReadDeadline(time.Time{})
|
||||
@@ -202,10 +205,10 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
|
||||
}
|
||||
}
|
||||
_, err = serverConn.Write(payload.Bytes())
|
||||
payload.Release()
|
||||
if err != nil {
|
||||
return N.ReportHandshakeFailure(conn, err)
|
||||
}
|
||||
payload.Release()
|
||||
}
|
||||
return bufio.CopyConn(ctx, conn, serverConn)
|
||||
}
|
||||
|
||||
+4
-48
@@ -17,8 +17,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/pires/go-proxyproto"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -33,7 +31,6 @@ type Direct struct {
|
||||
fallbackDelay time.Duration
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
proxyProto uint8
|
||||
}
|
||||
|
||||
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
|
||||
@@ -54,10 +51,9 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
|
||||
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||
dialer: outboundDialer,
|
||||
proxyProto: options.ProxyProtocol,
|
||||
}
|
||||
if options.ProxyProtocol > 2 {
|
||||
return nil, E.New("invalid proxy protocol option: ", options.ProxyProtocol)
|
||||
if options.ProxyProtocol != 0 {
|
||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||
}
|
||||
if options.OverrideAddress != "" && options.OverridePort != 0 {
|
||||
outbound.overrideOption = 1
|
||||
@@ -74,7 +70,6 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
|
||||
|
||||
func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
originDestination := metadata.Destination
|
||||
metadata.Outbound = h.tag
|
||||
metadata.Destination = destination
|
||||
switch h.overrideOption {
|
||||
@@ -94,31 +89,11 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
conn, err := h.dialer.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if h.proxyProto > 0 {
|
||||
source := metadata.Source
|
||||
if !source.IsValid() {
|
||||
source = M.SocksaddrFromNet(conn.LocalAddr())
|
||||
}
|
||||
if originDestination.Addr.Is6() {
|
||||
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
|
||||
}
|
||||
header := proxyproto.HeaderProxyFromAddrs(h.proxyProto, source.TCPAddr(), originDestination.TCPAddr())
|
||||
_, err = header.WriteTo(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, E.Cause(err, "write proxy protocol header")
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
return h.dialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
|
||||
func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
originDestination := metadata.Destination
|
||||
metadata.Outbound = h.tag
|
||||
metadata.Destination = destination
|
||||
switch h.overrideOption {
|
||||
@@ -141,26 +116,7 @@ func (h *Direct) DialParallel(ctx context.Context, network string, destination M
|
||||
} else {
|
||||
domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
||||
}
|
||||
conn, err := N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if h.proxyProto > 0 {
|
||||
source := metadata.Source
|
||||
if !source.IsValid() {
|
||||
source = M.SocksaddrFromNet(conn.LocalAddr())
|
||||
}
|
||||
if originDestination.Addr.Is6() {
|
||||
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
|
||||
}
|
||||
header := proxyproto.HeaderProxyFromAddrs(h.proxyProto, source.TCPAddr(), originDestination.TCPAddr())
|
||||
_, err = header.WriteTo(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, E.Cause(err, "write proxy protocol header")
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
return N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay)
|
||||
}
|
||||
|
||||
func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
|
||||
@@ -8,15 +8,15 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/congestion"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/qtls"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/hysteria"
|
||||
"github.com/sagernet/sing-quic"
|
||||
hyCC "github.com/sagernet/sing-quic/hysteria2/congestion"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -206,7 +206,7 @@ func (h *Hysteria) offerNew(ctx context.Context) (quic.Connection, error) {
|
||||
packetConn.Close()
|
||||
return nil, E.New("remote error: ", serverHello.Message)
|
||||
}
|
||||
quicConn.SetCongestionControl(hysteria.NewBrutalSender(congestion.ByteCount(serverHello.RecvBPS)))
|
||||
quicConn.SetCongestionControl(hyCC.NewBrutalSender(serverHello.RecvBPS, false, nil))
|
||||
h.conn = quicConn
|
||||
h.rawConn = udpConn
|
||||
return quicConn, nil
|
||||
|
||||
@@ -13,7 +13,8 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/hysteria2"
|
||||
"github.com/sagernet/sing-box/transport/hysteria"
|
||||
"github.com/sagernet/sing-quic/hysteria2"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -60,9 +61,11 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||
client, err := hysteria2.NewClient(hysteria2.ClientOptions{
|
||||
Context: ctx,
|
||||
Dialer: outboundDialer,
|
||||
Logger: logger,
|
||||
BrutalDebug: options.BrutalDebug,
|
||||
ServerAddress: options.ServerOptions.Build(),
|
||||
SendBPS: uint64(options.UpMbps * 1024 * 1024),
|
||||
ReceiveBPS: uint64(options.DownMbps * 1024 * 1024),
|
||||
SendBPS: uint64(options.UpMbps * hysteria.MbpsToBps),
|
||||
ReceiveBPS: uint64(options.DownMbps * hysteria.MbpsToBps),
|
||||
SalamanderPassword: salamanderPassword,
|
||||
Password: options.Password,
|
||||
TLSConfig: tlsConfig,
|
||||
|
||||
+28
-9
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/interrupt"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -20,10 +21,12 @@ var (
|
||||
|
||||
type Selector struct {
|
||||
myOutboundAdapter
|
||||
tags []string
|
||||
defaultTag string
|
||||
outbounds map[string]adapter.Outbound
|
||||
selected adapter.Outbound
|
||||
tags []string
|
||||
defaultTag string
|
||||
outbounds map[string]adapter.Outbound
|
||||
selected adapter.Outbound
|
||||
interruptGroup *interrupt.Group
|
||||
interruptExternalConnections bool
|
||||
}
|
||||
|
||||
func NewSelector(router adapter.Router, logger log.ContextLogger, tag string, options option.SelectorOutboundOptions) (*Selector, error) {
|
||||
@@ -35,9 +38,11 @@ func NewSelector(router adapter.Router, logger log.ContextLogger, tag string, op
|
||||
tag: tag,
|
||||
dependencies: options.Outbounds,
|
||||
},
|
||||
tags: options.Outbounds,
|
||||
defaultTag: options.Default,
|
||||
outbounds: make(map[string]adapter.Outbound),
|
||||
tags: options.Outbounds,
|
||||
defaultTag: options.Default,
|
||||
outbounds: make(map[string]adapter.Outbound),
|
||||
interruptGroup: interrupt.NewGroup(),
|
||||
interruptExternalConnections: options.InterruptExistConnections,
|
||||
}
|
||||
if len(outbound.tags) == 0 {
|
||||
return nil, E.New("missing tags")
|
||||
@@ -100,6 +105,9 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
||||
if !loaded {
|
||||
return false
|
||||
}
|
||||
if s.selected == detour {
|
||||
return true
|
||||
}
|
||||
s.selected = detour
|
||||
if s.tag != "" {
|
||||
if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
|
||||
@@ -109,22 +117,33 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
s.interruptGroup.Interrupt(s.interruptExternalConnections)
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Selector) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
return s.selected.DialContext(ctx, network, destination)
|
||||
conn, err := s.selected.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.interruptGroup.NewConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
}
|
||||
|
||||
func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return s.selected.ListenPacket(ctx, destination)
|
||||
conn, err := s.selected.ListenPacket(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
}
|
||||
|
||||
func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
return s.selected.NewConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
return s.selected.NewPacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
|
||||
+4
-181
@@ -4,192 +4,15 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/clashssr/obfs"
|
||||
"github.com/sagernet/sing-box/transport/clashssr/protocol"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
||||
"github.com/Dreamacro/clash/transport/shadowsocks/shadowstream"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*ShadowsocksR)(nil)
|
||||
var _ int = "ShadowsocksR is deprecated and removed in sing-box 1.6.0"
|
||||
|
||||
type ShadowsocksR struct {
|
||||
myOutboundAdapter
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
cipher core.Cipher
|
||||
obfs obfs.Obfs
|
||||
protocol protocol.Protocol
|
||||
}
|
||||
|
||||
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (*ShadowsocksR, error) {
|
||||
logger.Warn("ShadowsocksR is deprecated, see https://sing-box.sagernet.org/deprecated")
|
||||
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound := &ShadowsocksR{
|
||||
myOutboundAdapter: myOutboundAdapter{
|
||||
protocol: C.TypeShadowsocksR,
|
||||
network: options.Network.Build(),
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
dependencies: withDialerDependency(options.DialerOptions),
|
||||
},
|
||||
dialer: outboundDialer,
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
}
|
||||
var cipher string
|
||||
switch options.Method {
|
||||
case "none":
|
||||
cipher = "dummy"
|
||||
default:
|
||||
cipher = options.Method
|
||||
}
|
||||
outbound.cipher, err = core.PickCipher(cipher, nil, options.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
ivSize int
|
||||
key []byte
|
||||
)
|
||||
if cipher == "dummy" {
|
||||
ivSize = 0
|
||||
key = core.Kdf(options.Password, 16)
|
||||
} else {
|
||||
streamCipher, ok := outbound.cipher.(*core.StreamCipher)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s is not none or a supported stream cipher in ssr", cipher)
|
||||
}
|
||||
ivSize = streamCipher.IVSize()
|
||||
key = streamCipher.Key
|
||||
}
|
||||
obfs, obfsOverhead, err := obfs.PickObfs(options.Obfs, &obfs.Base{
|
||||
Host: options.Server,
|
||||
Port: int(options.ServerPort),
|
||||
Key: key,
|
||||
IVSize: ivSize,
|
||||
Param: options.ObfsParam,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize obfs")
|
||||
}
|
||||
protocol, err := protocol.PickProtocol(options.Protocol, &protocol.Base{
|
||||
Key: key,
|
||||
Overhead: obfsOverhead,
|
||||
Param: options.ProtocolParam,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize protocol")
|
||||
}
|
||||
outbound.obfs = obfs
|
||||
outbound.protocol = protocol
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (h *ShadowsocksR) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Outbound = h.tag
|
||||
metadata.Destination = destination
|
||||
switch network {
|
||||
case N.NetworkTCP:
|
||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||
conn, err := h.dialer.DialContext(ctx, network, h.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn = h.cipher.StreamConn(h.obfs.StreamConn(conn))
|
||||
writeIv, err := conn.(*shadowstream.Conn).ObtainWriteIV()
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
conn = h.protocol.StreamConn(conn, writeIv)
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, E.Cause(err, "write request")
|
||||
}
|
||||
return conn, nil
|
||||
case N.NetworkUDP:
|
||||
conn, err := h.ListenPacket(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bufio.NewBindPacketConn(conn, destination), nil
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ShadowsocksR) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Outbound = h.tag
|
||||
metadata.Destination = destination
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetConn := h.cipher.PacketConn(bufio.NewUnbindPacketConn(outConn))
|
||||
packetConn = h.protocol.PacketConn(packetConn)
|
||||
packetConn = &ssPacketConn{packetConn, outConn.RemoteAddr()}
|
||||
return packetConn, nil
|
||||
}
|
||||
|
||||
func (h *ShadowsocksR) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *ShadowsocksR) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
type ssPacketConn struct {
|
||||
net.PacketConn
|
||||
rAddr net.Addr
|
||||
}
|
||||
|
||||
func (spc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return spc.PacketConn.WriteTo(packet[3:], spc.rAddr)
|
||||
}
|
||||
|
||||
func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
n, _, e := spc.PacketConn.ReadFrom(b)
|
||||
if e != nil {
|
||||
return 0, nil, e
|
||||
}
|
||||
|
||||
addr := socks5.SplitAddr(b[:n])
|
||||
if addr == nil {
|
||||
return 0, nil, errors.New("parse addr error")
|
||||
}
|
||||
|
||||
udpAddr := addr.UDPAddr()
|
||||
if udpAddr == nil {
|
||||
return 0, nil, errors.New("parse addr error")
|
||||
}
|
||||
|
||||
copy(b, b[len(addr):])
|
||||
return n - len(addr), udpAddr, e
|
||||
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -12,5 +12,5 @@ import (
|
||||
)
|
||||
|
||||
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) {
|
||||
return nil, E.New(`ShadowsocksR is not included in this build, rebuild with -tags with_shadowsocksr`)
|
||||
return nil, E.New("ShadowsocksR is deprecated and removed in sing-box 1.6.0")
|
||||
}
|
||||
|
||||
+4
-3
@@ -8,6 +8,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -76,10 +77,10 @@ func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
if options.Password != "" {
|
||||
outbound.authMethod = append(outbound.authMethod, ssh.Password(options.Password))
|
||||
}
|
||||
if options.PrivateKey != "" || options.PrivateKeyPath != "" {
|
||||
if len(options.PrivateKey) > 0 || options.PrivateKeyPath != "" {
|
||||
var privateKey []byte
|
||||
if options.PrivateKey != "" {
|
||||
privateKey = []byte(options.PrivateKey)
|
||||
if len(options.PrivateKey) > 0 {
|
||||
privateKey = []byte(strings.Join(options.PrivateKey, "\n"))
|
||||
} else {
|
||||
var err error
|
||||
privateKey, err = os.ReadFile(os.ExpandEnv(options.PrivateKeyPath))
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/tuic"
|
||||
"github.com/sagernet/sing-quic/tuic"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
+73
-35
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/interrupt"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -30,12 +31,13 @@ var (
|
||||
|
||||
type URLTest struct {
|
||||
myOutboundAdapter
|
||||
ctx context.Context
|
||||
tags []string
|
||||
link string
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
group *URLTestGroup
|
||||
ctx context.Context
|
||||
tags []string
|
||||
link string
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
group *URLTestGroup
|
||||
interruptExternalConnections bool
|
||||
}
|
||||
|
||||
func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
||||
@@ -47,11 +49,12 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
tag: tag,
|
||||
dependencies: options.Outbounds,
|
||||
},
|
||||
ctx: ctx,
|
||||
tags: options.Outbounds,
|
||||
link: options.URL,
|
||||
interval: time.Duration(options.Interval),
|
||||
tolerance: options.Tolerance,
|
||||
ctx: ctx,
|
||||
tags: options.Outbounds,
|
||||
link: options.URL,
|
||||
interval: time.Duration(options.Interval),
|
||||
tolerance: options.Tolerance,
|
||||
interruptExternalConnections: options.InterruptExistConnections,
|
||||
}
|
||||
if len(outbound.tags) == 0 {
|
||||
return nil, E.New("missing tags")
|
||||
@@ -75,7 +78,7 @@ func (s *URLTest) Start() error {
|
||||
}
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
s.group = NewURLTestGroup(s.ctx, s.router, s.logger, outbounds, s.link, s.interval, s.tolerance)
|
||||
s.group = NewURLTestGroup(s.ctx, s.router, s.logger, outbounds, s.link, s.interval, s.tolerance, s.interruptExternalConnections)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -111,7 +114,7 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
|
||||
outbound := s.group.Select(network)
|
||||
conn, err := outbound.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
return s.group.interruptGroup.NewConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
}
|
||||
s.logger.ErrorContext(ctx, err)
|
||||
s.group.history.DeleteURLTestHistory(outbound.Tag())
|
||||
@@ -123,7 +126,7 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne
|
||||
outbound := s.group.Select(N.NetworkUDP)
|
||||
conn, err := outbound.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
return s.group.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
}
|
||||
s.logger.ErrorContext(ctx, err)
|
||||
s.group.history.DeleteURLTestHistory(outbound.Tag())
|
||||
@@ -131,10 +134,12 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne
|
||||
}
|
||||
|
||||
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
return NewConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
return NewPacketConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
@@ -144,23 +149,36 @@ func (s *URLTest) InterfaceUpdated() {
|
||||
}
|
||||
|
||||
type URLTestGroup struct {
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger log.Logger
|
||||
outbounds []adapter.Outbound
|
||||
link string
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
history *urltest.HistoryStorage
|
||||
checking atomic.Bool
|
||||
pauseManager pause.Manager
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger log.Logger
|
||||
outbounds []adapter.Outbound
|
||||
link string
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
history *urltest.HistoryStorage
|
||||
checking atomic.Bool
|
||||
pauseManager pause.Manager
|
||||
selectedOutboundTCP adapter.Outbound
|
||||
selectedOutboundUDP adapter.Outbound
|
||||
interruptGroup *interrupt.Group
|
||||
interruptExternalConnections bool
|
||||
|
||||
access sync.Mutex
|
||||
ticker *time.Ticker
|
||||
close chan struct{}
|
||||
}
|
||||
|
||||
func NewURLTestGroup(ctx context.Context, router adapter.Router, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16) *URLTestGroup {
|
||||
func NewURLTestGroup(
|
||||
ctx context.Context,
|
||||
router adapter.Router,
|
||||
logger log.Logger,
|
||||
outbounds []adapter.Outbound,
|
||||
link string,
|
||||
interval time.Duration,
|
||||
tolerance uint16,
|
||||
interruptExternalConnections bool,
|
||||
) *URLTestGroup {
|
||||
if interval == 0 {
|
||||
interval = C.DefaultURLTestInterval
|
||||
}
|
||||
@@ -175,16 +193,18 @@ func NewURLTestGroup(ctx context.Context, router adapter.Router, logger log.Logg
|
||||
history = urltest.NewHistoryStorage()
|
||||
}
|
||||
return &URLTestGroup{
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
outbounds: outbounds,
|
||||
link: link,
|
||||
interval: interval,
|
||||
tolerance: tolerance,
|
||||
history: history,
|
||||
close: make(chan struct{}),
|
||||
pauseManager: pause.ManagerFromContext(ctx),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
outbounds: outbounds,
|
||||
link: link,
|
||||
interval: interval,
|
||||
tolerance: tolerance,
|
||||
history: history,
|
||||
close: make(chan struct{}),
|
||||
pauseManager: pause.ManagerFromContext(ctx),
|
||||
interruptGroup: interrupt.NewGroup(),
|
||||
interruptExternalConnections: interruptExternalConnections,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,5 +349,23 @@ func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (ma
|
||||
})
|
||||
}
|
||||
b.Wait()
|
||||
g.performUpdateCheck()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) performUpdateCheck() {
|
||||
outbound := g.Select(N.NetworkTCP)
|
||||
var updated bool
|
||||
if outbound != g.selectedOutboundTCP {
|
||||
g.selectedOutboundTCP = outbound
|
||||
updated = true
|
||||
}
|
||||
outbound = g.Select(N.NetworkUDP)
|
||||
if outbound != g.selectedOutboundUDP {
|
||||
g.selectedOutboundUDP = outbound
|
||||
updated = true
|
||||
}
|
||||
if updated {
|
||||
g.interruptGroup.Interrupt(g.interruptExternalConnections)
|
||||
}
|
||||
}
|
||||
|
||||
+23
-4
@@ -12,8 +12,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/geoip"
|
||||
"github.com/sagernet/sing-box/common/geosite"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
@@ -602,6 +602,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
}
|
||||
return nil
|
||||
}
|
||||
conntrack.KillerCheck()
|
||||
metadata.Network = N.NetworkTCP
|
||||
switch metadata.Destination.Fqdn {
|
||||
case mux.Destination.Fqdn:
|
||||
@@ -693,6 +694,11 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
metadata.DestinationAddresses = addresses
|
||||
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
|
||||
}
|
||||
if metadata.Destination.IsIPv4() {
|
||||
metadata.IPVersion = 4
|
||||
} else if metadata.Destination.IsIPv6() {
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
ctx, matchedRule, detour, err := r.match(ctx, &metadata, r.defaultOutboundForConnection)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -738,6 +744,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
conntrack.KillerCheck()
|
||||
metadata.Network = N.NetworkUDP
|
||||
|
||||
if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) {
|
||||
@@ -805,6 +812,11 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
metadata.DestinationAddresses = addresses
|
||||
r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
|
||||
}
|
||||
if metadata.Destination.IsIPv4() {
|
||||
metadata.IPVersion = 4
|
||||
} else if metadata.Destination.IsIPv6() {
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
ctx, matchedRule, detour, err := r.match(ctx, &metadata, r.defaultOutboundForPacketConnection)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -915,13 +927,20 @@ func (r *Router) AutoDetectInterfaceFunc() control.Func {
|
||||
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
|
||||
return r.platformInterface.AutoDetectInterfaceControl()
|
||||
} else {
|
||||
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||
if C.IsLinux {
|
||||
return r.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
|
||||
interfaceName, interfaceIndex = r.InterfaceMonitor().DefaultInterface(remoteAddr)
|
||||
if interfaceIndex == -1 {
|
||||
err = tun.ErrNoRoute
|
||||
}
|
||||
} else {
|
||||
return "", r.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
||||
interfaceIndex = r.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
||||
if interfaceIndex == -1 {
|
||||
err = tun.ErrNoRoute
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -133,11 +133,11 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
||||
addrs, err := r.dnsClient.Lookup(ctx, transport, domain, strategy)
|
||||
if len(addrs) > 0 {
|
||||
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(addrs), " "))
|
||||
} else {
|
||||
} else if err != nil {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
|
||||
if err == nil {
|
||||
err = dns.RCodeNameError
|
||||
}
|
||||
} else {
|
||||
r.dnsLogger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result")
|
||||
err = dns.RCodeNameError
|
||||
}
|
||||
return addrs, err
|
||||
}
|
||||
|
||||
@@ -173,7 +173,11 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
|
||||
},
|
||||
}
|
||||
defer httpClient.CloseIdleConnections()
|
||||
response, err := httpClient.Get(downloadURL)
|
||||
request, err := http.NewRequest("GET", downloadURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := httpClient.Do(request.WithContext(r.ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -221,7 +225,11 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
|
||||
},
|
||||
}
|
||||
defer httpClient.CloseIdleConnections()
|
||||
response, err := httpClient.Get(downloadURL)
|
||||
request, err := http.NewRequest("GET", downloadURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := httpClient.Do(request.WithContext(r.ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+16
-19
@@ -36,7 +36,6 @@ const (
|
||||
ImageHysteria2 = "tobyxdd/hysteria:v2"
|
||||
ImageNginx = "nginx:stable"
|
||||
ImageShadowTLS = "ghcr.io/ihciah/shadow-tls:latest"
|
||||
ImageShadowsocksR = "teddysun/shadowsocks-r:latest"
|
||||
ImageXRayCore = "teddysun/xray:latest"
|
||||
ImageShadowsocksLegacy = "mritd/shadowsocks:latest"
|
||||
ImageTUICServer = "kilvn/tuic-server:latest"
|
||||
@@ -54,7 +53,6 @@ var allImages = []string{
|
||||
ImageHysteria2,
|
||||
ImageNginx,
|
||||
ImageShadowTLS,
|
||||
ImageShadowsocksR,
|
||||
ImageXRayCore,
|
||||
ImageShadowsocksLegacy,
|
||||
ImageTUICServer,
|
||||
@@ -366,7 +364,7 @@ func testLargeDataWithConn(t *testing.T, port uint16, cc func() (net.Conn, error
|
||||
}
|
||||
|
||||
func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.PacketConn, error)) error {
|
||||
return testLargeDataWithPacketConnSize(t, port, 1024, pcc)
|
||||
return testLargeDataWithPacketConnSize(t, port, 1500, pcc)
|
||||
}
|
||||
|
||||
func testLargeDataWithPacketConnSize(t *testing.T, port uint16, chunkSize int, pcc func() (net.PacketConn, error)) error {
|
||||
@@ -385,24 +383,23 @@ func testLargeDataWithPacketConnSize(t *testing.T, port uint16, chunkSize int, p
|
||||
hashMap := map[int][]byte{}
|
||||
mux := sync.Mutex{}
|
||||
for i := 0; i < times; i++ {
|
||||
go func(idx int) {
|
||||
buf := make([]byte, chunkSize)
|
||||
if _, err := rand.Read(buf[1:]); err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
buf[0] = byte(idx)
|
||||
buf := make([]byte, chunkSize)
|
||||
if _, err := rand.Read(buf[1:]); err != nil {
|
||||
t.Log(err.Error())
|
||||
continue
|
||||
}
|
||||
buf[0] = byte(i)
|
||||
|
||||
hash := md5.Sum(buf)
|
||||
mux.Lock()
|
||||
hashMap[idx] = hash[:]
|
||||
mux.Unlock()
|
||||
hash := md5.Sum(buf)
|
||||
mux.Lock()
|
||||
hashMap[i] = hash[:]
|
||||
mux.Unlock()
|
||||
|
||||
if _, err := pc.WriteTo(buf, addr); err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
}(i)
|
||||
if _, err := pc.WriteTo(buf, addr); err != nil {
|
||||
t.Log(err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
return hashMap, nil
|
||||
|
||||
+26
-25
@@ -6,23 +6,24 @@ require github.com/sagernet/sing-box v0.0.0
|
||||
|
||||
replace github.com/sagernet/sing-box => ../
|
||||
|
||||
replace github.com/sagernet/sing-quic => ../../sing-quic
|
||||
|
||||
require (
|
||||
github.com/docker/docker v24.0.5+incompatible
|
||||
github.com/docker/docker v24.0.6+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/sagernet/sing v0.2.10-0.20230912051334-9163f4486b0d
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5-0.20230907005610-126234728ca0
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.4-0.20230907005906-5d2917b29248
|
||||
github.com/sagernet/sing v0.2.13-0.20231001063649-e0ec961fb1ab
|
||||
github.com/sagernet/sing-quic v0.1.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.4
|
||||
github.com/spyzhov/ajson v0.9.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.uber.org/goleak v1.2.1
|
||||
golang.org/x/net v0.14.0
|
||||
golang.org/x/net v0.15.0
|
||||
)
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.17.0 // indirect
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
@@ -32,7 +33,6 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.10 // indirect
|
||||
github.com/go-chi/cors v1.2.1 // indirect
|
||||
@@ -44,14 +44,16 @@ require (
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/libdns/alidns v1.0.3 // indirect
|
||||
github.com/libdns/cloudflare v0.1.0 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||
github.com/mholt/acmez v1.2.0 // indirect
|
||||
github.com/miekg/dns v1.1.55 // indirect
|
||||
github.com/miekg/dns v1.1.56 // indirect
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
@@ -60,22 +62,21 @@ require (
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20230911082307-390b7c274032 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20231001051131-0fc736a289bb // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230911082806-425022bdc92b // indirect
|
||||
github.com/sagernet/sing-mux v0.1.3-0.20230908032617-759a1886a400 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.10 // indirect
|
||||
github.com/sagernet/sing-mux v0.1.3 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.12-0.20230821065522-7545dc2d5641 // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.8-0.20230907010359-161fb0ac716b // indirect
|
||||
github.com/sagernet/sing-tun v0.1.14 // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.8 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
|
||||
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
|
||||
@@ -87,17 +88,17 @@ require (
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.25.0 // indirect
|
||||
go.uber.org/zap v1.26.0 // indirect
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
|
||||
golang.org/x/crypto v0.13.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
|
||||
google.golang.org/grpc v1.57.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/grpc v1.58.2 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.4.0 // indirect
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user