Add USB/IP service

This commit is contained in:
世界
2026-06-14 19:19:46 +08:00
parent 12f160ab5f
commit 7cffd47dd4
30 changed files with 3117 additions and 246 deletions
+63
View File
@@ -0,0 +1,63 @@
//go:build with_usbip && (linux || (darwin && cgo) || windows)
package usbip
import (
"context"
"github.com/sagernet/sing-box/adapter"
boxService "github.com/sagernet/sing-box/adapter/service"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-usbip"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
)
type ClientService struct {
boxService.Adapter
ctx context.Context
logger log.ContextLogger
inner *usbip.ClientService
}
func NewClientService(ctx context.Context, logger log.ContextLogger, tag string, options option.USBIPClientServiceOptions) (adapter.Service, error) {
serviceDialer, err := dialer.NewWithOptions(dialer.Options{
Context: ctx,
Options: option.DialerOptions{
Detour: options.Detour,
},
RemoteIsDomain: true,
})
if err != nil {
return nil, E.Cause(err, "create dialer")
}
inner, err := usbip.NewClientService(ctx, usbip.ClientOptions{
Logger: logger,
Dialer: serviceDialer,
ServerAddress: M.ParseSocksaddr(options.Server),
Devices: toDeviceMatches(options.Devices),
})
if err != nil {
return nil, err
}
return &ClientService{
Adapter: boxService.NewAdapter(C.TypeUSBIPClient, tag),
ctx: ctx,
logger: logger,
inner: inner,
}, nil
}
func (s *ClientService) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateStart {
return nil
}
return s.inner.Start()
}
func (s *ClientService) Close() error {
return s.inner.Close()
}
+107
View File
@@ -0,0 +1,107 @@
//go:build with_usbip && (linux || (darwin && cgo) || windows)
package usbip
import (
"context"
"net"
"github.com/sagernet/sing-box/adapter"
boxService "github.com/sagernet/sing-box/adapter/service"
"github.com/sagernet/sing-box/common/listener"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-usbip"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
)
type ServerService struct {
boxService.Adapter
ctx context.Context
logger log.ContextLogger
inner *usbip.ServerService
}
type dynamicServerService struct {
ServerService
host *usbip.DynamicHost
}
var _ adapter.USBIPDynamicServer = (*dynamicServerService)(nil)
func NewServerService(ctx context.Context, logger log.ContextLogger, tag string, options option.USBIPServerServiceOptions) (adapter.Service, error) {
listenOptions := options.ListenOptions
if listenOptions.ListenPort == 0 {
listenOptions.ListenPort = usbip.DefaultPort
}
boxListener := listener.New(listener.Options{
Context: ctx,
Logger: logger,
Network: []string{N.NetworkTCP},
Listen: listenOptions,
})
serverOptions := usbip.ServerOptions{
Logger: logger,
Listen: func(context.Context) (net.Listener, error) {
return boxListener.ListenTCP()
},
}
base := ServerService{
Adapter: boxService.NewAdapter(C.TypeUSBIPServer, tag),
ctx: ctx,
logger: logger,
}
providerType := options.Provider
if providerType == "" {
providerType = option.USBIPProviderDefault
}
switch providerType {
case option.USBIPProviderDefault:
defaultOptions, isDefault := options.Options.(*option.USBIPDefaultProviderOptions)
if isDefault {
serverOptions.Devices = toDeviceMatches(defaultOptions.Devices)
}
inner, err := usbip.NewServerService(ctx, serverOptions)
if err != nil {
return nil, err
}
base.inner = inner
return &base, nil
case option.USBIPProviderDynamic:
host := usbip.NewDynamicHost(logger)
inner, err := usbip.NewDynamicServerService(ctx, serverOptions, host)
if err != nil {
return nil, err
}
base.inner = inner
return &dynamicServerService{ServerService: base, host: host}, nil
default:
return nil, E.New("unknown usbip provider type: ", providerType)
}
}
func (s *ServerService) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateStart {
return nil
}
return s.inner.Start()
}
func (s *ServerService) Close() error {
return s.inner.Close()
}
func (s *dynamicServerService) AddDevice(info usbip.DynamicDeviceInfo, transport usbip.DeviceTransport) (string, error) {
return s.host.AddDevice(info, transport)
}
func (s *dynamicServerService) RemoveDevice(busID string) {
s.host.RemoveDevice(busID)
}
func (s *dynamicServerService) SubscribeDevices(ctx context.Context, listener func([]usbip.ControlDeviceInfo)) {
s.inner.SubscribeDevices(ctx, listener)
}
+31
View File
@@ -0,0 +1,31 @@
//go:build with_usbip && (linux || (darwin && cgo) || windows)
package usbip
import (
boxService "github.com/sagernet/sing-box/adapter/service"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-usbip"
)
func RegisterService(registry *boxService.Registry) {
boxService.Register[option.USBIPServerServiceOptions](registry, C.TypeUSBIPServer, NewServerService)
boxService.Register[option.USBIPClientServiceOptions](registry, C.TypeUSBIPClient, NewClientService)
}
func toDeviceMatches(matches []option.USBIPDeviceMatch) []usbip.DeviceMatch {
if len(matches) == 0 {
return nil
}
deviceMatches := make([]usbip.DeviceMatch, 0, len(matches))
for _, match := range matches {
deviceMatches = append(deviceMatches, usbip.DeviceMatch{
BusID: match.BusID,
VendorID: match.VendorID,
ProductID: match.ProductID,
Serial: match.Serial,
})
}
return deviceMatches
}
+3
View File
@@ -0,0 +1,3 @@
//go:build !with_usbip || !(linux || (darwin && cgo) || windows)
package usbip