mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90c54c1a38 | ||
|
|
0dd19ac0f3 | ||
|
|
1f65ff88b5 | ||
|
|
bbdd495ed5 | ||
|
|
d686172854 | ||
|
|
e1d96cb64e | ||
|
|
d5f94b65b7 | ||
|
|
ec2d0b6b3c | ||
|
|
3a92bf993d | ||
|
|
ec13965fd0 | ||
|
|
ddf747006e | ||
|
|
4382093868 | ||
|
|
a5322850b3 | ||
|
|
407b08975c | ||
|
|
c7067ff5e8 | ||
|
|
9b2384b296 | ||
|
|
b498a22972 | ||
|
|
20e9da5c67 | ||
|
|
ec8974673b | ||
|
|
5e6e7923e4 |
@@ -32,6 +32,7 @@ type Router interface {
|
||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||
|
||||
InterfaceFinder() control.InterfaceFinder
|
||||
UpdateInterfaces() error
|
||||
DefaultInterface() string
|
||||
AutoDetectInterface() bool
|
||||
AutoDetectInterfaceFunc() control.Func
|
||||
|
||||
6
box.go
6
box.go
@@ -133,6 +133,12 @@ func New(options Options) (*Box, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.PlatformInterface != nil {
|
||||
err = options.PlatformInterface.Initialize(ctx, router)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize platform interface")
|
||||
}
|
||||
}
|
||||
preServices := make(map[string]adapter.Service)
|
||||
postServices := make(map[string]adapter.Service)
|
||||
if needClashAPI {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
@@ -17,41 +16,6 @@ import (
|
||||
"github.com/sagernet/tfo-go"
|
||||
)
|
||||
|
||||
var warnBindInterfaceOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
|
||||
},
|
||||
"outbound option `bind_interface` is only supported on Linux and Windows",
|
||||
)
|
||||
|
||||
var warnRoutingMarkOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !C.IsLinux
|
||||
},
|
||||
"outbound option `routing_mark` is only supported on Linux",
|
||||
)
|
||||
|
||||
var warnReuseAdderOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsDarwin || C.IsDragonfly || C.IsFreebsd || C.IsLinux || C.IsNetbsd || C.IsOpenbsd || C.IsSolaris || C.IsWindows)
|
||||
},
|
||||
"outbound option `reuse_addr` is unsupported on current platform",
|
||||
)
|
||||
|
||||
var warnProtectPathOnNonAndroid = warning.New(
|
||||
func() bool {
|
||||
return !C.IsAndroid
|
||||
},
|
||||
"outbound option `protect_path` is only supported on Android",
|
||||
)
|
||||
|
||||
var warnTFOOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsDarwin || C.IsFreebsd || C.IsLinux || C.IsWindows)
|
||||
},
|
||||
"outbound option `tcp_fast_open` is unsupported on current platform",
|
||||
)
|
||||
|
||||
type DefaultDialer struct {
|
||||
dialer4 tfo.Dialer
|
||||
dialer6 tfo.Dialer
|
||||
@@ -66,7 +30,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
var dialer net.Dialer
|
||||
var listener net.ListenConfig
|
||||
if options.BindInterface != "" {
|
||||
warnBindInterfaceOnUnsupportedPlatform.Check()
|
||||
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
@@ -80,7 +43,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
if options.RoutingMark != 0 {
|
||||
warnRoutingMarkOnUnsupportedPlatform.Check()
|
||||
dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(options.RoutingMark))
|
||||
} else if router.DefaultMark() != 0 {
|
||||
@@ -88,11 +50,9 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(router.DefaultMark()))
|
||||
}
|
||||
if options.ReuseAddr {
|
||||
warnReuseAdderOnUnsupportedPlatform.Check()
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
}
|
||||
if options.ProtectPath != "" {
|
||||
warnProtectPathOnNonAndroid.Check()
|
||||
dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath))
|
||||
listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath))
|
||||
}
|
||||
@@ -101,9 +61,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
} else {
|
||||
dialer.Timeout = C.TCPTimeout
|
||||
}
|
||||
if options.TCPFastOpen {
|
||||
warnTFOOnUnsupportedPlatform.Check()
|
||||
}
|
||||
var udpFragment bool
|
||||
if options.UDPFragment != nil {
|
||||
udpFragment = *options.UDPFragment
|
||||
|
||||
@@ -249,6 +249,10 @@ func (c *ClientConn) WriterReplaceable() bool {
|
||||
return c.requestWrite
|
||||
}
|
||||
|
||||
func (c *ClientConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
@@ -377,6 +381,10 @@ func (c *ClientPacketConn) RemoteAddr() net.Addr {
|
||||
return c.destination.UDPAddr()
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -518,6 +526,10 @@ func (c *ClientPacketAddrConn) FrontHeadroom() int {
|
||||
return 2 + M.MaxSocksaddrLength
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
@@ -131,6 +131,10 @@ func (c *ServerConn) FrontHeadroom() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *ServerConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -183,6 +187,10 @@ func (c *ServerPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksad
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -245,6 +253,10 @@ func (c *ServerPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Soc
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
)
|
||||
|
||||
@@ -15,5 +16,5 @@ func HTTPHost(ctx context.Context, reader io.Reader) (*adapter.InboundContext, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolHTTP, Domain: request.Host}, nil
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolHTTP, Domain: M.ParseSocksaddr(request.Host).AddrString()}, nil
|
||||
}
|
||||
|
||||
27
common/sniff/http_test.go
Normal file
27
common/sniff/http_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package sniff_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSniffHTTP1(t *testing.T) {
|
||||
t.Parallel()
|
||||
pkt := "GET / HTTP/1.1\r\nHost: www.google.com\r\nAccept: */*\r\n\r\n"
|
||||
metadata, err := sniff.HTTPHost(context.Background(), strings.NewReader(pkt))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, metadata.Domain, "www.google.com")
|
||||
}
|
||||
|
||||
func TestSniffHTTP1WithPort(t *testing.T) {
|
||||
t.Parallel()
|
||||
pkt := "GET / HTTP/1.1\r\nHost: www.gov.cn:8080\r\nAccept: */*\r\n\r\n"
|
||||
metadata, err := sniff.HTTPHost(context.Background(), strings.NewReader(pkt))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, metadata.Domain, "www.gov.cn")
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package warning
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
type Warning struct {
|
||||
logger log.Logger
|
||||
check CheckFunc
|
||||
message string
|
||||
checkOnce sync.Once
|
||||
}
|
||||
|
||||
type CheckFunc = func() bool
|
||||
|
||||
func New(checkFunc CheckFunc, message string) Warning {
|
||||
return Warning{
|
||||
check: checkFunc,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Warning) Check() {
|
||||
w.checkOnce.Do(func() {
|
||||
if w.check() {
|
||||
log.Warn(w.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
#### 1.2.5
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.2.4
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@@ -12,5 +12,6 @@ Experimental Android client for sing-box.
|
||||
|
||||
#### 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
|
||||
* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
* The working directory is located at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory)
|
||||
* Crash logs is located in `$working_directory/stderr.log`
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录)
|
||||
* 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录)
|
||||
* 崩溃日志位于 `$working_directory/stderr.log`
|
||||
|
||||
@@ -13,8 +13,8 @@ Experimental iOS client for sing-box.
|
||||
|
||||
#### Note
|
||||
|
||||
* `system` tun stack not working on iOS
|
||||
* User Agent is `SFI/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
|
||||
* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
* Crash logs is located in `Settings` -> `View Service Log`
|
||||
|
||||
#### Privacy policy
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* `system` tun stack 在 iOS 不工作
|
||||
* 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 崩溃日志位于 `设置` -> `查看服务日志`
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
|
||||
@@ -29,3 +29,19 @@ func (i *iterator[T]) Next() T {
|
||||
func (i *iterator[T]) HasNext() bool {
|
||||
return len(i.values) > 0
|
||||
}
|
||||
|
||||
type abstractIterator[T any] interface {
|
||||
Next() T
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
func iteratorToArray[T any](iterator abstractIterator[T]) []T {
|
||||
if iterator == nil {
|
||||
return nil
|
||||
}
|
||||
var values []T
|
||||
for iterator.HasNext() {
|
||||
values = append(values, iterator.Next())
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
183
experimental/libbox/monitor.go
Normal file
183
experimental/libbox/monitor.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
var (
|
||||
_ tun.DefaultInterfaceMonitor = (*platformDefaultInterfaceMonitor)(nil)
|
||||
_ InterfaceUpdateListener = (*platformDefaultInterfaceMonitor)(nil)
|
||||
)
|
||||
|
||||
type platformDefaultInterfaceMonitor struct {
|
||||
*platformInterfaceWrapper
|
||||
errorHandler E.Handler
|
||||
networkAddresses []networkAddress
|
||||
defaultInterfaceName string
|
||||
defaultInterfaceIndex int
|
||||
element *list.Element[tun.NetworkUpdateCallback]
|
||||
access sync.Mutex
|
||||
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
||||
}
|
||||
|
||||
type networkAddress struct {
|
||||
interfaceName string
|
||||
interfaceIndex int
|
||||
addresses []netip.Prefix
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) Start() error {
|
||||
return m.iif.StartDefaultInterfaceMonitor(m)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) Close() error {
|
||||
return m.iif.CloseDefaultInterfaceMonitor(m)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterfaceName(destination netip.Addr) string {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceName
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.defaultInterfaceName
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterfaceIndex(destination netip.Addr) int {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.defaultInterfaceIndex
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) AndroidVPNEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) RegisterCallback(callback tun.DefaultInterfaceUpdateCallback) *list.Element[tun.DefaultInterfaceUpdateCallback] {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
return m.callbacks.PushBack(callback)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.callbacks.Remove(element)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
|
||||
var err error
|
||||
if m.iif.UsePlatformInterfaceGetter() {
|
||||
err = m.updateInterfacesPlatform()
|
||||
} else {
|
||||
err = m.updateInterfaces()
|
||||
}
|
||||
if err == nil {
|
||||
err = m.router.UpdateInterfaces()
|
||||
}
|
||||
if err != nil {
|
||||
m.errorHandler.NewError(context.Background(), E.Cause(err, "update interfaces"))
|
||||
}
|
||||
interfaceIndex := int(interfaceIndex32)
|
||||
if interfaceName == "" {
|
||||
for _, netIf := range m.networkAddresses {
|
||||
if netIf.interfaceIndex == interfaceIndex {
|
||||
interfaceName = netIf.interfaceName
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if interfaceIndex == -1 {
|
||||
for _, netIf := range m.networkAddresses {
|
||||
if netIf.interfaceName == interfaceName {
|
||||
interfaceIndex = netIf.interfaceIndex
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if interfaceName == "" {
|
||||
m.errorHandler.NewError(context.Background(), E.New("invalid interface name for ", interfaceIndex))
|
||||
return
|
||||
} else if interfaceIndex == -1 {
|
||||
m.errorHandler.NewError(context.Background(), E.New("invalid interface index for ", interfaceName))
|
||||
return
|
||||
}
|
||||
if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
|
||||
return
|
||||
}
|
||||
m.defaultInterfaceName = interfaceName
|
||||
m.defaultInterfaceIndex = interfaceIndex
|
||||
m.access.Lock()
|
||||
callbacks := m.callbacks.Array()
|
||||
m.access.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
err = callback(tun.EventInterfaceUpdate)
|
||||
if err != nil {
|
||||
m.errorHandler.NewError(context.Background(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) updateInterfaces() error {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var addresses []networkAddress
|
||||
for _, iif := range interfaces {
|
||||
var netAddresses []net.Addr
|
||||
netAddresses, err = iif.Addrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var address networkAddress
|
||||
address.interfaceName = iif.Name
|
||||
address.interfaceIndex = iif.Index
|
||||
address.addresses = common.Map(common.FilterIsInstance(netAddresses, func(it net.Addr) (*net.IPNet, bool) {
|
||||
value, loaded := it.(*net.IPNet)
|
||||
return value, loaded
|
||||
}), func(it *net.IPNet) netip.Prefix {
|
||||
bits, _ := it.Mask.Size()
|
||||
return netip.PrefixFrom(M.AddrFromIP(it.IP), bits)
|
||||
})
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
m.networkAddresses = addresses
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) updateInterfacesPlatform() error {
|
||||
interfaces, err := m.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var addresses []networkAddress
|
||||
for _, iif := range interfaces {
|
||||
var address networkAddress
|
||||
address.interfaceName = iif.Name
|
||||
address.interfaceIndex = iif.Index
|
||||
// address.addresses = common.Map(iif.Addresses, netip.MustParsePrefix)
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
m.networkAddresses = addresses
|
||||
return nil
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package libbox
|
||||
|
||||
import "github.com/sagernet/sing-box/option"
|
||||
import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
type PlatformInterface interface {
|
||||
UsePlatformAutoDetectInterfaceControl() bool
|
||||
AutoDetectInterfaceControl(fd int32) error
|
||||
OpenTun(options TunOptions) (int32, error)
|
||||
WriteLog(message string)
|
||||
@@ -10,6 +13,11 @@ type PlatformInterface interface {
|
||||
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||
PackageNameByUid(uid int32) (string, error)
|
||||
UIDByPackageName(packageName string) (int32, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||
UsePlatformInterfaceGetter() bool
|
||||
GetInterfaces() (NetworkInterfaceIterator, error)
|
||||
}
|
||||
|
||||
type TunInterface interface {
|
||||
@@ -17,8 +25,19 @@ type TunInterface interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
type OnDemandRuleIterator interface {
|
||||
Next() OnDemandRule
|
||||
type InterfaceUpdateListener interface {
|
||||
UpdateDefaultInterface(interfaceName string, interfaceIndex int32)
|
||||
}
|
||||
|
||||
type NetworkInterface struct {
|
||||
Index int32
|
||||
MTU int32
|
||||
Name string
|
||||
Addresses StringIterator
|
||||
}
|
||||
|
||||
type NetworkInterfaceIterator interface {
|
||||
Next() *NetworkInterface
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
@@ -31,6 +50,11 @@ type OnDemandRule interface {
|
||||
ProbeURL() string
|
||||
}
|
||||
|
||||
type OnDemandRuleIterator interface {
|
||||
Next() OnDemandRule
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type onDemandRule struct {
|
||||
option.OnDemandRule
|
||||
}
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
Initialize(ctx context.Context, router adapter.Router) error
|
||||
UsePlatformAutoDetectInterfaceControl() bool
|
||||
AutoDetectInterfaceControl() control.Func
|
||||
OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor
|
||||
UsePlatformInterfaceGetter() bool
|
||||
Interfaces() ([]NetworkInterface, error)
|
||||
process.Searcher
|
||||
io.Writer
|
||||
}
|
||||
|
||||
type NetworkInterface struct {
|
||||
Index int
|
||||
MTU int
|
||||
Name string
|
||||
Addresses []netip.Prefix
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -31,7 +33,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
PlatformInterface: &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()},
|
||||
PlatformInterface: &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()},
|
||||
})
|
||||
if err != nil {
|
||||
cancel()
|
||||
@@ -58,6 +60,16 @@ var _ platform.Interface = (*platformInterfaceWrapper)(nil)
|
||||
type platformInterfaceWrapper struct {
|
||||
iif PlatformInterface
|
||||
useProcFS bool
|
||||
router adapter.Router
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
|
||||
w.router = router
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bool {
|
||||
return w.iif.UsePlatformAutoDetectInterfaceControl()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
||||
@@ -68,7 +80,7 @@ func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
||||
return nil, E.New("android: unsupported uid options")
|
||||
}
|
||||
@@ -79,12 +91,16 @@ func (w *platformInterfaceWrapper) OpenTun(options tun.Options, platformOptions
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.Name, err = getTunnelName(tunFd)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "query tun name")
|
||||
}
|
||||
dupFd, err := dup(int(tunFd))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dup tun file descriptor")
|
||||
}
|
||||
options.FileDescriptor = dupFd
|
||||
return tun.New(options)
|
||||
return tun.New(*options)
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) {
|
||||
@@ -118,3 +134,36 @@ func (w *platformInterfaceWrapper) FindProcessInfo(ctx context.Context, network
|
||||
packageName, _ := w.iif.PackageNameByUid(uid)
|
||||
return &process.Info{UserId: uid, PackageName: packageName}, nil
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformDefaultInterfaceMonitor() bool {
|
||||
return w.iif.UsePlatformDefaultInterfaceMonitor()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor {
|
||||
return &platformDefaultInterfaceMonitor{
|
||||
platformInterfaceWrapper: w,
|
||||
errorHandler: errorHandler,
|
||||
defaultInterfaceIndex: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformInterfaceGetter() bool {
|
||||
return w.iif.UsePlatformInterfaceGetter()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Interfaces() ([]platform.NetworkInterface, error) {
|
||||
interfaceIterator, err := w.iif.GetInterfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var interfaces []platform.NetworkInterface
|
||||
for _, netInterface := range iteratorToArray[*NetworkInterface](interfaceIterator) {
|
||||
interfaces = append(interfaces, platform.NetworkInterface{
|
||||
Index: int(netInterface.Index),
|
||||
MTU: int(netInterface.MTU),
|
||||
Name: netInterface.Name,
|
||||
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
||||
})
|
||||
}
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator {
|
||||
var _ TunOptions = (*tunOptions)(nil)
|
||||
|
||||
type tunOptions struct {
|
||||
tun.Options
|
||||
*tun.Options
|
||||
option.TunPlatformOptions
|
||||
}
|
||||
|
||||
|
||||
11
experimental/libbox/tun_name_darwin.go
Normal file
11
experimental/libbox/tun_name_darwin.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package libbox
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func getTunnelName(fd int32) (string, error) {
|
||||
return unix.GetsockoptString(
|
||||
int(fd),
|
||||
2, /* #define SYSPROTO_CONTROL 2 */
|
||||
2, /* #define UTUN_OPT_IFNAME 2 */
|
||||
)
|
||||
}
|
||||
26
experimental/libbox/tun_name_linux.go
Normal file
26
experimental/libbox/tun_name_linux.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const ifReqSize = unix.IFNAMSIZ + 64
|
||||
|
||||
func getTunnelName(fd int32) (string, error) {
|
||||
var ifr [ifReqSize]byte
|
||||
var errno syscall.Errno
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.TUNGETIFF),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
return "", fmt.Errorf("failed to get name of TUN device: %w", errno)
|
||||
}
|
||||
return unix.ByteSliceToString(ifr[:]), nil
|
||||
}
|
||||
9
experimental/libbox/tun_name_other.go
Normal file
9
experimental/libbox/tun_name_other.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build !(darwin || linux)
|
||||
|
||||
package libbox
|
||||
|
||||
import "os"
|
||||
|
||||
func getTunnelName(fd int32) (string, error) {
|
||||
return "", os.ErrInvalid
|
||||
}
|
||||
13
go.mod
13
go.mod
@@ -12,7 +12,6 @@ require (
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
|
||||
@@ -26,12 +25,12 @@ require (
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.2.3
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.0
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab
|
||||
github.com/sagernet/sing-vmess v0.1.3
|
||||
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3
|
||||
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
|
||||
|
||||
26
go.sum
26
go.sum
@@ -33,8 +33,6 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
@@ -113,18 +111,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.3 h1:V50MvZ4c3Iij2lYFWPlzL1PyipwSzjGeN9x+Ox89vpk=
|
||||
github.com/sagernet/sing v0.2.3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
|
||||
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.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/sing v0.2.4-0.20230418095640-3b5e6c1812d3 h1:dkH6SEs3yZlUjSXUAn64LUlFAfAgJqiThaWiBKWJ0Q0=
|
||||
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q=
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||
|
||||
@@ -53,7 +53,7 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
} else {
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](ctx, udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
inbound.packetUpstream = inbound.udpNat
|
||||
|
||||
@@ -90,6 +90,9 @@ func (n *Naive) Start() error {
|
||||
n.httpServer = &http.Server{
|
||||
Handler: n,
|
||||
TLSConfig: tlsConfig,
|
||||
BaseContext: func(listener net.Listener) context.Context {
|
||||
return n.ctx
|
||||
},
|
||||
}
|
||||
go func() {
|
||||
var sErr error
|
||||
@@ -606,6 +609,10 @@ func (c *naiveH2Conn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *naiveH2Conn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *naiveH2Conn) UpstreamReader() any {
|
||||
return c.reader
|
||||
}
|
||||
|
||||
@@ -64,11 +64,11 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
var err error
|
||||
switch {
|
||||
case options.Method == shadowsocks.MethodNone:
|
||||
inbound.service = shadowsocks.NewNoneService(options.UDPTimeout, inbound.upstreamContextHandler())
|
||||
inbound.service = shadowsocks.NewNoneService(ctx, options.UDPTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead.List, options.Method):
|
||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
inbound.service, err = shadowaead.NewService(ctx, options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead_2022.List, options.Method):
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(ctx, options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
|
||||
default:
|
||||
err = E.New("unsupported method: ", options.Method)
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
return nil, E.New("unsupported method: " + options.Method)
|
||||
}
|
||||
service, err := shadowaead_2022.NewMultiServiceWithPassword[int](
|
||||
ctx,
|
||||
options.Method,
|
||||
options.Password,
|
||||
udpTimeout,
|
||||
|
||||
@@ -50,6 +50,7 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
service, err := shadowaead_2022.NewRelayServiceWithPassword[int](
|
||||
ctx,
|
||||
options.Method,
|
||||
options.Password,
|
||||
udpTimeout,
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
}
|
||||
tproxy.connHandler = tproxy
|
||||
tproxy.oobPacketHandler = tproxy
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](udpTimeout, tproxy.upstreamContextHandler())
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](ctx, udpTimeout, tproxy.upstreamContextHandler())
|
||||
tproxy.packetUpstream = tproxy.udpNat
|
||||
return tproxy
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ func (t *Tun) Start() error {
|
||||
err error
|
||||
)
|
||||
if t.platformInterface != nil {
|
||||
tunInterface, err = t.platformInterface.OpenTun(t.tunOptions, t.platformOptions)
|
||||
tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions)
|
||||
} else {
|
||||
tunInterface, err = tun.New(t.tunOptions)
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func (h *ShadowsocksR) DialContext(ctx context.Context, network string, destinat
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bufio.BindPacketConn{PacketConn: conn, Addr: destination}, nil
|
||||
return bufio.NewBindPacketConn(conn, destination), nil
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
var _ control.InterfaceFinder = (*myInterfaceFinder)(nil)
|
||||
|
||||
type myInterfaceFinder struct {
|
||||
ifs []net.Interface
|
||||
interfaces []net.Interface
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) update() error {
|
||||
@@ -17,12 +17,16 @@ func (f *myInterfaceFinder) update() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ifs = ifs
|
||||
f.interfaces = ifs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) updateInterfaces(interfaces []net.Interface) {
|
||||
f.interfaces = interfaces
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex int, err error) {
|
||||
for _, netInterface := range f.ifs {
|
||||
for _, netInterface := range f.interfaces {
|
||||
if netInterface.Name == name {
|
||||
return netInterface.Index, nil
|
||||
}
|
||||
@@ -36,7 +40,7 @@ func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex in
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceNameByIndex(index int) (interfaceName string, err error) {
|
||||
for _, netInterface := range f.ifs {
|
||||
for _, netInterface := range f.interfaces {
|
||||
if netInterface.Index == index {
|
||||
return netInterface.Name, nil
|
||||
}
|
||||
|
||||
103
route/router.go
103
route/router.go
@@ -15,12 +15,12 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/geoip"
|
||||
"github.com/sagernet/sing-box/common/geosite"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -42,27 +42,6 @@ import (
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
)
|
||||
|
||||
var warnDefaultInterfaceOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
|
||||
},
|
||||
"route option `default_mark` is only supported on Linux and Windows",
|
||||
)
|
||||
|
||||
var warnDefaultMarkOnNonLinux = warning.New(
|
||||
func() bool {
|
||||
return !C.IsLinux
|
||||
},
|
||||
"route option `default_mark` is only supported on Linux",
|
||||
)
|
||||
|
||||
var warnFindProcessOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
|
||||
},
|
||||
"route option `find_process` is only supported on Linux, Windows, and macOS",
|
||||
)
|
||||
|
||||
var _ adapter.Router = (*Router)(nil)
|
||||
|
||||
type Router struct {
|
||||
@@ -113,16 +92,6 @@ func NewRouter(
|
||||
inbounds []option.Inbound,
|
||||
platformInterface platform.Interface,
|
||||
) (*Router, error) {
|
||||
if options.DefaultInterface != "" {
|
||||
warnDefaultInterfaceOnUnsupportedPlatform.Check()
|
||||
}
|
||||
if options.DefaultMark != 0 {
|
||||
warnDefaultMarkOnNonLinux.Check()
|
||||
}
|
||||
if options.FindProcess {
|
||||
warnFindProcessOnUnsupportedPlatform.Check()
|
||||
}
|
||||
|
||||
router := &Router{
|
||||
ctx: ctx,
|
||||
logger: logFactory.NewLogger("router"),
|
||||
@@ -142,7 +111,7 @@ func NewRouter(
|
||||
defaultMark: options.DefaultMark,
|
||||
platformInterface: platformInterface,
|
||||
}
|
||||
router.dnsClient = dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
||||
router.dnsClient = dns.NewClient(ctx, dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
||||
for i, ruleOptions := range options.Rules {
|
||||
routeRule, err := NewRule(router, router.logger, ruleOptions)
|
||||
if err != nil {
|
||||
@@ -268,29 +237,36 @@ func NewRouter(
|
||||
router.transportMap = transportMap
|
||||
router.transportDomainStrategy = transportDomainStrategy
|
||||
|
||||
needInterfaceMonitor := platformInterface == nil && (options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor()
|
||||
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
||||
}))
|
||||
})
|
||||
|
||||
if needInterfaceMonitor {
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||
if err == nil {
|
||||
router.networkMonitor = networkMonitor
|
||||
networkMonitor.RegisterCallback(router.interfaceFinder.update)
|
||||
if !usePlatformDefaultInterfaceMonitor {
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||
if err != os.ErrInvalid {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
router.networkMonitor = networkMonitor
|
||||
networkMonitor.RegisterCallback(router.interfaceFinder.update)
|
||||
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
|
||||
OverrideAndroidVPN: options.OverrideAndroidVPN,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.New("auto_detect_interface unsupported on current platform")
|
||||
}
|
||||
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
} else {
|
||||
interfaceMonitor := platformInterface.CreateDefaultInterfaceMonitor(router)
|
||||
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
}
|
||||
|
||||
if router.networkMonitor != nil && needInterfaceMonitor {
|
||||
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
|
||||
OverrideAndroidVPN: options.OverrideAndroidVPN,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.New("auto_detect_interface unsupported on current platform")
|
||||
}
|
||||
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
|
||||
needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess
|
||||
needPackageManager := C.IsAndroid && platformInterface == nil && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
|
||||
@@ -823,12 +799,31 @@ func (r *Router) InterfaceFinder() control.InterfaceFinder {
|
||||
return &r.interfaceFinder
|
||||
}
|
||||
|
||||
func (r *Router) UpdateInterfaces() error {
|
||||
if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
|
||||
return r.interfaceFinder.update()
|
||||
} else {
|
||||
interfaces, err := r.platformInterface.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.interfaceFinder.updateInterfaces(common.Map(interfaces, func(it platform.NetworkInterface) net.Interface {
|
||||
return net.Interface{
|
||||
Name: it.Name,
|
||||
Index: it.Index,
|
||||
MTU: it.MTU,
|
||||
}
|
||||
}))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) AutoDetectInterface() bool {
|
||||
return r.autoDetectInterface
|
||||
}
|
||||
|
||||
func (r *Router) AutoDetectInterfaceFunc() control.Func {
|
||||
if r.platformInterface != nil {
|
||||
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
|
||||
return r.platformInterface.AutoDetectInterfaceControl()
|
||||
} else {
|
||||
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||
@@ -1136,7 +1131,7 @@ func (r *Router) NewError(ctx context.Context, err error) {
|
||||
}
|
||||
|
||||
func (r *Router) notifyNetworkUpdate(int) error {
|
||||
if C.IsAndroid {
|
||||
if C.IsAndroid && r.platformInterface == nil {
|
||||
var vpnStatus string
|
||||
if r.interfaceMonitor.AndroidVPNEnabled() {
|
||||
vpnStatus = "enabled"
|
||||
@@ -1148,6 +1143,10 @@ func (r *Router) notifyNetworkUpdate(int) error {
|
||||
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||||
}
|
||||
|
||||
if conntrack.Enabled {
|
||||
conntrack.Close()
|
||||
}
|
||||
|
||||
for _, outbound := range r.outbounds {
|
||||
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
|
||||
if isListener {
|
||||
|
||||
@@ -4,13 +4,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
var warnPackageNameOnNonAndroid = warning.New(
|
||||
func() bool { return !C.IsAndroid },
|
||||
"rule item `package_name` is only supported on Android",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*PackageNameItem)(nil)
|
||||
@@ -21,7 +14,6 @@ type PackageNameItem struct {
|
||||
}
|
||||
|
||||
func NewPackageNameItem(packageNameList []string) *PackageNameItem {
|
||||
warnPackageNameOnNonAndroid.Check()
|
||||
rule := &PackageNameItem{
|
||||
packageNames: packageNameList,
|
||||
packageMap: make(map[string]bool),
|
||||
|
||||
@@ -5,13 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
var warnProcessNameOnNonSupportedPlatform = warning.New(
|
||||
func() bool { return !(C.IsLinux || C.IsWindows || C.IsDarwin) },
|
||||
"rule item `process_name` is only supported on Linux, Windows and macOS",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*ProcessItem)(nil)
|
||||
@@ -22,7 +15,6 @@ type ProcessItem struct {
|
||||
}
|
||||
|
||||
func NewProcessItem(processNameList []string) *ProcessItem {
|
||||
warnProcessNameOnNonSupportedPlatform.Check()
|
||||
rule := &ProcessItem{
|
||||
processes: processNameList,
|
||||
processMap: make(map[string]bool),
|
||||
|
||||
@@ -4,13 +4,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
var warnProcessPathOnNonSupportedPlatform = warning.New(
|
||||
func() bool { return !(C.IsLinux || C.IsWindows || C.IsDarwin) },
|
||||
"rule item `process_path` is only supported on Linux, Windows and macOS",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*ProcessPathItem)(nil)
|
||||
@@ -21,7 +14,6 @@ type ProcessPathItem struct {
|
||||
}
|
||||
|
||||
func NewProcessPathItem(processNameList []string) *ProcessPathItem {
|
||||
warnProcessPathOnNonSupportedPlatform.Check()
|
||||
rule := &ProcessPathItem{
|
||||
processes: processNameList,
|
||||
processMap: make(map[string]bool),
|
||||
|
||||
@@ -4,16 +4,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var warnUserOnNonLinux = warning.New(
|
||||
func() bool { return !C.IsLinux },
|
||||
"rule item `user` is only supported on Linux",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*UserItem)(nil)
|
||||
|
||||
type UserItem struct {
|
||||
@@ -22,7 +15,6 @@ type UserItem struct {
|
||||
}
|
||||
|
||||
func NewUserItem(users []string) *UserItem {
|
||||
warnUserOnNonLinux.Check()
|
||||
userMap := make(map[string]bool)
|
||||
for _, protocol := range users {
|
||||
userMap[protocol] = true
|
||||
|
||||
@@ -4,16 +4,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var warnUserIDOnNonLinux = warning.New(
|
||||
func() bool { return !C.IsLinux },
|
||||
"rule item `user_id` is only supported on Linux",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*UserIdItem)(nil)
|
||||
|
||||
type UserIdItem struct {
|
||||
@@ -22,7 +15,6 @@ type UserIdItem struct {
|
||||
}
|
||||
|
||||
func NewUserIDItem(userIdList []int32) *UserIdItem {
|
||||
warnUserIDOnNonLinux.Check()
|
||||
rule := &UserIdItem{
|
||||
userIds: userIdList,
|
||||
userIdMap: make(map[int32]bool),
|
||||
|
||||
15
test/go.mod
15
test/go.mod
@@ -10,8 +10,8 @@ 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.2-0.20230407053809-308e421e33c2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0
|
||||
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
go.uber.org/goleak v1.2.0
|
||||
@@ -20,7 +20,7 @@ require (
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.14.0 // indirect
|
||||
github.com/Dreamacro/clash v1.15.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
|
||||
@@ -36,13 +36,14 @@ require (
|
||||
github.com/go-chi/cors v1.2.1 // indirect
|
||||
github.com/go-chi/render v1.0.2 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gofrs/uuid/v5 v5.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
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/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 // 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
|
||||
@@ -70,9 +71,9 @@ require (
|
||||
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-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab // indirect
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 // 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
|
||||
@@ -87,7 +88,7 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
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/crypto v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
|
||||
30
test/go.sum
30
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.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
|
||||
github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
|
||||
github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE=
|
||||
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
|
||||
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=
|
||||
@@ -45,6 +45,8 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
@@ -61,8 +63,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-20230327135226-74ae03f2425e h1:8ChxkWKTVYg7LKBvYNLNRnlobgbPrzzossZUoST2T7o=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/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=
|
||||
@@ -126,16 +128,16 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.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 v0.2.4-0.20230418025125-f196b4303e31 h1:qgq8jeY/rbnY9NwYXByO//AP0ByIxnsKUxQx1tOB3W0=
|
||||
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q=
|
||||
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.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-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q=
|
||||
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=
|
||||
@@ -189,8 +191,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
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=
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
|
||||
var muxProtocols = []mux.Protocol{
|
||||
|
||||
59
test/udpnat_test.go
Normal file
59
test/udpnat_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/udpnat"
|
||||
)
|
||||
|
||||
func TestUDPNatClose(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
connCtx, connCancel := common.ContextWithCancelCause(context.Background())
|
||||
defer connCancel(net.ErrClosed)
|
||||
service := udpnat.New[int](ctx, 1, &testUDPNatCloseHandler{connCancel})
|
||||
service.NewPacket(ctx, 0, buf.As([]byte("Hello")), M.Metadata{}, func(natConn N.PacketConn) N.PacketWriter {
|
||||
return &testPacketWriter{}
|
||||
})
|
||||
select {
|
||||
case <-connCtx.Done():
|
||||
if E.IsClosed(connCtx.Err()) {
|
||||
t.Fatal(E.New("conn closed unexpectedly: ", connCtx.Err()))
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("conn not closed")
|
||||
}
|
||||
}
|
||||
|
||||
type testUDPNatCloseHandler struct {
|
||||
done common.ContextCancelCauseFunc
|
||||
}
|
||||
|
||||
func (h *testUDPNatCloseHandler) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||
for {
|
||||
buffer := buf.NewPacket()
|
||||
_, err := conn.ReadPacket(buffer)
|
||||
buffer.Release()
|
||||
if err != nil {
|
||||
h.done(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *testUDPNatCloseHandler) NewError(ctx context.Context, err error) {
|
||||
}
|
||||
|
||||
type testPacketWriter struct{}
|
||||
|
||||
func (t *testPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
return nil
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/vless"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -119,7 +119,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
func (t *Transport) fetchInterface() (*net.Interface, error) {
|
||||
interfaceName := t.interfaceName
|
||||
if t.autoInterface {
|
||||
if t.router.NetworkMonitor() == nil {
|
||||
if t.router.InterfaceMonitor() == nil {
|
||||
return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
|
||||
}
|
||||
interfaceName = t.router.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})
|
||||
|
||||
@@ -535,6 +535,10 @@ func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) Read(b []byte) (n int, err error) {
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
|
||||
|
||||
if hostOpt, loaded := pluginOpts.Get("host"); loaded {
|
||||
host = hostOpt
|
||||
tlsOptions.ServerName = hostOpt
|
||||
}
|
||||
if pathOpt, loaded := pluginOpts.Get("path"); loaded {
|
||||
path = pathOpt
|
||||
|
||||
@@ -136,3 +136,11 @@ func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) er
|
||||
func (c *PacketConn) FrontHeadroom() int {
|
||||
return M.MaxSocksaddrLength + 4
|
||||
}
|
||||
|
||||
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
@@ -48,9 +48,6 @@ func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socks
|
||||
case C.V2RayTransportTypeHTTP:
|
||||
return v2rayhttp.NewClient(ctx, dialer, serverAddr, options.HTTPOptions, tlsConfig)
|
||||
case C.V2RayTransportTypeGRPC:
|
||||
if tlsConfig == nil {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
return NewGRPCClient(ctx, dialer, serverAddr, options.GRPCOptions, tlsConfig)
|
||||
case C.V2RayTransportTypeWebsocket:
|
||||
return v2raywebsocket.NewClient(ctx, dialer, serverAddr, options.WebsocketOptions, tlsConfig), nil
|
||||
|
||||
@@ -36,7 +36,9 @@ type Client struct {
|
||||
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
|
||||
var dialOptions []grpc.DialOption
|
||||
if tlsConfig != nil {
|
||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||
if len(tlsConfig.NextProtos()) == 0 {
|
||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||
}
|
||||
dialOptions = append(dialOptions, grpc.WithTransportCredentials(NewTLSTransportCredentials(tlsConfig)))
|
||||
} else {
|
||||
dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
|
||||
@@ -81,6 +81,10 @@ func (c *GRPCConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *GRPCConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *GRPCConn) Upstream() any {
|
||||
return c.GunService
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package v2raygrpclite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -13,6 +12,7 @@ import (
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
@@ -31,56 +31,63 @@ type Client struct {
|
||||
ctx context.Context
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
transport http.RoundTripper
|
||||
transport *http2.Transport
|
||||
options option.V2RayGRPCOptions
|
||||
url *url.URL
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
|
||||
var transport http.RoundTripper
|
||||
if tlsConfig == nil {
|
||||
transport = &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||
},
|
||||
}
|
||||
var host string
|
||||
if tlsConfig != nil && tlsConfig.ServerName() != "" {
|
||||
host = M.ParseSocksaddrHostPort(tlsConfig.ServerName(), serverAddr.Port).String()
|
||||
} else {
|
||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||
transport = &http2.Transport{
|
||||
ReadIdleTimeout: time.Duration(options.IdleTimeout),
|
||||
PingTimeout: time.Duration(options.PingTimeout),
|
||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
||||
conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tls.ClientHandshake(ctx, conn, tlsConfig)
|
||||
},
|
||||
}
|
||||
host = serverAddr.String()
|
||||
}
|
||||
return &Client{
|
||||
client := &Client{
|
||||
ctx: ctx,
|
||||
dialer: dialer,
|
||||
serverAddr: serverAddr,
|
||||
options: options,
|
||||
transport: transport,
|
||||
transport: &http2.Transport{
|
||||
ReadIdleTimeout: time.Duration(options.IdleTimeout),
|
||||
PingTimeout: time.Duration(options.PingTimeout),
|
||||
DisableCompression: true,
|
||||
},
|
||||
url: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: serverAddr.String(),
|
||||
Path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
|
||||
Scheme: "https",
|
||||
Host: host,
|
||||
Path: "/" + options.ServiceName + "/Tun",
|
||||
RawPath: "/" + url.PathEscape(options.ServiceName) + "/Tun",
|
||||
},
|
||||
}
|
||||
|
||||
if tlsConfig == nil {
|
||||
client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||
}
|
||||
} else {
|
||||
if len(tlsConfig.NextProtos()) == 0 {
|
||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||
}
|
||||
client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
||||
conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tls.ClientHandshake(ctx, conn, tlsConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
pipeInReader, pipeInWriter := io.Pipe()
|
||||
request := &http.Request{
|
||||
Method: http.MethodPost,
|
||||
Body: pipeInReader,
|
||||
URL: c.url,
|
||||
Proto: "HTTP/2",
|
||||
ProtoMajor: 2,
|
||||
Header: defaultClientHeader,
|
||||
Method: http.MethodPost,
|
||||
Body: pipeInReader,
|
||||
URL: c.url,
|
||||
Header: defaultClientHeader,
|
||||
}
|
||||
request = request.WithContext(ctx)
|
||||
conn := newLateGunConn(pipeInWriter)
|
||||
@@ -88,6 +95,9 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
response, err := c.transport.RoundTrip(request)
|
||||
if err == nil {
|
||||
conn.setup(response.Body, nil)
|
||||
} else if response.StatusCode != 200 {
|
||||
response.Body.Close()
|
||||
conn.setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
|
||||
} else {
|
||||
conn.setup(nil, err)
|
||||
}
|
||||
@@ -96,6 +106,8 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
v2rayhttp.CloseIdleConnections(c.transport)
|
||||
if c.transport != nil {
|
||||
v2rayhttp.CloseIdleConnections(c.transport)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
dataLen := buffer.Len()
|
||||
varLen := rw.UVariantLen(uint64(dataLen))
|
||||
header := buffer.ExtendHeader(6 + varLen)
|
||||
_ = header[6]
|
||||
header[0] = 0x00
|
||||
binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen))
|
||||
header[5] = 0x0A
|
||||
@@ -145,28 +146,17 @@ func (c *GunConn) RemoteAddr() net.Addr {
|
||||
}
|
||||
|
||||
func (c *GunConn) SetDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetWriteDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetWriteDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *GunConn) SetReadDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetReadDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetReadDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *GunConn) SetWriteDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetWriteDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetWriteDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *GunConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@ package v2raygrpclite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -45,13 +43,16 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t
|
||||
server := &Server{
|
||||
tlsConfig: tlsConfig,
|
||||
handler: handler,
|
||||
path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
|
||||
path: "/" + options.ServiceName + "/Tun",
|
||||
h2Server: &http2.Server{
|
||||
IdleTimeout: time.Duration(options.IdleTimeout),
|
||||
},
|
||||
}
|
||||
server.httpServer = &http.Server{
|
||||
Handler: server,
|
||||
BaseContext: func(net.Listener) context.Context {
|
||||
return ctx
|
||||
},
|
||||
}
|
||||
server.h2cHandler = h2c.NewHandler(server, server.h2Server)
|
||||
return server, nil
|
||||
|
||||
@@ -43,7 +43,9 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
},
|
||||
}
|
||||
} else {
|
||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||
if len(tlsConfig.NextProtos()) == 0 {
|
||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||
}
|
||||
transport = &http2.Transport{
|
||||
ReadIdleTimeout: time.Duration(options.IdleTimeout),
|
||||
PingTimeout: time.Duration(options.PingTimeout),
|
||||
@@ -137,15 +139,16 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
|
||||
default:
|
||||
request.Host = c.host[rand.Intn(hostLen)]
|
||||
}
|
||||
conn := newLateHTTPConn(pipeInWriter)
|
||||
conn := NewLateHTTPConn(pipeInWriter)
|
||||
go func() {
|
||||
response, err := c.transport.RoundTrip(request)
|
||||
if err != nil {
|
||||
conn.setup(nil, err)
|
||||
conn.Setup(nil, err)
|
||||
} else if response.StatusCode != 200 {
|
||||
conn.setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
|
||||
response.Body.Close()
|
||||
conn.Setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
|
||||
} else {
|
||||
conn.setup(response.Body, nil)
|
||||
conn.Setup(response.Body, nil)
|
||||
}
|
||||
}()
|
||||
return conn, nil
|
||||
|
||||
@@ -140,14 +140,14 @@ func NewHTTPConn(reader io.Reader, writer io.Writer) HTTP2Conn {
|
||||
}
|
||||
}
|
||||
|
||||
func newLateHTTPConn(writer io.Writer) *HTTP2Conn {
|
||||
func NewLateHTTPConn(writer io.Writer) *HTTP2Conn {
|
||||
return &HTTP2Conn{
|
||||
create: make(chan struct{}),
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HTTP2Conn) setup(reader io.Reader, err error) {
|
||||
func (c *HTTP2Conn) Setup(reader io.Reader, err error) {
|
||||
c.reader = reader
|
||||
c.err = err
|
||||
close(c.create)
|
||||
@@ -182,32 +182,21 @@ func (c *HTTP2Conn) RemoteAddr() net.Addr {
|
||||
}
|
||||
|
||||
func (c *HTTP2Conn) SetDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetWriteDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetWriteDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *HTTP2Conn) SetReadDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetReadDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetReadDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *HTTP2Conn) SetWriteDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetWriteDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetWriteDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *HTTP2Conn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type ServerHTTPConn struct {
|
||||
HTTP2Conn
|
||||
flusher http.Flusher
|
||||
|
||||
@@ -70,6 +70,9 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t
|
||||
Handler: server,
|
||||
ReadHeaderTimeout: C.TCPTimeout,
|
||||
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
|
||||
BaseContext: func(net.Listener) context.Context {
|
||||
return ctx
|
||||
},
|
||||
}
|
||||
server.h2cHandler = h2c.NewHandler(server, server.h2Server)
|
||||
return server, nil
|
||||
|
||||
@@ -77,6 +77,10 @@ func (c *WebsocketConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) Upstream() any {
|
||||
return c.Conn.NetConn()
|
||||
}
|
||||
@@ -214,6 +218,10 @@ func (c *EarlyWebsocketConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) Upstream() any {
|
||||
return common.PtrOrNil(c.conn)
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsCon
|
||||
Handler: server,
|
||||
ReadHeaderTimeout: C.TCPTimeout,
|
||||
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
|
||||
BaseContext: func(net.Listener) context.Context {
|
||||
return ctx
|
||||
},
|
||||
}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
@@ -132,6 +132,10 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
||||
return c.protocolConn.Write(b)
|
||||
}
|
||||
|
||||
func (c *Conn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Conn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
@@ -212,6 +216,10 @@ func (c *PacketConn) FrontHeadroom() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
|
||||
type Service[T comparable] struct {
|
||||
@@ -134,6 +134,14 @@ func (c *serverConn) Write(b []byte) (n int, err error) {
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
|
||||
func (c *serverConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *serverConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
type serverPacketConn struct {
|
||||
N.ExtendedConn
|
||||
responseWriter io.Writer
|
||||
|
||||
@@ -360,6 +360,10 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
||||
return buffers
|
||||
}
|
||||
|
||||
func (c *VisionConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *VisionConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user