Files
2026-07-13 19:12:05 +08:00

409 lines
12 KiB
Go

package libbox
import (
"crypto/rand"
"encoding/hex"
"errors"
"net"
"net/netip"
"runtime"
"strconv"
"sync"
"syscall"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
"github.com/sagernet/sing-box/option"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
)
var _ adapter.PlatformInterface = (*platformInterfaceWrapper)(nil)
type platformInterfaceWrapper struct {
iif PlatformInterface
useProcFS bool
networkManager adapter.NetworkManager
myTunName string
myTunAddress []netip.Addr
defaultInterfaceAccess sync.Mutex
defaultInterface *control.Interface
isExpensive bool
isConstrained bool
}
func (w *platformInterfaceWrapper) Initialize(networkManager adapter.NetworkManager) error {
w.networkManager = networkManager
return nil
}
func (w *platformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bool {
return w.iif.UsePlatformAutoDetectInterfaceControl()
}
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error {
return w.iif.AutoDetectInterfaceControl(int32(fd))
}
func (w *platformInterfaceWrapper) UsePlatformInterface() bool {
return true
}
func (w *platformInterfaceWrapper) OpenInterface(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
return nil, E.New("platform: unsupported uid options")
}
if len(options.IncludeAndroidUser) > 0 {
return nil, E.New("platform: unsupported android_user option")
}
routeRanges, err := options.BuildAutoRouteRanges(true)
if err != nil {
return nil, err
}
tunFd, err := w.iif.OpenTun(&tunOptions{options, routeRanges, platformOptions})
if err != nil {
return nil, err
}
options.Name, err = getTunnelName(tunFd)
if err != nil {
return nil, E.Cause(err, "query tun name")
}
options.InterfaceMonitor.RegisterMyInterface(options.Name)
dupFd, err := dup(int(tunFd))
if err != nil {
return nil, E.Cause(err, "dup tun file descriptor")
}
options.FileDescriptor = dupFd
w.myTunName = options.Name
w.myTunAddress = myTunAddress(options)
w.iif.RegisterMyInterface(options.Name)
return tun.New(*options)
}
func (w *platformInterfaceWrapper) ProcessPlatformOptions(options option.TunPlatformOptions) error {
return nil
}
func myTunAddress(options *tun.Options) []netip.Addr {
addresses := make([]netip.Addr, 0, len(options.Inet4Address)+len(options.Inet6Address))
for _, prefix := range options.Inet4Address {
addresses = append(addresses, prefix.Addr())
}
for _, prefix := range options.Inet6Address {
addresses = append(addresses, prefix.Addr())
}
return addresses
}
func (w *platformInterfaceWrapper) MyInterfaceAddress() []netip.Addr {
return w.myTunAddress
}
func (w *platformInterfaceWrapper) UsePlatformDefaultInterfaceMonitor() bool {
return true
}
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
return &platformDefaultInterfaceMonitor{
platformInterfaceWrapper: w,
logger: logger,
}
}
func (w *platformInterfaceWrapper) UsePlatformNetworkInterfaces() bool {
return true
}
func (w *platformInterfaceWrapper) NetworkInterfaces() ([]adapter.NetworkInterface, error) {
interfaceIterator, err := w.iif.GetInterfaces()
if err != nil {
return nil, err
}
var interfaces []adapter.NetworkInterface
for _, netInterface := range iteratorToArray[*NetworkInterface](interfaceIterator) {
w.defaultInterfaceAccess.Lock()
// (GOOS=windows) SA4006: this value of `isDefault` is never used
// Why not used?
//nolint:staticcheck
isDefault := netInterface.Name != w.myTunName && w.defaultInterface != nil && int(netInterface.Index) == w.defaultInterface.Index
w.defaultInterfaceAccess.Unlock()
interfaces = append(interfaces, adapter.NetworkInterface{
Interface: control.Interface{
Index: int(netInterface.Index),
MTU: int(netInterface.MTU),
Name: netInterface.Name,
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
Flags: linkFlags(uint32(netInterface.Flags)),
},
Type: C.InterfaceType(netInterface.Type),
DNSServers: iteratorToArray[string](netInterface.DNSServer),
Expensive: netInterface.Metered || isDefault && w.isExpensive,
Constrained: isDefault && w.isConstrained,
})
}
interfaces = common.UniqBy(interfaces, func(it adapter.NetworkInterface) string {
return it.Name
})
return interfaces, nil
}
func (w *platformInterfaceWrapper) UnderNetworkExtension() bool {
return w.iif.UnderNetworkExtension()
}
func (w *platformInterfaceWrapper) NetworkExtensionIncludeAllNetworks() bool {
return w.iif.IncludeAllNetworks()
}
func (w *platformInterfaceWrapper) ClearDNSCache() {
w.iif.ClearDNSCache()
}
func (w *platformInterfaceWrapper) RequestPermissionForWIFIState() error {
return nil
}
func (w *platformInterfaceWrapper) UsePlatformWIFIMonitor() bool {
return true
}
func (w *platformInterfaceWrapper) ReadWIFIState() adapter.WIFIState {
wifiState := w.iif.ReadWIFIState()
if wifiState == nil {
return adapter.WIFIState{}
}
return (adapter.WIFIState)(*wifiState)
}
func (w *platformInterfaceWrapper) UsePlatformConnectionOwnerFinder() bool {
return true
}
func (w *platformInterfaceWrapper) FindConnectionOwner(request *adapter.FindConnectionOwnerRequest) (*adapter.ConnectionOwner, error) {
if w.useProcFS {
var source netip.AddrPort
var destination netip.AddrPort
sourceAddr, _ := netip.ParseAddr(request.SourceAddress)
source = netip.AddrPortFrom(sourceAddr, uint16(request.SourcePort))
destAddr, _ := netip.ParseAddr(request.DestinationAddress)
destination = netip.AddrPortFrom(destAddr, uint16(request.DestinationPort))
var network string
switch request.IpProtocol {
case int32(syscall.IPPROTO_TCP):
network = "tcp"
case int32(syscall.IPPROTO_UDP):
network = "udp"
default:
return nil, E.New("unknown protocol: ", request.IpProtocol)
}
uid := procfs.ResolveSocketByProcSearch(network, source, destination)
if uid == -1 {
return nil, E.New("procfs: not found")
}
return &adapter.ConnectionOwner{
UserId: uid,
}, nil
}
result, err := w.iif.FindConnectionOwner(request.IpProtocol, request.SourceAddress, request.SourcePort, request.DestinationAddress, request.DestinationPort)
if err != nil {
return nil, err
}
return &adapter.ConnectionOwner{
UserId: result.UserId,
UserName: result.UserName,
ProcessPath: result.ProcessPath,
AndroidPackageNames: result.androidPackageNames,
}, nil
}
func (w *platformInterfaceWrapper) DisableColors() bool {
return runtime.GOOS != "android"
}
func (w *platformInterfaceWrapper) UsePlatformNotification() bool {
return true
}
func (w *platformInterfaceWrapper) SendNotification(notification *adapter.Notification) error {
return w.iif.SendNotification((*Notification)(notification))
}
func (w *platformInterfaceWrapper) UsePlatformNeighborResolver() bool {
return true
}
func (w *platformInterfaceWrapper) StartNeighborMonitor(listener adapter.NeighborUpdateListener) error {
return w.iif.StartNeighborMonitor(&neighborUpdateListenerWrapper{listener: listener})
}
func (w *platformInterfaceWrapper) CloseNeighborMonitor(listener adapter.NeighborUpdateListener) error {
return w.iif.CloseNeighborMonitor(nil)
}
func (w *platformInterfaceWrapper) UsePlatformShell() bool {
return w.iif.UsePlatformShell()
}
func (w *platformInterfaceWrapper) CheckPlatformShell() error {
return w.iif.CheckPlatformShell()
}
func (w *platformInterfaceWrapper) OpenShellSession(user *adapter.PlatformUser, command string, environ []string, term string, rows int32, cols int32) (adapter.ShellSession, error) {
libboxUser := &PlatformUser{
Username: user.Username,
Uid: int32(user.Uid),
Gid: int32(user.Gid),
HomeDir: user.HomeDir,
Shell: user.Shell,
}
if len(user.Groups) > 0 {
libboxUser.SetGroups(newIterator(common.Map(user.Groups, func(g int) int32 { return int32(g) })))
}
session, err := w.iif.OpenShellSession(libboxUser, command, newIterator(environ), term, rows, cols)
if err != nil {
return nil, err
}
return session, nil
}
func (w *platformInterfaceWrapper) LookupSFTPServer() (string, error) {
return w.iif.LookupSFTPServer()
}
func (w *platformInterfaceWrapper) ReadSystemSSHHostKey() ([]byte, error) {
result, err := w.iif.ReadSystemSSHHostKey()
if err != nil {
return nil, err
}
return []byte(result), nil
}
func (w *platformInterfaceWrapper) TailscaleHostname() string {
return w.iif.TailscaleHostname()
}
func (w *platformInterfaceWrapper) UsePlatformBridge() bool {
return w.iif.UsePlatformBridge()
}
func (w *platformInterfaceWrapper) CreateBridge(options adapter.BridgeOptions) (adapter.BridgeSession, error) {
bridgeOptions := &BridgeOptions{
BridgeName: options.BridgeName,
MTU: int32(options.MTU),
Interface: options.Interface,
RuleIndex: int32(options.RuleIndex),
RouteTable: int32(options.RouteTable),
}
if options.Inet4Port.IsValid() {
bridgeOptions.Inet4Port = options.Inet4Port.String()
}
if options.Inet6Port.IsValid() {
bridgeOptions.Inet6Port = options.Inet6Port.String()
}
session, err := w.iif.CreateBridge(bridgeOptions)
if err != nil {
return nil, err
}
return &bridgeSessionWrapper{session}, nil
}
type bridgeSessionWrapper struct {
session BridgeSession
}
func (w *bridgeSessionWrapper) FileDescriptor() int {
return int(w.session.FileDescriptor())
}
func (w *bridgeSessionWrapper) Name() string {
return w.session.Name()
}
func (w *bridgeSessionWrapper) Inet6Active() bool {
return w.session.Inet6Active()
}
func (w *bridgeSessionWrapper) SetEgress(interfaceName string) error {
return w.session.SetEgress(interfaceName)
}
func (w *bridgeSessionWrapper) Close() error {
return w.session.Close()
}
func (w *platformInterfaceWrapper) LookupUser(username string) (*adapter.PlatformUser, error) {
platformUser, err := w.iif.LookupUser(username)
if err != nil {
return nil, err
}
return &adapter.PlatformUser{
Username: platformUser.Username,
Uid: int(platformUser.Uid),
Gid: int(platformUser.Gid),
HomeDir: platformUser.HomeDir,
Shell: platformUser.Shell,
Groups: common.Map(iteratorToArray[int32](platformUser.Groups()), func(g int32) int { return int(g) }),
}, nil
}
type neighborUpdateListenerWrapper struct {
listener adapter.NeighborUpdateListener
}
func (w *neighborUpdateListenerWrapper) UpdateNeighborTable(entries NeighborEntryIterator) {
var result []adapter.NeighborEntry
for entries.HasNext() {
entry := entries.Next()
if entry == nil {
continue
}
address, err := netip.ParseAddr(entry.Address)
if err != nil {
continue
}
macAddress, err := net.ParseMAC(entry.MacAddress)
if err != nil {
continue
}
result = append(result, adapter.NeighborEntry{
Address: address,
MACAddress: macAddress,
Hostname: entry.Hostname,
})
}
w.listener.UpdateNeighborTable(result)
}
func AvailablePort(startPort int32) (int32, error) {
for port := int(startPort); ; port++ {
if port > 65535 {
return 0, E.New("no available port found")
}
listener, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(int(port))))
if err != nil {
if errors.Is(err, syscall.EADDRINUSE) {
continue
}
return 0, E.Cause(err, "find available port")
}
err = listener.Close()
if err != nil {
return 0, E.Cause(err, "close listener")
}
return int32(port), nil
}
}
func RandomHex(length int32) *StringBox {
bytes := make([]byte, length)
common.Must1(rand.Read(bytes))
return wrapString(hex.EncodeToString(bytes))
}