mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 02:27:19 +10:00
Compare commits
30 Commits
v1.5.0-rc.
...
v1.5.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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
|
||||
}
|
||||
|
||||
@@ -10,6 +10,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/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
|
||||
@@ -120,18 +121,18 @@ func mergeTLSInboundOptions(options *option.InboundTLSOptions) *option.InboundTL
|
||||
}
|
||||
if options.CertificatePath != "" {
|
||||
if content, err := os.ReadFile(options.CertificatePath); err == nil {
|
||||
options.Certificate = strings.Split(string(content), "\n")
|
||||
options.Certificate = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
if options.KeyPath != "" {
|
||||
if content, err := os.ReadFile(options.KeyPath); err == nil {
|
||||
options.Key = strings.Split(string(content), "\n")
|
||||
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 = strings.Split(string(content), "\n")
|
||||
options.ECH.Key = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,13 +145,13 @@ func mergeTLSOutboundOptions(options *option.OutboundTLSOptions) *option.Outboun
|
||||
}
|
||||
if options.CertificatePath != "" {
|
||||
if content, err := os.ReadFile(options.CertificatePath); err == nil {
|
||||
options.Certificate = strings.Split(string(content), "\n")
|
||||
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 = strings.Split(string(content), "\n")
|
||||
options.ECH.Config = trimStringArray(strings.Split(string(content), "\n"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,9 +160,15 @@ func mergeTLSOutboundOptions(options *option.OutboundTLSOptions) *option.Outboun
|
||||
|
||||
func mergeSSHOutboundOptions(options option.SSHOutboundOptions) option.SSHOutboundOptions {
|
||||
if options.PrivateKeyPath != "" {
|
||||
if content, err := os.ReadFile(options.PrivateKeyPath); err == nil {
|
||||
options.PrivateKey = strings.Split(string(content), "\n")
|
||||
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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -90,7 +90,10 @@ func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Con
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,8 +1,96 @@
|
||||
#### 1.5.0-rc.2
|
||||
#### 1.5.2
|
||||
|
||||
* Our [Apple tvOS client](/installation/clients/sft) is now available in the App Store 🍎
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.5.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.5.0-rc.1
|
||||
#### 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
|
||||
|
||||
@@ -17,7 +105,7 @@ This command also parses path resources that appear in the configuration file an
|
||||
configuration, such as TLS certificates or SSH private keys.
|
||||
|
||||
```
|
||||
Merge configuration
|
||||
Merge configurations
|
||||
|
||||
Usage:
|
||||
sing-box merge [output] [flags]
|
||||
@@ -103,7 +191,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**:
|
||||
|
||||
|
||||
@@ -233,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
|
||||
|
||||
|
||||
@@ -228,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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
31
docs/installation/clients/sft.zh.md
Normal file
31
docs/installation/clients/sft.zh.md
Normal file
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
19
go.mod
19
go.mod
@@ -27,14 +27,14 @@ require (
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2
|
||||
github.com/sagernet/quic-go v0.0.0-20230919101909-0cc6c5dcecee
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.2.12-0.20230921162020-494f88c9b8bf
|
||||
github.com/sagernet/sing-dns v0.1.10-0.20230921024525-fc3e4c051ccd
|
||||
github.com/sagernet/sing v0.2.13
|
||||
github.com/sagernet/sing-dns v0.1.10
|
||||
github.com/sagernet/sing-mux v0.1.3
|
||||
github.com/sagernet/sing-quic v0.1.1-0.20230922040527-541e66a4a16d
|
||||
github.com/sagernet/sing-quic v0.1.2
|
||||
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.13-0.20230922035004-b6d323004edd
|
||||
github.com/sagernet/sing-tun v0.1.15
|
||||
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
|
||||
@@ -46,17 +46,18 @@ require (
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.uber.org/zap v1.26.0
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925
|
||||
golang.org/x/crypto v0.13.0
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
||||
golang.org/x/net v0.15.0
|
||||
golang.org/x/sys v0.12.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.58.1
|
||||
google.golang.org/grpc v1.58.2
|
||||
google.golang.org/protobuf v1.31.0
|
||||
howett.net/plist v1.0.0
|
||||
)
|
||||
|
||||
//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
|
||||
|
||||
38
go.sum
38
go.sum
@@ -114,22 +114,22 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
|
||||
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.12-0.20230921162020-494f88c9b8bf h1:O8jjYmCExZbsgmqZEHyn05C/7ZzD0SLTG21QNcYoP2Q=
|
||||
github.com/sagernet/sing v0.2.12-0.20230921162020-494f88c9b8bf/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg=
|
||||
github.com/sagernet/sing-dns v0.1.10-0.20230921024525-fc3e4c051ccd h1:czixTtZijtdR4bMQYT/0LZy1x5ouiaDBi742YE0zudU=
|
||||
github.com/sagernet/sing-dns v0.1.10-0.20230921024525-fc3e4c051ccd/go.mod h1:y76ieq1uilVg6fe5wJWqM2oKjdrn4q0lY1nwAZ86ok0=
|
||||
github.com/sagernet/sing v0.2.13 h1:ohczGKWP+Yn3zlQXSvFn+6EKSELGggBi66D5rqpYRQ0=
|
||||
github.com/sagernet/sing v0.2.13/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.1-0.20230922040527-541e66a4a16d h1:CzdkTdId4Pa0oY7UrhMIiMh+cY01Rh+B3BXMXLt7REY=
|
||||
github.com/sagernet/sing-quic v0.1.1-0.20230922040527-541e66a4a16d/go.mod h1:Inf4N8ihB4+lB5ZDo++GXbq4rKusL7f1s67v7IVeL2I=
|
||||
github.com/sagernet/sing-quic v0.1.2 h1:+u9CRf0KHi5HgXmJ3eB0CtqpWXtF0lx2QlWq+ZFZ+XY=
|
||||
github.com/sagernet/sing-quic v0.1.2/go.mod h1:H1TX0/y9UUM43wyaLQ+qjg2+o901ibYtwWX2rWG+a3o=
|
||||
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.13-0.20230922035004-b6d323004edd h1:R7DOvvQfYMmsdIr43wCQGHregky4/FGcvOEcIuxEt5w=
|
||||
github.com/sagernet/sing-tun v0.1.13-0.20230922035004-b6d323004edd/go.mod h1:7IGpNWXuP0TnxkUiGJRJjewFLquTOhLw1RtfNgxzjJI=
|
||||
github.com/sagernet/sing-tun v0.1.15 h1:XfHQD/dhCCQeespPojB4gRhADI1A/4mSLLJCnh5qUnQ=
|
||||
github.com/sagernet/sing-tun v0.1.15/go.mod h1:zgRoBAtOM24QXx0IKYFEnuTtXPq1Z4rDYRWkP8kJm+g=
|
||||
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=
|
||||
@@ -175,16 +175,16 @@ go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0Eq
|
||||
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.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
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.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
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=
|
||||
@@ -196,10 +196,10 @@ 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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.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.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||
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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
@@ -214,8 +214,8 @@ golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvY
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
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.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58=
|
||||
google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
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=
|
||||
|
||||
@@ -116,11 +116,13 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
@@ -131,8 +133,10 @@ func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn,
|
||||
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)
|
||||
}
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -88,11 +88,13 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
@@ -103,8 +105,10 @@ func (h *TUIC) newPacketConnection(ctx context.Context, conn N.PacketConn, metad
|
||||
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)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -694,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
|
||||
@@ -807,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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
12
test/go.mod
12
test/go.mod
@@ -12,8 +12,8 @@ require (
|
||||
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.12-0.20230921162020-494f88c9b8bf
|
||||
github.com/sagernet/sing-quic v0.1.1-0.20230922040527-541e66a4a16d
|
||||
github.com/sagernet/sing v0.2.12
|
||||
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
|
||||
@@ -73,12 +73,12 @@ require (
|
||||
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-20230919101909-0cc6c5dcecee // 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.10-0.20230921024525-fc3e4c051ccd // 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.13-0.20230922035004-b6d323004edd // 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
|
||||
@@ -101,7 +101,7 @@ require (
|
||||
golang.org/x/time v0.3.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.1 // 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
|
||||
|
||||
20
test/go.sum
20
test/go.sum
@@ -123,16 +123,16 @@ github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTS
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
|
||||
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-20230919101909-0cc6c5dcecee h1:ykuhl9jCS638N+jw1vC9AvT9bbQn6xRNScP2FWPV9dM=
|
||||
github.com/sagernet/quic-go v0.0.0-20230919101909-0cc6c5dcecee/go.mod h1:0CfhWwZAeXGYM9+Nkkw1zcQtFHQC8KWjbpeDv7pu8iw=
|
||||
github.com/sagernet/quic-go v0.0.0-20231001051131-0fc736a289bb h1:jlrVCepGBoob4QsPChIbe1j0d/lZSJkyVj2ukX3D4PE=
|
||||
github.com/sagernet/quic-go v0.0.0-20231001051131-0fc736a289bb/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.12-0.20230921162020-494f88c9b8bf h1:O8jjYmCExZbsgmqZEHyn05C/7ZzD0SLTG21QNcYoP2Q=
|
||||
github.com/sagernet/sing v0.2.12-0.20230921162020-494f88c9b8bf/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg=
|
||||
github.com/sagernet/sing-dns v0.1.10-0.20230921024525-fc3e4c051ccd h1:czixTtZijtdR4bMQYT/0LZy1x5ouiaDBi742YE0zudU=
|
||||
github.com/sagernet/sing-dns v0.1.10-0.20230921024525-fc3e4c051ccd/go.mod h1:y76ieq1uilVg6fe5wJWqM2oKjdrn4q0lY1nwAZ86ok0=
|
||||
github.com/sagernet/sing v0.2.12 h1:wwdLm3c4qvU4hW8tNtadh60V5z2FGlDZSYYGRzHhD74=
|
||||
github.com/sagernet/sing v0.2.12/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg=
|
||||
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-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
|
||||
@@ -141,8 +141,8 @@ github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+M
|
||||
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.13-0.20230922035004-b6d323004edd h1:R7DOvvQfYMmsdIr43wCQGHregky4/FGcvOEcIuxEt5w=
|
||||
github.com/sagernet/sing-tun v0.1.13-0.20230922035004-b6d323004edd/go.mod h1:7IGpNWXuP0TnxkUiGJRJjewFLquTOhLw1RtfNgxzjJI=
|
||||
github.com/sagernet/sing-tun v0.1.14 h1:Vsval4r78kngCsZsz0FExT6p6akiUeRuiVXjfgnN3Ok=
|
||||
github.com/sagernet/sing-tun v0.1.14/go.mod h1:Z2WibDUoQh/3wwFCfkIzIG0n/NlAlPuEbLTq3rD1aQY=
|
||||
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=
|
||||
@@ -250,8 +250,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58=
|
||||
google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
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=
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -176,7 +177,11 @@ func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) err
|
||||
var listener net.ListenConfig
|
||||
listener.Control = control.Append(listener.Control, control.BindToInterface(t.router.InterfaceFinder(), iface.Name, iface.Index))
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
packetConn, err := listener.ListenPacket(t.ctx, "udp4", "0.0.0.0:68")
|
||||
listenAddr := "0.0.0.0:68"
|
||||
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||
listenAddr = "255.255.255.255:68"
|
||||
}
|
||||
packetConn, err := listener.ListenPacket(t.ctx, "udp4", listenAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -64,15 +64,15 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
if request.URL.Path != s.path {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
return
|
||||
}
|
||||
if request.Method != http.MethodPost {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad method: ", request.Method))
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad method: ", request.Method))
|
||||
return
|
||||
}
|
||||
if ct := request.Header.Get("Content-Type"); !strings.HasPrefix(ct, "application/grpc") {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad content type: ", ct))
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad content type: ", ct))
|
||||
return
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/grpc")
|
||||
@@ -85,18 +85,11 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
conn.CloseWrapper()
|
||||
}
|
||||
|
||||
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||
conn := v2rayhttp.NewHTTPConn(request.Body, writer)
|
||||
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||
if fErr == nil {
|
||||
return
|
||||
} else if fErr == os.ErrInvalid {
|
||||
fErr = nil
|
||||
}
|
||||
func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||
if statusCode > 0 {
|
||||
writer.WriteHeader(statusCode)
|
||||
}
|
||||
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||
s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr))
|
||||
}
|
||||
|
||||
func (s *Server) Serve(listener net.Listener) error {
|
||||
|
||||
@@ -85,15 +85,15 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
host := request.Host
|
||||
if len(s.host) > 0 && !common.Contains(s.host, host) {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.New("bad host: ", host))
|
||||
s.invalidRequest(writer, request, http.StatusBadRequest, E.New("bad host: ", host))
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(request.URL.Path, s.path) {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
return
|
||||
}
|
||||
if request.Method != s.method {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad method: ", request.Method))
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad method: ", request.Method))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
requestBody = buf.NewSize(contentLength)
|
||||
_, err := requestBody.ReadFullFrom(request.Body, contentLength)
|
||||
if err != nil {
|
||||
s.fallbackRequest(request.Context(), writer, request, 0, E.Cause(err, "read request"))
|
||||
s.invalidRequest(writer, request, 0, E.Cause(err, "read request"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -121,14 +121,15 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.(http.Flusher).Flush()
|
||||
conn, reader, err := h.Hijack()
|
||||
if err != nil {
|
||||
s.fallbackRequest(request.Context(), writer, request, 0, E.Cause(err, "hijack conn"))
|
||||
s.invalidRequest(writer, request, 0, E.Cause(err, "hijack conn"))
|
||||
return
|
||||
}
|
||||
if cacheLen := reader.Reader.Buffered(); cacheLen > 0 {
|
||||
cache := buf.NewSize(cacheLen)
|
||||
_, err = cache.ReadFullFrom(reader.Reader, cacheLen)
|
||||
if err != nil {
|
||||
s.fallbackRequest(request.Context(), writer, request, 0, E.Cause(err, "read cache"))
|
||||
conn.Close()
|
||||
s.invalidRequest(writer, request, 0, E.Cause(err, "read cache"))
|
||||
return
|
||||
}
|
||||
conn = bufio.NewCachedConn(conn, cache)
|
||||
@@ -148,18 +149,11 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||
conn := NewHTTPConn(request.Body, writer)
|
||||
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||
if fErr == nil {
|
||||
return
|
||||
} else if fErr == os.ErrInvalid {
|
||||
fErr = nil
|
||||
}
|
||||
func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||
if statusCode > 0 {
|
||||
writer.WriteHeader(statusCode)
|
||||
}
|
||||
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||
s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr))
|
||||
}
|
||||
|
||||
func (s *Server) Serve(listener net.Listener) error {
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
@@ -69,7 +68,7 @@ var upgrader = websocket.Upgrader{
|
||||
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
if s.maxEarlyData == 0 || s.earlyDataHeaderName != "" {
|
||||
if request.URL.Path != s.path {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -83,7 +82,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
earlyDataStr := request.URL.RequestURI()[len(s.path):]
|
||||
earlyData, err = base64.RawURLEncoding.DecodeString(earlyDataStr)
|
||||
} else {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
s.invalidRequest(writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -93,12 +92,12 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.Cause(err, "decode early data"))
|
||||
s.invalidRequest(writer, request, http.StatusBadRequest, E.Cause(err, "decode early data"))
|
||||
return
|
||||
}
|
||||
wsConn, err := upgrader.Upgrade(writer, request, nil)
|
||||
if err != nil {
|
||||
s.fallbackRequest(request.Context(), writer, request, 0, E.Cause(err, "upgrade websocket connection"))
|
||||
s.invalidRequest(writer, request, 0, E.Cause(err, "upgrade websocket connection"))
|
||||
return
|
||||
}
|
||||
var metadata M.Metadata
|
||||
@@ -110,18 +109,11 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||
}
|
||||
|
||||
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||
conn := v2rayhttp.NewHTTPConn(request.Body, writer)
|
||||
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||
if fErr == nil {
|
||||
return
|
||||
} else if fErr == os.ErrInvalid {
|
||||
fErr = nil
|
||||
}
|
||||
func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||
if statusCode > 0 {
|
||||
writer.WriteHeader(statusCode)
|
||||
}
|
||||
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||
s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr))
|
||||
}
|
||||
|
||||
func (s *Server) Network() []string {
|
||||
|
||||
Reference in New Issue
Block a user