mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72dbf2e2b4 | ||
|
|
46c318c6fe | ||
|
|
05bb1b88c3 | ||
|
|
5176ea9fe0 | ||
|
|
36d349acd2 | ||
|
|
4feee983b5 | ||
|
|
9b12e3e389 | ||
|
|
afd3464216 | ||
|
|
8b64446274 | ||
|
|
28aa4c4d1f | ||
|
|
0be3cdc8fb | ||
|
|
f8be484019 | ||
|
|
35f03f092d | ||
|
|
c3d7401ead | ||
|
|
4db7eb9d9e | ||
|
|
fd4efd6104 | ||
|
|
19a35ec6a4 | ||
|
|
2012c0ca1e | ||
|
|
187421c754 | ||
|
|
b3fb86d415 | ||
|
|
88fafd4e30 | ||
|
|
8056932f9c | ||
|
|
c8af003bfc | ||
|
|
4999441a85 | ||
|
|
09b001e795 | ||
|
|
3b3a251008 |
@@ -117,8 +117,6 @@ nfpms:
|
||||
dst: /etc/systemd/system/sing-box@.service
|
||||
- src: LICENSE
|
||||
dst: /usr/share/licenses/sing-box/LICENSE
|
||||
scripts:
|
||||
postremove: release/config/postremove.sh
|
||||
source:
|
||||
enabled: false
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}.source'
|
||||
|
||||
5
LICENSE
5
LICENSE
@@ -11,4 +11,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, no derivative work may use the name or imply association
|
||||
with this application without prior consent.
|
||||
|
||||
@@ -25,4 +25,7 @@ GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, no derivative work may use the name or imply association
|
||||
with this application without prior consent.
|
||||
```
|
||||
96
box.go
96
box.go
@@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/inbound"
|
||||
@@ -31,18 +30,25 @@ type Box struct {
|
||||
outbounds []adapter.Outbound
|
||||
logFactory log.Factory
|
||||
logger log.ContextLogger
|
||||
logFile *os.File
|
||||
preServices map[string]adapter.Service
|
||||
postServices map[string]adapter.Service
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func New(ctx context.Context, options option.Options, platformInterface platform.Interface) (*Box, error) {
|
||||
createdAt := time.Now()
|
||||
type Options struct {
|
||||
option.Options
|
||||
Context context.Context
|
||||
PlatformInterface platform.Interface
|
||||
}
|
||||
|
||||
func New(options Options) (*Box, error) {
|
||||
ctx := options.Context
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
createdAt := time.Now()
|
||||
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
||||
applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
|
||||
|
||||
var needClashAPI bool
|
||||
var needV2RayAPI bool
|
||||
if experimentalOptions.ClashAPI != nil && experimentalOptions.ClashAPI.ExternalController != "" {
|
||||
@@ -51,60 +57,20 @@ func New(ctx context.Context, options option.Options, platformInterface platform
|
||||
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
||||
needV2RayAPI = true
|
||||
}
|
||||
|
||||
logOptions := common.PtrValueOrDefault(options.Log)
|
||||
|
||||
var logFactory log.Factory
|
||||
var observableLogFactory log.ObservableFactory
|
||||
var logFile *os.File
|
||||
var logWriter io.Writer
|
||||
if logOptions.Disabled {
|
||||
observableLogFactory = log.NewNOPFactory()
|
||||
logFactory = observableLogFactory
|
||||
} else {
|
||||
switch logOptions.Output {
|
||||
case "":
|
||||
if platformInterface != nil {
|
||||
logWriter = io.Discard
|
||||
} else {
|
||||
logWriter = os.Stdout
|
||||
}
|
||||
case "stderr":
|
||||
logWriter = os.Stderr
|
||||
case "stdout":
|
||||
logWriter = os.Stdout
|
||||
default:
|
||||
var err error
|
||||
logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logWriter = logFile
|
||||
}
|
||||
logFormatter := log.Formatter{
|
||||
BaseTime: createdAt,
|
||||
DisableColors: logOptions.DisableColor || logFile != nil,
|
||||
DisableTimestamp: !logOptions.Timestamp && logFile != nil,
|
||||
FullTimestamp: logOptions.Timestamp,
|
||||
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
||||
}
|
||||
if needClashAPI {
|
||||
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, platformInterface)
|
||||
logFactory = observableLogFactory
|
||||
} else {
|
||||
logFactory = log.NewFactory(logFormatter, logWriter, platformInterface)
|
||||
}
|
||||
if logOptions.Level != "" {
|
||||
logLevel, err := log.ParseLevel(logOptions.Level)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse log level")
|
||||
}
|
||||
logFactory.SetLevel(logLevel)
|
||||
} else {
|
||||
logFactory.SetLevel(log.LevelTrace)
|
||||
}
|
||||
var defaultLogWriter io.Writer
|
||||
if options.PlatformInterface != nil {
|
||||
defaultLogWriter = io.Discard
|
||||
}
|
||||
logFactory, err := log.New(log.Options{
|
||||
Options: common.PtrValueOrDefault(options.Log),
|
||||
Observable: needClashAPI,
|
||||
DefaultWriter: defaultLogWriter,
|
||||
BaseTime: createdAt,
|
||||
PlatformWriter: options.PlatformInterface,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create log factory")
|
||||
}
|
||||
|
||||
router, err := route.NewRouter(
|
||||
ctx,
|
||||
logFactory,
|
||||
@@ -112,7 +78,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
|
||||
common.PtrValueOrDefault(options.DNS),
|
||||
common.PtrValueOrDefault(options.NTP),
|
||||
options.Inbounds,
|
||||
platformInterface,
|
||||
options.PlatformInterface,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse route options")
|
||||
@@ -132,7 +98,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
|
||||
router,
|
||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||
inboundOptions,
|
||||
platformInterface,
|
||||
options.PlatformInterface,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||
@@ -169,7 +135,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
|
||||
preServices := make(map[string]adapter.Service)
|
||||
postServices := make(map[string]adapter.Service)
|
||||
if needClashAPI {
|
||||
clashServer, err := experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI))
|
||||
clashServer, err := experimental.NewClashServer(router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create clash api server")
|
||||
}
|
||||
@@ -191,7 +157,6 @@ func New(ctx context.Context, options option.Options, platformInterface platform
|
||||
createdAt: createdAt,
|
||||
logFactory: logFactory,
|
||||
logger: logFactory.Logger(),
|
||||
logFile: logFile,
|
||||
preServices: preServices,
|
||||
postServices: postServices,
|
||||
done: make(chan struct{}),
|
||||
@@ -312,7 +277,7 @@ func (s *Box) Close() error {
|
||||
}
|
||||
for i, out := range s.outbounds {
|
||||
errors = E.Append(errors, common.Close(out), func(err error) error {
|
||||
return E.Cause(err, "close inbound/", out.Type(), "[", i, "]")
|
||||
return E.Cause(err, "close outbound/", out.Type(), "[", i, "]")
|
||||
})
|
||||
}
|
||||
if err := common.Close(s.router); err != nil {
|
||||
@@ -330,11 +295,6 @@ func (s *Box) Close() error {
|
||||
return E.Cause(err, "close log factory")
|
||||
})
|
||||
}
|
||||
if s.logFile != nil {
|
||||
errors = E.Append(errors, s.logFile.Close(), func(err error) error {
|
||||
return E.Cause(err, "close log file")
|
||||
})
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,10 @@ func check() error {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
instance, err := box.New(ctx, options, nil)
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
})
|
||||
if err == nil {
|
||||
instance.Close()
|
||||
}
|
||||
|
||||
@@ -127,7 +127,10 @@ func create() (*box.Box, context.CancelFunc, error) {
|
||||
options.Log.DisableColor = true
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
instance, err := box.New(ctx, options, nil)
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
})
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, nil, E.Cause(err, "create service")
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -27,7 +25,7 @@ func createPreStartedClient() (*box.Box, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instance, err := box.New(context.Background(), options, nil)
|
||||
instance, err := box.New(box.Options{Options: options})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create service")
|
||||
}
|
||||
|
||||
@@ -14,10 +14,16 @@ var (
|
||||
)
|
||||
|
||||
func Count() int {
|
||||
if !Enabled {
|
||||
return 0
|
||||
}
|
||||
return openConnection.Len()
|
||||
}
|
||||
|
||||
func List() []io.Closer {
|
||||
if !Enabled {
|
||||
return nil
|
||||
}
|
||||
connAccess.RLock()
|
||||
defer connAccess.RUnlock()
|
||||
connList := make([]io.Closer, 0, openConnection.Len())
|
||||
@@ -28,6 +34,9 @@ func List() []io.Closer {
|
||||
}
|
||||
|
||||
func Close() {
|
||||
if !Enabled {
|
||||
return
|
||||
}
|
||||
connAccess.Lock()
|
||||
defer connAccess.Unlock()
|
||||
for element := openConnection.Front(); element != nil; element = element.Next() {
|
||||
|
||||
@@ -154,9 +154,9 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkUDP:
|
||||
if !address.IsIPv6() {
|
||||
return d.udpDialer4.DialContext(ctx, network, address.String())
|
||||
return trackConn(d.udpDialer4.DialContext(ctx, network, address.String()))
|
||||
} else {
|
||||
return d.udpDialer6.DialContext(ctx, network, address.String())
|
||||
return trackConn(d.udpDialer6.DialContext(ctx, network, address.String()))
|
||||
}
|
||||
}
|
||||
if !address.IsIPv6() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
@@ -68,11 +69,11 @@ func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := N.ListenSerial(ctx, d.dialer, destination, addresses)
|
||||
conn, destinationAddress, err := N.ListenSerial(ctx, d.dialer, destination, addresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewResolvePacketConn(ctx, d.router, d.strategy, conn), nil
|
||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, M.SocksaddrFrom(destinationAddress, destination.Port)), nil
|
||||
}
|
||||
|
||||
func (d *ResolveDialer) Upstream() any {
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func NewResolvePacketConn(ctx context.Context, router adapter.Router, strategy dns.DomainStrategy, conn net.PacketConn) N.NetPacketConn {
|
||||
if udpConn, ok := conn.(*net.UDPConn); ok {
|
||||
return &ResolveUDPConn{udpConn, ctx, router, strategy}
|
||||
} else {
|
||||
return &ResolvePacketConn{conn, ctx, router, strategy}
|
||||
}
|
||||
}
|
||||
|
||||
type ResolveUDPConn struct {
|
||||
*net.UDPConn
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
strategy dns.DomainStrategy
|
||||
}
|
||||
|
||||
func (w *ResolveUDPConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||
n, addr, err := w.ReadFromUDPAddrPort(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, err
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
return M.SocksaddrFromNetIP(addr), nil
|
||||
}
|
||||
|
||||
func (w *ResolveUDPConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
if destination.IsFqdn() {
|
||||
addresses, err := w.router.Lookup(w.ctx, destination.Fqdn, w.strategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return common.Error(w.UDPConn.WriteToUDPAddrPort(buffer.Bytes(), M.SocksaddrFrom(addresses[0], destination.Port).AddrPort()))
|
||||
}
|
||||
return common.Error(w.UDPConn.WriteToUDPAddrPort(buffer.Bytes(), destination.AddrPort()))
|
||||
}
|
||||
|
||||
func (w *ResolveUDPConn) Upstream() any {
|
||||
return w.UDPConn
|
||||
}
|
||||
|
||||
type ResolvePacketConn struct {
|
||||
net.PacketConn
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
strategy dns.DomainStrategy
|
||||
}
|
||||
|
||||
func (w *ResolvePacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||
_, addr, err := buffer.ReadPacketFrom(w)
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, err
|
||||
}
|
||||
return M.SocksaddrFromNet(addr), err
|
||||
}
|
||||
|
||||
func (w *ResolvePacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
if destination.IsFqdn() {
|
||||
addresses, err := w.router.Lookup(w.ctx, destination.Fqdn, w.strategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return common.Error(w.WriteTo(buffer.Bytes(), M.SocksaddrFrom(addresses[0], destination.Port).UDPAddr()))
|
||||
}
|
||||
return common.Error(w.WriteTo(buffer.Bytes(), destination.UDPAddr()))
|
||||
}
|
||||
|
||||
func (w *ResolvePacketConn) Upstream() any {
|
||||
return w.PacketConn
|
||||
}
|
||||
@@ -124,8 +124,9 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
|
||||
binary.BigEndian.PutUint64(hello.SessionId, uint64(nowTime.Unix()))
|
||||
|
||||
hello.SessionId[0] = 1
|
||||
hello.SessionId[1] = 7
|
||||
hello.SessionId[2] = 5
|
||||
hello.SessionId[1] = 8
|
||||
hello.SessionId[2] = 0
|
||||
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
|
||||
copy(hello.SessionId[8:], e.shortID[:])
|
||||
|
||||
if debug.Enabled {
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
#### 1.2.3
|
||||
|
||||
* Introducing our [new Android client application](/installation/clients/sfa)
|
||||
* Improve UDP domain destination NAT
|
||||
* Update reality protocol
|
||||
* Fix TTL calculation for DNS response
|
||||
* Fix v2ray HTTP transport compatibility
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
#### 1.2.2
|
||||
|
||||
* Accept `any` outbound in dns rule **1**
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
*1*:
|
||||
|
||||
Now you can use the `any` outbound rule to match server address queries instead of filling in all server domains
|
||||
to `domain` rule.
|
||||
|
||||
#### 1.2.1
|
||||
|
||||
* Fix missing default host in v2ray http transport`s request
|
||||
* Flush DNS cache for macOS when tun start/close
|
||||
* Fix tun's DNS hijacking compatibility with systemd-resolved
|
||||
|
||||
#### 1.2.0
|
||||
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
@@ -232,6 +232,8 @@ Invert match result.
|
||||
|
||||
Match outbound.
|
||||
|
||||
`any` can be used as a value to match any outbound.
|
||||
|
||||
#### server
|
||||
|
||||
==Required==
|
||||
@@ -254,18 +256,4 @@ Disable cache and save cache in this query.
|
||||
|
||||
#### rules
|
||||
|
||||
Included default rules.
|
||||
|
||||
#### invert
|
||||
|
||||
Invert match result.
|
||||
|
||||
#### server
|
||||
|
||||
==Required==
|
||||
|
||||
Tag of the target dns server.
|
||||
|
||||
#### disable_cache
|
||||
|
||||
Disable cache and save cache in this query.
|
||||
Included default rules.
|
||||
@@ -231,6 +231,8 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
匹配出站。
|
||||
|
||||
`any` 可作为值用于匹配任意出站。
|
||||
|
||||
#### server
|
||||
|
||||
==必填==
|
||||
@@ -253,18 +255,4 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
#### rules
|
||||
|
||||
包括的默认规则。
|
||||
|
||||
#### invert
|
||||
|
||||
反选匹配结果。
|
||||
|
||||
#### server
|
||||
|
||||
==必填==
|
||||
|
||||
目标 DNS 服务器的标签。
|
||||
|
||||
#### disable_cache
|
||||
|
||||
在此查询中禁用缓存。
|
||||
包括的默认规则。
|
||||
@@ -107,8 +107,7 @@ Enforce strict routing rules when `auto_route` is enabled:
|
||||
* Let unsupported network unreachable
|
||||
* Route all connections to tun
|
||||
|
||||
It prevents address leaks and makes DNS hijacking work on Android and Linux with systemd-resolved, but your device will
|
||||
not be accessible by others.
|
||||
It prevents address leaks and makes DNS hijacking work on Android, but your device will not be accessible by others.
|
||||
|
||||
*In Windows*:
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ tun 接口的 IPv6 前缀。
|
||||
* 让不支持的网络无法到达
|
||||
* 将所有连接路由到 tun
|
||||
|
||||
它可以防止地址泄漏,并使 DNS 劫持在 Android 和使用 systemd-resolved 的 Linux 上工作,但你的设备将无法其他设备被访问。
|
||||
它可以防止地址泄漏,并使 DNS 劫持在 Android 上工作,但你的设备将无法其他设备被访问。
|
||||
|
||||
*在 Windows 中*:
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#### Install
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SagerNet/sing-box
|
||||
git clone -b main https://github.com/SagerNet/sing-box
|
||||
cd sing-box
|
||||
./release/local/install_go.sh # skip if you have go1.19 already installed
|
||||
./release/local/install_go.sh # skip if you have golang already installed
|
||||
./release/local/install.sh
|
||||
```
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#### 安装
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SagerNet/sing-box
|
||||
git clone -b main https://github.com/SagerNet/sing-box
|
||||
cd sing-box
|
||||
./release/local/install_go.sh # 如果已安装 go1.19 则跳过
|
||||
./release/local/install_go.sh # 如果已安装 golang 则跳过
|
||||
./release/local/install.sh
|
||||
```
|
||||
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
"disable_cache": true
|
||||
},
|
||||
{
|
||||
"domain": "mydomain.com",
|
||||
"outbound": "any",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"geosite": "cn",
|
||||
"server": "local"
|
||||
}
|
||||
|
||||
@@ -9,10 +9,6 @@ the public internet.
|
||||
|
||||
`auto-route` cannot automatically hijack DNS requests when Android's `Private DNS` enabled or `strict_route` disabled.
|
||||
|
||||
##### on Linux
|
||||
|
||||
`auto-route` cannot automatically hijack DNS requests with `systemd-resolved` enabled and `strict_route` disabled.
|
||||
|
||||
#### System proxy
|
||||
|
||||
##### on Linux
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
|
||||
`auto-route` 无法自动劫持 DNS 请求如果 `私人 DNS` 开启或 `strict_route` 禁用。
|
||||
|
||||
##### Linux
|
||||
|
||||
`auto-route` 无法自动劫持 DNS 请求如果 `systemd-resolved` 开启且 `strict_route` 禁用。
|
||||
|
||||
#### 系统代理
|
||||
|
||||
##### Linux
|
||||
|
||||
@@ -25,4 +25,7 @@ GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, no derivative work may use the name or imply association
|
||||
with this application without prior consent.
|
||||
```
|
||||
|
||||
@@ -25,4 +25,7 @@ GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, no derivative work may use the name or imply association
|
||||
with this application without prior consent.
|
||||
```
|
||||
|
||||
16
docs/installation/clients/sfa.md
Normal file
16
docs/installation/clients/sfa.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# SFA
|
||||
|
||||
Experimental Android client for sing-box.
|
||||
|
||||
#### Requirements
|
||||
|
||||
* Android 5.0+
|
||||
|
||||
#### Download
|
||||
|
||||
* [AppCenter](https://install.appcenter.ms/users/nekohasekai/apps/sfa/distribution_groups/publictest)
|
||||
|
||||
#### Note
|
||||
|
||||
* Working directory is at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory)
|
||||
* User Agent is `SFA/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
|
||||
16
docs/installation/clients/sfa.zh.md
Normal file
16
docs/installation/clients/sfa.zh.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# SFA
|
||||
|
||||
实验性的 Android sing-box 客户端。
|
||||
|
||||
#### 要求
|
||||
|
||||
* Android 5.0+
|
||||
|
||||
#### 下载
|
||||
|
||||
* [AppCenter](https://install.appcenter.ms/users/nekohasekai/apps/sfa/distribution_groups/publictest)
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录)
|
||||
* 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
@@ -1,6 +1,6 @@
|
||||
# SFI
|
||||
|
||||
Experimental official iOS client for sing-box.
|
||||
Experimental iOS client for sing-box.
|
||||
|
||||
#### Requirements
|
||||
|
||||
@@ -11,9 +11,10 @@ Experimental official iOS client for sing-box.
|
||||
|
||||
* [TestFlight](https://testflight.apple.com/join/c6ylui2j)
|
||||
|
||||
#### Limit
|
||||
#### Note
|
||||
|
||||
* `system` tun stack not working
|
||||
* `system` tun stack not working on iOS
|
||||
* User Agent is `SFI/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
|
||||
|
||||
#### Privacy policy
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SFI
|
||||
|
||||
实验性的官方 iOS sing-box 客户端。
|
||||
实验性的 iOS sing-box 客户端。
|
||||
|
||||
#### 要求
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
|
||||
* [TestFlight](https://testflight.apple.com/join/c6ylui2j)
|
||||
|
||||
#### 限制
|
||||
#### 注意事项
|
||||
|
||||
* `system` tun stack 不工作
|
||||
* `system` tun stack 在 iOS 不工作
|
||||
* 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)`
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
@@ -46,6 +44,7 @@ func clientConnect(sharedDirectory string) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (c *CommandClient) Connect() error {
|
||||
common.Close(c.conn)
|
||||
conn, err := clientConnect(c.sharedDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
@@ -10,6 +8,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/observable"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
@@ -57,7 +56,10 @@ func (s *CommandServer) Start() error {
|
||||
}
|
||||
|
||||
func (s *CommandServer) Close() error {
|
||||
return s.listener.Close()
|
||||
return common.Close(
|
||||
s.listener,
|
||||
s.observer,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *CommandServer) loopConnection(listener net.Listener) {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
//go:build linux || darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
@@ -15,3 +18,36 @@ func parseConfig(configContent string) (option.Options, error) {
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func CheckConfig(configContent string) error {
|
||||
options, err := parseConfig(configContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
})
|
||||
if err == nil {
|
||||
instance.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func FormatConfig(configContent string) (string, error) {
|
||||
options, err := parseConfig(configContent)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
json.NewEncoder(&buffer)
|
||||
encoder := json.NewEncoder(&buffer)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
241
experimental/libbox/http.go
Normal file
241
experimental/libbox/http.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
"github.com/sagernet/sing/protocol/socks/socks5"
|
||||
)
|
||||
|
||||
type HTTPClient interface {
|
||||
RestrictedTLS()
|
||||
ModernTLS()
|
||||
PinnedTLS12()
|
||||
PinnedSHA256(sumHex string)
|
||||
TrySocks5(port int32)
|
||||
KeepAlive()
|
||||
NewRequest() HTTPRequest
|
||||
Close()
|
||||
}
|
||||
|
||||
type HTTPRequest interface {
|
||||
SetURL(link string) error
|
||||
SetMethod(method string)
|
||||
SetHeader(key string, value string)
|
||||
SetContent(content []byte)
|
||||
SetContentString(content string)
|
||||
RandomUserAgent()
|
||||
SetUserAgent(userAgent string)
|
||||
Execute() (HTTPResponse, error)
|
||||
}
|
||||
|
||||
type HTTPResponse interface {
|
||||
GetContent() ([]byte, error)
|
||||
GetContentString() (string, error)
|
||||
WriteTo(path string) error
|
||||
}
|
||||
|
||||
var (
|
||||
_ HTTPClient = (*httpClient)(nil)
|
||||
_ HTTPRequest = (*httpRequest)(nil)
|
||||
_ HTTPResponse = (*httpResponse)(nil)
|
||||
)
|
||||
|
||||
type httpClient struct {
|
||||
tls tls.Config
|
||||
client http.Client
|
||||
transport http.Transport
|
||||
}
|
||||
|
||||
func NewHTTPClient() HTTPClient {
|
||||
client := new(httpClient)
|
||||
client.client.Timeout = C.TCPTimeout
|
||||
client.client.Transport = &client.transport
|
||||
client.transport.TLSClientConfig = &client.tls
|
||||
client.transport.DisableKeepAlives = true
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *httpClient) ModernTLS() {
|
||||
c.tls.MinVersion = tls.VersionTLS12
|
||||
c.tls.CipherSuites = common.Map(tls.CipherSuites(), func(it *tls.CipherSuite) uint16 { return it.ID })
|
||||
}
|
||||
|
||||
func (c *httpClient) RestrictedTLS() {
|
||||
c.tls.MinVersion = tls.VersionTLS13
|
||||
c.tls.CipherSuites = common.Map(common.Filter(tls.CipherSuites(), func(it *tls.CipherSuite) bool {
|
||||
return common.Contains(it.SupportedVersions, uint16(tls.VersionTLS13))
|
||||
}), func(it *tls.CipherSuite) uint16 {
|
||||
return it.ID
|
||||
})
|
||||
}
|
||||
|
||||
func (c *httpClient) PinnedTLS12() {
|
||||
c.tls.MinVersion = tls.VersionTLS12
|
||||
c.tls.MaxVersion = tls.VersionTLS12
|
||||
}
|
||||
|
||||
func (c *httpClient) PinnedSHA256(sumHex string) {
|
||||
c.tls.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
for _, rawCert := range rawCerts {
|
||||
certSum := sha256.Sum256(rawCert)
|
||||
if sumHex == hex.EncodeToString(certSum[:]) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return E.New("pinned sha256 sum mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *httpClient) TrySocks5(port int32) {
|
||||
dialer := new(net.Dialer)
|
||||
c.transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
for {
|
||||
socksConn, err := dialer.DialContext(ctx, "tcp", "127.0.0.1:"+strconv.Itoa(int(port)))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
_, err = socks.ClientHandshake5(socksConn, socks5.CommandConnect, M.ParseSocksaddr(addr), "", "")
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
//nolint:staticcheck
|
||||
return socksConn, err
|
||||
}
|
||||
return dialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *httpClient) KeepAlive() {
|
||||
c.transport.ForceAttemptHTTP2 = true
|
||||
c.transport.DisableKeepAlives = false
|
||||
}
|
||||
|
||||
func (c *httpClient) NewRequest() HTTPRequest {
|
||||
req := &httpRequest{httpClient: c}
|
||||
req.request = http.Request{
|
||||
Method: "GET",
|
||||
Header: http.Header{},
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *httpClient) Close() {
|
||||
c.transport.CloseIdleConnections()
|
||||
}
|
||||
|
||||
type httpRequest struct {
|
||||
*httpClient
|
||||
request http.Request
|
||||
}
|
||||
|
||||
func (r *httpRequest) SetURL(link string) (err error) {
|
||||
r.request.URL, err = url.Parse(link)
|
||||
if r.request.URL.User != nil {
|
||||
user := r.request.URL.User.Username()
|
||||
password, _ := r.request.URL.User.Password()
|
||||
r.request.SetBasicAuth(user, password)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *httpRequest) SetMethod(method string) {
|
||||
r.request.Method = method
|
||||
}
|
||||
|
||||
func (r *httpRequest) SetHeader(key string, value string) {
|
||||
r.request.Header.Set(key, value)
|
||||
}
|
||||
|
||||
func (r *httpRequest) RandomUserAgent() {
|
||||
r.request.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%54, rand.Int()%2))
|
||||
}
|
||||
|
||||
func (r *httpRequest) SetUserAgent(userAgent string) {
|
||||
r.request.Header.Set("User-Agent", userAgent)
|
||||
}
|
||||
|
||||
func (r *httpRequest) SetContent(content []byte) {
|
||||
buffer := bytes.Buffer{}
|
||||
buffer.Write(content)
|
||||
r.request.Body = io.NopCloser(bytes.NewReader(buffer.Bytes()))
|
||||
r.request.ContentLength = int64(len(content))
|
||||
}
|
||||
|
||||
func (r *httpRequest) SetContentString(content string) {
|
||||
r.SetContent([]byte(content))
|
||||
}
|
||||
|
||||
func (r *httpRequest) Execute() (HTTPResponse, error) {
|
||||
response, err := r.client.Do(&r.request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpResp := &httpResponse{Response: response}
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, errors.New(httpResp.errorString())
|
||||
}
|
||||
return httpResp, nil
|
||||
}
|
||||
|
||||
type httpResponse struct {
|
||||
*http.Response
|
||||
|
||||
getContentOnce sync.Once
|
||||
content []byte
|
||||
contentError error
|
||||
}
|
||||
|
||||
func (h *httpResponse) errorString() string {
|
||||
content, err := h.GetContentString()
|
||||
if err != nil {
|
||||
return fmt.Sprint("HTTP ", h.Status)
|
||||
}
|
||||
return fmt.Sprint("HTTP ", h.Status, ": ", content)
|
||||
}
|
||||
|
||||
func (h *httpResponse) GetContent() ([]byte, error) {
|
||||
h.getContentOnce.Do(func() {
|
||||
defer h.Body.Close()
|
||||
h.content, h.contentError = io.ReadAll(h.Body)
|
||||
})
|
||||
return h.content, h.contentError
|
||||
}
|
||||
|
||||
func (h *httpResponse) GetContentString() (string, error) {
|
||||
content, err := h.GetContent()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func (h *httpResponse) WriteTo(path string) error {
|
||||
defer h.Body.Close()
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return common.Error(bufio.Copy(file, h.Body))
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux || darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import "github.com/sagernet/sing/common"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux || darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import "github.com/sagernet/sing-box/option"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux || darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux || darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
@@ -30,7 +28,11 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
instance, err := box.New(ctx, options, &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()})
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
PlatformInterface: &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()},
|
||||
})
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, E.Cause(err, "create service")
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux || darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux || darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
|
||||
22
go.mod
22
go.mod
@@ -14,29 +14,29 @@ require (
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.1.0
|
||||
github.com/miekg/dns v1.1.52
|
||||
github.com/miekg/dns v1.1.53
|
||||
github.com/ooni/go-libtor v1.1.7
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||
github.com/sagernet/reality v0.0.0-20230323230523-5fa25e693e7f
|
||||
github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286
|
||||
github.com/sagernet/sing-dns v0.1.4
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.0
|
||||
github.com/sagernet/sing-tun v0.1.3-0.20230323073325-35d565af6515
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab
|
||||
github.com/sagernet/sing-vmess v0.1.3
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.2
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.uber.org/atomic v1.10.0
|
||||
@@ -44,8 +44,8 @@ require (
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/net v0.8.0
|
||||
golang.org/x/sys v0.6.0
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/sys v0.7.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde
|
||||
google.golang.org/grpc v1.54.0
|
||||
google.golang.org/protobuf v1.30.0
|
||||
@@ -64,7 +64,7 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
@@ -84,7 +84,7 @@ require (
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
|
||||
46
go.sum
46
go.sum
@@ -49,10 +49,10 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
|
||||
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-20230327135226-74ae03f2425e h1:8ChxkWKTVYg7LKBvYNLNRnlobgbPrzzossZUoST2T7o=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
@@ -70,8 +70,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
|
||||
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
|
||||
github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c=
|
||||
github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
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.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||
@@ -107,20 +107,20 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||
github.com/sagernet/reality v0.0.0-20230323230523-5fa25e693e7f h1:plVtFF9NVw5Py4jH/KQuWxojdMFDroTsQ1PVJcU9djM=
|
||||
github.com/sagernet/reality v0.0.0-20230323230523-5fa25e693e7f/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
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.1-0.20230323071235-f8038854d286 h1:0Td2b5l1KgrdlOnbRWgFFWsyb0TLoq/tP6j9Lut4JN0=
|
||||
github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
|
||||
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
||||
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2 h1:VjeHDxEgpB2fqK5G16yBvtLacibvg3h2MsIjal0UXH0=
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 h1:a3W2X1n5C/oYGp/Dd26eoymME3iXN8TJq7LZtO2MSUY=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0 h1:ILDWL7pwWfkPLEbviE/MyCgfjaBmJY/JVVY+5jhSb58=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0/go.mod h1:ysYzszRLpNzJSorvlWRMuzU6Vchsp7sd52q+JNY4axw=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||
github.com/sagernet/sing-tun v0.1.3-0.20230323073325-35d565af6515 h1:r25BJqn3o34g+bDdhoRU65zimKPfGCTv7nHuysyJojo=
|
||||
github.com/sagernet/sing-tun v0.1.3-0.20230323073325-35d565af6515/go.mod h1:1xzFt4zJ7CzXdbgPcy7+Lsg4ypZo0ivDNZ0oQdL3zEY=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
|
||||
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM=
|
||||
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
@@ -133,8 +133,8 @@ github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+V
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -180,8 +180,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
@@ -199,15 +199,15 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
@@ -49,6 +49,10 @@ func (f *simpleFactory) NewLogger(tag string) ContextLogger {
|
||||
return &simpleLogger{f, tag}
|
||||
}
|
||||
|
||||
func (f *simpleFactory) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ ContextLogger = (*simpleLogger)(nil)
|
||||
|
||||
type simpleLogger struct {
|
||||
|
||||
@@ -15,6 +15,7 @@ type Factory interface {
|
||||
SetLevel(level Level)
|
||||
Logger() ContextLogger
|
||||
NewLogger(tag string) ContextLogger
|
||||
Close() error
|
||||
}
|
||||
|
||||
type ObservableFactory interface {
|
||||
|
||||
@@ -36,15 +36,16 @@ func (f Formatter) Format(ctx context.Context, level Level, tag string, message
|
||||
if tag != "" {
|
||||
message = tag + ": " + message
|
||||
}
|
||||
var id uint32
|
||||
var id ID
|
||||
var hasId bool
|
||||
if ctx != nil {
|
||||
id, hasId = IDFromContext(ctx)
|
||||
}
|
||||
if hasId {
|
||||
activeDuration := formatDuration(time.Since(id.CreatedAt))
|
||||
if !f.DisableColors {
|
||||
var color aurora.Color
|
||||
color = aurora.Color(uint8(id))
|
||||
color = aurora.Color(uint8(id.ID))
|
||||
color %= 215
|
||||
row := uint(color / 36)
|
||||
column := uint(color % 36)
|
||||
@@ -62,9 +63,9 @@ func (f Formatter) Format(ctx context.Context, level Level, tag string, message
|
||||
color += 16
|
||||
color = color << 16
|
||||
color |= 1 << 14
|
||||
message = F.ToString("[", aurora.Colorize(id, color).String(), "] ", message)
|
||||
message = F.ToString("[", aurora.Colorize(id.ID, color).String(), " ", activeDuration, "] ", message)
|
||||
} else {
|
||||
message = F.ToString("[", id, "] ", message)
|
||||
message = F.ToString("[", id.ID, " ", activeDuration, "] ", message)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
@@ -99,15 +100,16 @@ func (f Formatter) FormatWithSimple(ctx context.Context, level Level, tag string
|
||||
message = tag + ": " + message
|
||||
}
|
||||
messageSimple := message
|
||||
var id uint32
|
||||
var id ID
|
||||
var hasId bool
|
||||
if ctx != nil {
|
||||
id, hasId = IDFromContext(ctx)
|
||||
}
|
||||
if hasId {
|
||||
activeDuration := formatDuration(time.Since(id.CreatedAt))
|
||||
if !f.DisableColors {
|
||||
var color aurora.Color
|
||||
color = aurora.Color(uint8(id))
|
||||
color = aurora.Color(uint8(id.ID))
|
||||
color %= 215
|
||||
row := uint(color / 36)
|
||||
column := uint(color % 36)
|
||||
@@ -125,11 +127,11 @@ func (f Formatter) FormatWithSimple(ctx context.Context, level Level, tag string
|
||||
color += 16
|
||||
color = color << 16
|
||||
color |= 1 << 14
|
||||
message = F.ToString("[", aurora.Colorize(id, color).String(), "] ", message)
|
||||
message = F.ToString("[", aurora.Colorize(id.ID, color).String(), " ", activeDuration, "] ", message)
|
||||
} else {
|
||||
message = F.ToString("[", id, "] ", message)
|
||||
message = F.ToString("[", id.ID, " ", activeDuration, "] ", message)
|
||||
}
|
||||
messageSimple = F.ToString("[", id, "] ", messageSimple)
|
||||
messageSimple = F.ToString("[", id.ID, " ", activeDuration, "] ", messageSimple)
|
||||
|
||||
}
|
||||
switch {
|
||||
@@ -153,3 +155,13 @@ func xd(value int, x int) string {
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
func formatDuration(duration time.Duration) string {
|
||||
if duration < time.Second {
|
||||
return F.ToString(duration.Milliseconds(), "ms")
|
||||
} else if duration < time.Minute {
|
||||
return F.ToString(int64(duration.Seconds()), ".", int64(duration.Seconds()*100)%100, "s")
|
||||
} else {
|
||||
return F.ToString(int64(duration.Minutes()), "m", int64(duration.Seconds())%60, "s")
|
||||
}
|
||||
}
|
||||
|
||||
17
log/id.go
17
log/id.go
@@ -3,6 +3,7 @@ package log
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/random"
|
||||
)
|
||||
@@ -13,11 +14,19 @@ func init() {
|
||||
|
||||
type idKey struct{}
|
||||
|
||||
func ContextWithNewID(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, (*idKey)(nil), rand.Uint32())
|
||||
type ID struct {
|
||||
ID uint32
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func IDFromContext(ctx context.Context) (uint32, bool) {
|
||||
id, loaded := ctx.Value((*idKey)(nil)).(uint32)
|
||||
func ContextWithNewID(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, (*idKey)(nil), ID{
|
||||
ID: rand.Uint32(),
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
func IDFromContext(ctx context.Context) (ID, bool) {
|
||||
id, loaded := ctx.Value((*idKey)(nil)).(ID)
|
||||
return id, loaded
|
||||
}
|
||||
|
||||
110
log/log.go
Normal file
110
log/log.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type factoryWithFile struct {
|
||||
Factory
|
||||
file *os.File
|
||||
}
|
||||
|
||||
func (f *factoryWithFile) Close() error {
|
||||
return common.Close(
|
||||
f.Factory,
|
||||
common.PtrOrNil(f.file),
|
||||
)
|
||||
}
|
||||
|
||||
type observableFactoryWithFile struct {
|
||||
ObservableFactory
|
||||
file *os.File
|
||||
}
|
||||
|
||||
func (f *observableFactoryWithFile) Close() error {
|
||||
return common.Close(
|
||||
f.ObservableFactory,
|
||||
common.PtrOrNil(f.file),
|
||||
)
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Options option.LogOptions
|
||||
Observable bool
|
||||
DefaultWriter io.Writer
|
||||
BaseTime time.Time
|
||||
PlatformWriter io.Writer
|
||||
}
|
||||
|
||||
func New(options Options) (Factory, error) {
|
||||
logOptions := options.Options
|
||||
|
||||
if logOptions.Disabled {
|
||||
return NewNOPFactory(), nil
|
||||
}
|
||||
|
||||
var logFile *os.File
|
||||
var logWriter io.Writer
|
||||
|
||||
switch logOptions.Output {
|
||||
case "":
|
||||
logWriter = options.DefaultWriter
|
||||
if logWriter == nil {
|
||||
logWriter = os.Stderr
|
||||
}
|
||||
case "stderr":
|
||||
logWriter = os.Stderr
|
||||
case "stdout":
|
||||
logWriter = os.Stdout
|
||||
default:
|
||||
var err error
|
||||
logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logWriter = logFile
|
||||
}
|
||||
logFormatter := Formatter{
|
||||
BaseTime: options.BaseTime,
|
||||
DisableColors: logOptions.DisableColor || logFile != nil,
|
||||
DisableTimestamp: !logOptions.Timestamp && logFile != nil,
|
||||
FullTimestamp: logOptions.Timestamp,
|
||||
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
||||
}
|
||||
var factory Factory
|
||||
if options.Observable {
|
||||
factory = NewObservableFactory(logFormatter, logWriter, options.PlatformWriter)
|
||||
} else {
|
||||
factory = NewFactory(logFormatter, logWriter, options.PlatformWriter)
|
||||
}
|
||||
if logOptions.Level != "" {
|
||||
logLevel, err := ParseLevel(logOptions.Level)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse log level")
|
||||
}
|
||||
factory.SetLevel(logLevel)
|
||||
} else {
|
||||
factory.SetLevel(LevelTrace)
|
||||
}
|
||||
if logFile != nil {
|
||||
if options.Observable {
|
||||
factory = &observableFactoryWithFile{
|
||||
ObservableFactory: factory.(ObservableFactory),
|
||||
file: logFile,
|
||||
}
|
||||
} else {
|
||||
factory = &factoryWithFile{
|
||||
Factory: factory,
|
||||
file: logFile,
|
||||
}
|
||||
}
|
||||
}
|
||||
return factory, nil
|
||||
}
|
||||
@@ -72,6 +72,10 @@ func (f *nopFactory) FatalContext(ctx context.Context, args ...any) {
|
||||
func (f *nopFactory) PanicContext(ctx context.Context, args ...any) {
|
||||
}
|
||||
|
||||
func (f *nopFactory) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *nopFactory) Subscribe() (subscription observable.Subscription[Entry], done <-chan struct{}, err error) {
|
||||
return nil, nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ nav:
|
||||
- Installation:
|
||||
- From source: installation/from-source.md
|
||||
- Clients:
|
||||
- SFI:
|
||||
- installation/clients/sfi/index.md
|
||||
- iOS: installation/clients/sfi.md
|
||||
- Android: installation/clients/sfa.md
|
||||
- Configuration:
|
||||
- configuration/index.md
|
||||
- Log:
|
||||
|
||||
@@ -61,19 +61,19 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
|
||||
}
|
||||
|
||||
type V2RayHTTPOptions struct {
|
||||
Host Listable[string] `json:"host,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||
PingTimeout Duration `json:"ping_timeout,omitempty"`
|
||||
Host Listable[string] `json:"host,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Headers map[string]Listable[string] `json:"headers,omitempty"`
|
||||
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||
PingTimeout Duration `json:"ping_timeout,omitempty"`
|
||||
}
|
||||
|
||||
type V2RayWebsocketOptions struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
MaxEarlyData uint32 `json:"max_early_data,omitempty"`
|
||||
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers map[string]Listable[string] `json:"headers,omitempty"`
|
||||
MaxEarlyData uint32 `json:"max_early_data,omitempty"`
|
||||
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
|
||||
}
|
||||
|
||||
type V2RayQUICOptions struct{}
|
||||
|
||||
@@ -11,6 +11,11 @@ import (
|
||||
)
|
||||
|
||||
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Outbound) (adapter.Outbound, error) {
|
||||
var metadata *adapter.InboundContext
|
||||
if options.Tag != "" {
|
||||
ctx, metadata = adapter.AppendContext(ctx)
|
||||
metadata.Outbound = options.Tag
|
||||
}
|
||||
if options.Type == "" {
|
||||
return nil, E.New("missing outbound type")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package outbound
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
@@ -56,15 +57,21 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
|
||||
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var outConn net.PacketConn
|
||||
var destinationAddress netip.Addr
|
||||
var err error
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
outConn, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
||||
outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
||||
} else {
|
||||
outConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||
}
|
||||
if err != nil {
|
||||
return N.HandshakeFailure(conn, err)
|
||||
}
|
||||
if destinationAddress.IsValid() {
|
||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
||||
natConn.UpdateDestination(destinationAddress)
|
||||
}
|
||||
}
|
||||
switch metadata.Protocol {
|
||||
case C.ProtocolSTUN:
|
||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout)
|
||||
|
||||
@@ -37,7 +37,7 @@ func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, option
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
},
|
||||
http.NewClient(detour, options.ServerOptions.Build(), options.Username, options.Password),
|
||||
http.NewClient(detour, options.ServerOptions.Build(), options.Username, options.Password, nil),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,9 @@ var _ N.Dialer = (*shadowsocksDialer)(nil)
|
||||
type shadowsocksDialer Shadowsocks
|
||||
|
||||
func (h *shadowsocksDialer) 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 N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
var outConn net.Conn
|
||||
@@ -161,6 +164,9 @@ func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, des
|
||||
}
|
||||
|
||||
func (h *shadowsocksDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Outbound = h.tag
|
||||
metadata.Destination = destination
|
||||
outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -99,6 +99,9 @@ func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.Cont
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -131,6 +134,9 @@ func (h *ShadowsocksR) DialContext(ctx context.Context, network string, destinat
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -86,23 +86,26 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
func (h *ShadowTLS) 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 N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
return s.client.DialContext(ctx)
|
||||
return h.client.DialContext(ctx)
|
||||
default:
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
func (h *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, s, conn, metadata)
|
||||
func (h *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
func (h *ShadowTLS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-box/transport/vless"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
@@ -105,11 +104,14 @@ func (h *VLESS) DialContext(ctx context.Context, network string, destination M.S
|
||||
if h.xudp {
|
||||
return h.client.DialEarlyXUDPPacketConn(conn, destination)
|
||||
} else if h.packetAddr {
|
||||
if destination.IsFqdn() {
|
||||
return nil, E.New("packetaddr: domain destination is not supported")
|
||||
}
|
||||
packetConn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bufio.BindPacketConn{PacketConn: dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(packetConn, destination)), Addr: destination}, nil
|
||||
return &bufio.BindPacketConn{PacketConn: packetaddr.NewConn(packetConn, destination), Addr: destination}, nil
|
||||
} else {
|
||||
return h.client.DialEarlyPacketConn(conn, destination)
|
||||
}
|
||||
@@ -140,11 +142,14 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
|
||||
if h.xudp {
|
||||
return h.client.DialEarlyXUDPPacketConn(conn, destination)
|
||||
} else if h.packetAddr {
|
||||
if destination.IsFqdn() {
|
||||
return nil, E.New("packetaddr: domain destination is not supported")
|
||||
}
|
||||
conn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(conn, destination)), nil
|
||||
return packetaddr.NewConn(conn, destination), nil
|
||||
} else {
|
||||
return h.client.DialEarlyPacketConn(conn, destination)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-vmess"
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
@@ -188,7 +187,10 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
return nil, err
|
||||
}
|
||||
if h.packetAddr {
|
||||
return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination)), nil
|
||||
if destination.IsFqdn() {
|
||||
return nil, E.New("packetaddr: domain destination is not supported")
|
||||
}
|
||||
return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil
|
||||
} else if h.xudp {
|
||||
return h.client.DialEarlyXUDPPacketConn(conn, destination), nil
|
||||
} else {
|
||||
|
||||
@@ -179,5 +179,6 @@ func (w *WireGuard) Close() error {
|
||||
if w.device != nil {
|
||||
w.device.Close()
|
||||
}
|
||||
return common.Close(w.tunDevice)
|
||||
w.tunDevice.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -rf /var/lib/sing-box
|
||||
@@ -4,10 +4,9 @@ Documentation=https://sing-box.sagernet.org
|
||||
After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/lib/sing-box
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/bin/sing-box run -c /etc/sing-box/config.json
|
||||
ExecStart=/usr/bin/sing-box -D /var/lib/sing-box -C /etc/sing-box run
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
@@ -4,10 +4,9 @@ Documentation=https://sing-box.sagernet.org
|
||||
After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/lib/sing-box-%i
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/bin/sing-box run -c /etc/sing-box/%i.json
|
||||
ExecStart=/usr/bin/sing-box -D /var/lib/sing-box-%i -c /etc/sing-box/%i.json run
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.20.1.linux-amd64.tar.gz
|
||||
|
||||
go_version=$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
|
||||
curl -Lo go.tar.gz "https://go.dev/dl/go$go_version.linux-amd64.tar.gz"
|
||||
sudo rm -rf /usr/local/go
|
||||
sudo tar -C /usr/local -xzf go.tar.gz
|
||||
rm go.tar.gz
|
||||
|
||||
@@ -4,10 +4,9 @@ Documentation=https://sing-box.sagernet.org
|
||||
After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/lib/sing-box
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/local/bin/sing-box run -c /usr/local/etc/sing-box/config.json
|
||||
ExecStart=/usr/local/bin/sing-box -D /var/lib/sing-box -C /usr/local/etc/sing-box run
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
@@ -12,17 +12,25 @@ var _ RuleItem = (*OutboundItem)(nil)
|
||||
type OutboundItem struct {
|
||||
outbounds []string
|
||||
outboundMap map[string]bool
|
||||
matchAny bool
|
||||
}
|
||||
|
||||
func NewOutboundRule(outbounds []string) *OutboundItem {
|
||||
rule := &OutboundItem{outbounds, make(map[string]bool)}
|
||||
rule := &OutboundItem{outbounds: outbounds, outboundMap: make(map[string]bool)}
|
||||
for _, outbound := range outbounds {
|
||||
rule.outboundMap[outbound] = true
|
||||
if outbound == "any" {
|
||||
rule.matchAny = true
|
||||
} else {
|
||||
rule.outboundMap[outbound] = true
|
||||
}
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
func (r *OutboundItem) Match(metadata *adapter.InboundContext) bool {
|
||||
if r.matchAny && metadata.Outbound != "" {
|
||||
return true
|
||||
}
|
||||
return r.outboundMap[metadata.Outbound]
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,10 @@ func startInstance(t *testing.T, options option.Options) *box.Box {
|
||||
var instance *box.Box
|
||||
var err error
|
||||
for retry := 0; retry < 3; retry++ {
|
||||
instance, err = box.New(ctx, options, nil)
|
||||
instance, err = box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = instance.Start()
|
||||
if err != nil {
|
||||
|
||||
26
test/go.mod
26
test/go.mod
@@ -10,17 +10,17 @@ require (
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046
|
||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
go.uber.org/goleak v1.2.0
|
||||
golang.org/x/net v0.8.0
|
||||
golang.org/x/net v0.9.0
|
||||
)
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.13.0 // indirect
|
||||
github.com/Dreamacro/clash v1.14.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
@@ -42,14 +42,14 @@ require (
|
||||
github.com/google/btree v1.0.1 // 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-20230307103557-e252950ab961 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||
github.com/mholt/acmez v1.1.0 // indirect
|
||||
github.com/miekg/dns v1.1.52 // indirect
|
||||
github.com/miekg/dns v1.1.53 // indirect
|
||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
@@ -69,10 +69,10 @@ require (
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.4 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d // indirect
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.3 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
|
||||
@@ -88,14 +88,14 @@ require (
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
google.golang.org/grpc v1.53.0 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.4.0 // indirect
|
||||
|
||||
54
test/go.sum
54
test/go.sum
@@ -1,8 +1,8 @@
|
||||
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/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Dreamacro/clash v1.13.0 h1:gF0E0TluE1LCmuhhg0/bjqABYDmSnXkUjXjRhZxyrm8=
|
||||
github.com/Dreamacro/clash v1.13.0/go.mod h1:hf0RkWPsQ0e8oS8WVJBIRocY/1ILYzQQg9zeMwd8LsM=
|
||||
github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
|
||||
github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
|
||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
@@ -61,8 +61,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e h1:8ChxkWKTVYg7LKBvYNLNRnlobgbPrzzossZUoST2T7o=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
@@ -80,8 +80,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
|
||||
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
|
||||
github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c=
|
||||
github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g=
|
||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
@@ -122,20 +122,20 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||
github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8 h1:4M3+0/kqvJuTsimEORH0/vUYTpMDUETBH2zNUU38SUw=
|
||||
github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
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.1-0.20230318094614-4bbf5f2c3046 h1:/+ZWbxRvQmco9ES2qT5Eh/x/IiQRjAcUyRG/vQ4dpxc=
|
||||
github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
|
||||
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
||||
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU=
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2 h1:VjeHDxEgpB2fqK5G16yBvtLacibvg3h2MsIjal0UXH0=
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 h1:a3W2X1n5C/oYGp/Dd26eoymME3iXN8TJq7LZtO2MSUY=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0 h1:ILDWL7pwWfkPLEbviE/MyCgfjaBmJY/JVVY+5jhSb58=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0/go.mod h1:ysYzszRLpNzJSorvlWRMuzU6Vchsp7sd52q+JNY4axw=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||
github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d h1:1gt4Hu2fHCrmL2NZYCNJ3nCgeczuhK09oCMni9mZmZk=
|
||||
github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d/go.mod h1:KnRkwaDHbb06zgeNPu0LQ8A+vA9myMxKEgHN1brCPHg=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
|
||||
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM=
|
||||
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
@@ -191,8 +191,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo=
|
||||
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -206,8 +206,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -232,15 +232,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -257,8 +257,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 v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
|
||||
@@ -66,8 +66,8 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
|
||||
transportOptions = option.V2RayTransportOptions{
|
||||
Type: C.V2RayTransportTypeWebsocket,
|
||||
WebsocketOptions: option.V2RayWebsocketOptions{
|
||||
Headers: map[string]string{
|
||||
"Host": host,
|
||||
Headers: map[string]option.Listable[string]{
|
||||
"Host": []string{host},
|
||||
},
|
||||
Path: path,
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
@@ -93,3 +94,8 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
v2rayhttp.CloseIdleConnections(c.transport)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package v2rayhttp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"math/rand"
|
||||
@@ -81,8 +80,8 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
if !strings.HasPrefix(uri.Path, "/") {
|
||||
uri.Path = "/" + uri.Path
|
||||
}
|
||||
for key, value := range options.Headers {
|
||||
client.headers.Set(key, value)
|
||||
for key, valueList := range options.Headers {
|
||||
client.headers[key] = valueList
|
||||
}
|
||||
client.url = &uri
|
||||
return client
|
||||
@@ -97,60 +96,46 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) {
|
||||
conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, c.serverAddr)
|
||||
conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request := &http.Request{
|
||||
Method: c.method,
|
||||
URL: c.url,
|
||||
ProtoMajor: 1,
|
||||
Proto: "HTTP/1.1",
|
||||
Header: c.headers.Clone(),
|
||||
Method: c.method,
|
||||
URL: c.url,
|
||||
Header: c.headers.Clone(),
|
||||
}
|
||||
request = request.WithContext(ctx)
|
||||
switch hostLen := len(c.host); hostLen {
|
||||
case 0:
|
||||
request.Host = c.serverAddr.AddrString()
|
||||
case 1:
|
||||
request.Host = c.host[0]
|
||||
default:
|
||||
request.Host = c.host[rand.Intn(hostLen)]
|
||||
}
|
||||
err = request.Write(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := bufio.NewReader(conn)
|
||||
response, err := http.ReadResponse(reader, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
return nil, E.New("unexpected status: ", response.Status)
|
||||
}
|
||||
return conn, nil
|
||||
|
||||
return NewHTTP1Conn(conn, request), nil
|
||||
}
|
||||
|
||||
func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
|
||||
pipeInReader, pipeInWriter := io.Pipe()
|
||||
request := &http.Request{
|
||||
Method: c.method,
|
||||
Body: pipeInReader,
|
||||
URL: c.url,
|
||||
ProtoMajor: 2,
|
||||
Proto: "HTTP/2",
|
||||
Header: c.headers.Clone(),
|
||||
Method: c.method,
|
||||
Body: pipeInReader,
|
||||
URL: c.url,
|
||||
Header: c.headers.Clone(),
|
||||
}
|
||||
request = request.WithContext(ctx)
|
||||
switch hostLen := len(c.host); hostLen {
|
||||
case 0:
|
||||
// https://github.com/v2fly/v2ray-core/blob/master/transport/internet/http/config.go#L13
|
||||
request.Host = "www.example.com"
|
||||
case 1:
|
||||
request.Host = c.host[0]
|
||||
default:
|
||||
request.Host = c.host[rand.Intn(hostLen)]
|
||||
}
|
||||
// Disable any compression method from server.
|
||||
request.Header.Set("Accept-Encoding", "identity")
|
||||
conn := newLateHTTPConn(pipeInWriter)
|
||||
go func() {
|
||||
response, err := c.transport.RoundTrip(request)
|
||||
@@ -164,3 +149,8 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
|
||||
}()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
CloseIdleConnections(c.transport)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package v2rayhttp
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -12,37 +14,146 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type HTTPConn struct {
|
||||
net.Conn
|
||||
request *http.Request
|
||||
requestWritten bool
|
||||
responseRead bool
|
||||
responseCache *buf.Buffer
|
||||
}
|
||||
|
||||
func NewHTTP1Conn(conn net.Conn, request *http.Request) *HTTPConn {
|
||||
return &HTTPConn{
|
||||
Conn: conn,
|
||||
request: request,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HTTPConn) Read(b []byte) (n int, err error) {
|
||||
if !c.responseRead {
|
||||
reader := std_bufio.NewReader(c.Conn)
|
||||
response, err := http.ReadResponse(reader, c.request)
|
||||
if err != nil {
|
||||
return 0, E.Cause(err, "read response")
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
return 0, E.New("unexpected status: ", response.Status)
|
||||
}
|
||||
if cacheLen := reader.Buffered(); cacheLen > 0 {
|
||||
c.responseCache = buf.NewSize(cacheLen)
|
||||
_, err = c.responseCache.ReadFullFrom(reader, cacheLen)
|
||||
if err != nil {
|
||||
c.responseCache.Release()
|
||||
return 0, E.Cause(err, "read cache")
|
||||
}
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
if c.responseCache != nil {
|
||||
n, err = c.responseCache.Read(b)
|
||||
if err == io.EOF {
|
||||
c.responseCache.Release()
|
||||
c.responseCache = nil
|
||||
}
|
||||
if n > 0 {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *HTTPConn) Write(b []byte) (int, error) {
|
||||
if !c.requestWritten {
|
||||
err := c.writeRequest(b)
|
||||
if err != nil {
|
||||
return 0, E.Cause(err, "write request")
|
||||
}
|
||||
c.requestWritten = true
|
||||
return len(b), nil
|
||||
}
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
|
||||
func (c *HTTPConn) writeRequest(payload []byte) error {
|
||||
writer := bufio.NewBufferedWriter(c.Conn, buf.New())
|
||||
const CRLF = "\r\n"
|
||||
_, err := writer.Write([]byte(F.ToString(c.request.Method, " ", c.request.URL.RequestURI(), " HTTP/1.1", CRLF)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.request.Header.Get("Host") == "" {
|
||||
c.request.Header.Set("Host", c.request.Host)
|
||||
}
|
||||
for key, value := range c.request.Header {
|
||||
_, err = writer.Write([]byte(F.ToString(key, ": ", strings.Join(value, ", "), CRLF)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = writer.Write([]byte(CRLF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writer.Fallthrough()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HTTPConn) ReaderReplaceable() bool {
|
||||
return c.responseRead
|
||||
}
|
||||
|
||||
func (c *HTTPConn) WriterReplaceable() bool {
|
||||
return c.requestWritten
|
||||
}
|
||||
|
||||
func (c *HTTPConn) NeedHandshake() bool {
|
||||
return !c.requestWritten
|
||||
}
|
||||
|
||||
func (c *HTTPConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
type HTTP2Conn struct {
|
||||
reader io.Reader
|
||||
writer io.Writer
|
||||
create chan struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
func NewHTTPConn(reader io.Reader, writer io.Writer) HTTPConn {
|
||||
return HTTPConn{
|
||||
func NewHTTPConn(reader io.Reader, writer io.Writer) HTTP2Conn {
|
||||
return HTTP2Conn{
|
||||
reader: reader,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func newLateHTTPConn(writer io.Writer) *HTTPConn {
|
||||
return &HTTPConn{
|
||||
func newLateHTTPConn(writer io.Writer) *HTTP2Conn {
|
||||
return &HTTP2Conn{
|
||||
create: make(chan struct{}),
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HTTPConn) setup(reader io.Reader, err error) {
|
||||
func (c *HTTP2Conn) setup(reader io.Reader, err error) {
|
||||
c.reader = reader
|
||||
c.err = err
|
||||
close(c.create)
|
||||
}
|
||||
|
||||
func (c *HTTPConn) Read(b []byte) (n int, err error) {
|
||||
func (c *HTTP2Conn) Read(b []byte) (n int, err error) {
|
||||
if c.reader == nil {
|
||||
<-c.create
|
||||
if c.err != nil {
|
||||
@@ -53,24 +164,24 @@ func (c *HTTPConn) Read(b []byte) (n int, err error) {
|
||||
return n, baderror.WrapH2(err)
|
||||
}
|
||||
|
||||
func (c *HTTPConn) Write(b []byte) (n int, err error) {
|
||||
func (c *HTTP2Conn) Write(b []byte) (n int, err error) {
|
||||
n, err = c.writer.Write(b)
|
||||
return n, baderror.WrapH2(err)
|
||||
}
|
||||
|
||||
func (c *HTTPConn) Close() error {
|
||||
func (c *HTTP2Conn) Close() error {
|
||||
return common.Close(c.reader, c.writer)
|
||||
}
|
||||
|
||||
func (c *HTTPConn) LocalAddr() net.Addr {
|
||||
func (c *HTTP2Conn) LocalAddr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HTTPConn) RemoteAddr() net.Addr {
|
||||
func (c *HTTP2Conn) RemoteAddr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HTTPConn) SetDeadline(t time.Time) error {
|
||||
func (c *HTTP2Conn) SetDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetWriteDeadline(time.Time) error
|
||||
}); loaded {
|
||||
@@ -79,7 +190,7 @@ func (c *HTTPConn) SetDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *HTTPConn) SetReadDeadline(t time.Time) error {
|
||||
func (c *HTTP2Conn) SetReadDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetReadDeadline(time.Time) error
|
||||
}); loaded {
|
||||
@@ -88,7 +199,7 @@ func (c *HTTPConn) SetReadDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *HTTPConn) SetWriteDeadline(t time.Time) error {
|
||||
func (c *HTTP2Conn) SetWriteDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetWriteDeadline(time.Time) error
|
||||
}); loaded {
|
||||
@@ -98,7 +209,7 @@ func (c *HTTPConn) SetWriteDeadline(t time.Time) error {
|
||||
}
|
||||
|
||||
type ServerHTTPConn struct {
|
||||
HTTPConn
|
||||
HTTP2Conn
|
||||
flusher http.Flusher
|
||||
}
|
||||
|
||||
|
||||
13
transport/v2rayhttp/pool.go
Normal file
13
transport/v2rayhttp/pool.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package v2rayhttp
|
||||
|
||||
import "net/http"
|
||||
|
||||
type ConnectionPool interface {
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
func CloseIdleConnections(transport http.RoundTripper) {
|
||||
if connectionPool, ok := transport.(ConnectionPool); ok {
|
||||
connectionPool.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -62,7 +64,7 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t
|
||||
server.path = "/" + server.path
|
||||
}
|
||||
for key, value := range options.Headers {
|
||||
server.headers.Set(key, value)
|
||||
server.headers[key] = value
|
||||
}
|
||||
server.httpServer = &http.Server{
|
||||
Handler: server,
|
||||
@@ -106,11 +108,20 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
var metadata M.Metadata
|
||||
metadata.Source = sHttp.SourceAddress(request)
|
||||
if h, ok := writer.(http.Hijacker); ok {
|
||||
conn, _, err := h.Hijack()
|
||||
conn, reader, err := h.Hijack()
|
||||
if err != nil {
|
||||
s.fallbackRequest(request.Context(), writer, request, http.StatusInternalServerError, 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, http.StatusInternalServerError, E.Cause(err, "read cache"))
|
||||
return
|
||||
}
|
||||
conn = bufio.NewCachedConn(conn, cache)
|
||||
}
|
||||
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||
} else {
|
||||
conn := NewHTTP2Wrapper(&ServerHTTPConn{
|
||||
|
||||
@@ -62,7 +62,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
}
|
||||
headers := make(http.Header)
|
||||
for key, value := range options.Headers {
|
||||
headers.Set(key, value)
|
||||
headers[key] = value
|
||||
}
|
||||
return &Client{
|
||||
wsDialer,
|
||||
|
||||
Reference in New Issue
Block a user