c3946c00a3
Windows export now reports the real USB link speed, probed from the parent hub (IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX and _V2 for SuperSpeedPlus), so SuperSpeed devices route to the correct root-hub speed domain instead of advertising speed=0. - protocol: pin DeviceInfoTruncated/DeviceInterface wire sizes with two-sided compile-time assertions so a struct change fails the build instead of silently mis-bounding the reader - server: bound inbound connections with a handshake read deadline and a per-iteration idle deadline on the control loop, plus write deadlines on control writes; clear the deadline before the conn becomes a data session - server: serialize import reservation under reconcileAccess so a reserve cannot interleave a reconcile pass that would release a busy device - data: validate CMD_SUBMIT iso descriptor offset/length against the transfer buffer before forwarding to a platform engine - darwin: make darwinUSBHostDevice.Close idempotent via sync.Once to avoid a double close/free under concurrent shutdown - windows: guard windowsExport.device with a mutex and hand the claimed handle to a single closer
88 lines
2.0 KiB
Go
88 lines
2.0 KiB
Go
//go:build linux || (darwin && cgo) || windows
|
|
|
|
package usbip
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"time"
|
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
)
|
|
|
|
// serverHandshakeTimeout bounds how long an unauthenticated peer may hold a
|
|
// goroutine before sending its preface/hello. The post-handshake control loop
|
|
// uses controlReadTimeout, which tolerates the client's controlPingInterval.
|
|
const serverHandshakeTimeout = 10 * time.Second
|
|
|
|
func (s *ServerService) acceptLoop(ln net.Listener) {
|
|
for {
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
select {
|
|
case <-s.ctx.Done():
|
|
return
|
|
default:
|
|
}
|
|
if E.IsClosed(err) {
|
|
return
|
|
}
|
|
//nolint:staticcheck // net.Error.Temporary predates net.ErrClosed; replacement needs a separate audit.
|
|
if netError, isNetError := err.(net.Error); isNetError && netError.Temporary() {
|
|
s.logger.Error("accept: ", err)
|
|
if !sleepCtx(s.ctx, 200*time.Millisecond) {
|
|
return
|
|
}
|
|
continue
|
|
}
|
|
s.logger.Error("accept: ", err)
|
|
return
|
|
}
|
|
go s.dispatchConn(conn)
|
|
}
|
|
}
|
|
|
|
func (s *ServerService) dispatchConn(conn net.Conn) {
|
|
cancelClose := closeConnOnContextDone(s.ctx, conn)
|
|
defer cancelClose()
|
|
_ = conn.SetReadDeadline(time.Now().Add(serverHandshakeTimeout))
|
|
var prefix [controlPrefaceSize]byte
|
|
_, err := io.ReadFull(conn, prefix[:])
|
|
if err != nil {
|
|
s.logger.Debug("read connection preface: ", err)
|
|
_ = conn.Close()
|
|
return
|
|
}
|
|
if bytes.Equal(prefix[:], controlPreface[:]) {
|
|
s.handleControlConn(conn)
|
|
return
|
|
}
|
|
s.handleStandardConn(conn, ParseOpHeader(prefix[:]))
|
|
}
|
|
|
|
func (s *ServerService) readControlConn(sub *exportSubscriber, done chan<- struct{}) {
|
|
defer close(done)
|
|
var reader controlReader
|
|
for {
|
|
err := sub.conn.SetReadDeadline(time.Now().Add(controlReadTimeout))
|
|
if err != nil {
|
|
return
|
|
}
|
|
message, err := reader.read(sub.conn)
|
|
if err != nil {
|
|
return
|
|
}
|
|
frame := message.Frame
|
|
switch frame.Type {
|
|
case controlFramePing:
|
|
s.ledger.enqueueFrame(sub, controlFrame{
|
|
Type: controlFramePong,
|
|
Version: controlProtocolVersion,
|
|
})
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
}
|