Files
sing-box/service/usbip/data_protocol_test.go
T
2026-06-09 10:42:26 +08:00

360 lines
9.8 KiB
Go

package usbip
import (
"bytes"
"encoding/binary"
"errors"
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestUSBIPSubmitCommandRoundTripOut(t *testing.T) {
t.Parallel()
expected := SubmitCommand{
Header: DataHeader{
Command: CmdSubmit,
SeqNum: 7,
DevID: 0x00030009,
Direction: USBIPDirOut,
Endpoint: 2,
},
TransferFlags: 0x400,
TransferBufferLength: 3,
StartFrame: 11,
NumberOfPackets: nonIsoPacketCount,
Interval: 4,
Setup: [8]byte{0, 1, 2, 3, 4, 5, 6, 7},
Buffer: []byte{1, 2, 3},
}
var buffer bytes.Buffer
require.NoError(t, WriteSubmitCommand(&buffer, expected))
header, err := ReadDataHeader(&buffer)
require.NoError(t, err)
require.Equal(t, expected.Header, header)
actual, err := ReadSubmitCommandBody(&buffer, header)
require.NoError(t, err)
require.Equal(t, expected, actual)
}
func TestUSBIPSubmitCommandRoundTripInOmitsCommandPayload(t *testing.T) {
t.Parallel()
expected := SubmitCommand{
Header: DataHeader{
Command: CmdSubmit,
SeqNum: 8,
DevID: 0x00030009,
Direction: USBIPDirIn,
Endpoint: 1,
},
TransferBufferLength: 4,
NumberOfPackets: nonIsoPacketCount,
}
var buffer bytes.Buffer
require.NoError(t, WriteSubmitCommand(&buffer, expected))
require.Equal(t, dataHeaderSize, buffer.Len())
header, err := ReadDataHeader(&buffer)
require.NoError(t, err)
actual, err := ReadSubmitCommandBody(&buffer, header)
require.NoError(t, err)
require.Equal(t, expected.Header, actual.Header)
require.Equal(t, expected.TransferBufferLength, actual.TransferBufferLength)
require.Empty(t, actual.Buffer)
}
func TestUSBIPSubmitResponseRoundTripInWithIsoPackets(t *testing.T) {
t.Parallel()
expected := SubmitResponse{
Header: DataHeader{
Command: RetSubmit,
SeqNum: 9,
DevID: 0x00030009,
Direction: USBIPDirIn,
Endpoint: 3,
},
Status: 0,
ActualLength: 4,
StartFrame: 21,
NumberOfPackets: 2,
ErrorCount: 1,
Buffer: []byte{9, 8, 7, 6},
IsoPackets: []IsoPacketDescriptor{
{Offset: 0, Length: 2, ActualLength: 2, Status: 0},
{Offset: 2, Length: 2, ActualLength: 1, Status: -32},
},
}
var buffer bytes.Buffer
require.NoError(t, WriteSubmitResponse(&buffer, expected))
header, err := ReadDataHeader(&buffer)
require.NoError(t, err)
require.Equal(t, DataHeader{Command: RetSubmit, SeqNum: expected.Header.SeqNum}, header)
actual, err := ReadSubmitResponseBody(&buffer, header, expected.Header.Direction)
require.NoError(t, err)
expected.Header = DataHeader{Command: RetSubmit, SeqNum: expected.Header.SeqNum}
require.Equal(t, expected, actual)
}
func TestUSBIPSubmitResponseRoundTripOutOmitsResponsePayload(t *testing.T) {
t.Parallel()
expected := SubmitResponse{
Header: DataHeader{
Command: RetSubmit,
SeqNum: 10,
DevID: 0x00030009,
Direction: USBIPDirOut,
Endpoint: 2,
},
Status: 0,
ActualLength: 3,
NumberOfPackets: nonIsoPacketCount,
}
var buffer bytes.Buffer
require.NoError(t, WriteSubmitResponse(&buffer, expected))
require.Equal(t, dataHeaderSize, buffer.Len())
header, err := ReadDataHeader(&buffer)
require.NoError(t, err)
actual, err := ReadSubmitResponseBody(&buffer, header, expected.Header.Direction)
require.NoError(t, err)
require.Equal(t, DataHeader{Command: RetSubmit, SeqNum: expected.Header.SeqNum}, actual.Header)
require.Equal(t, expected.ActualLength, actual.ActualLength)
require.Equal(t, expected.NumberOfPackets, actual.NumberOfPackets)
require.Empty(t, actual.Buffer)
}
func TestUSBIPSubmitResponseReadsPayloadFromOriginalDirection(t *testing.T) {
t.Parallel()
expected := SubmitResponse{
Header: DataHeader{
Command: RetSubmit,
SeqNum: 42,
Direction: USBIPDirIn,
},
Status: 0,
ActualLength: 3,
NumberOfPackets: nonIsoPacketCount,
Buffer: []byte{4, 5, 6},
}
var buffer bytes.Buffer
require.NoError(t, WriteSubmitResponse(&buffer, expected))
header, err := ReadDataHeader(&buffer)
require.NoError(t, err)
require.Equal(t, DataHeader{Command: RetSubmit, SeqNum: expected.Header.SeqNum}, header)
actual, err := ReadSubmitResponseBody(&buffer, header, USBIPDirIn)
require.NoError(t, err)
expected.Header = header
require.Equal(t, expected, actual)
}
func TestUSBIPSubmitCommandAcceptsNonISOPacketSentinel(t *testing.T) {
t.Parallel()
var raw [28]byte
binary.BigEndian.PutUint32(raw[12:16], 0xffffffff)
command, err := ReadSubmitCommandBody(bytes.NewReader(raw[:]), DataHeader{Command: CmdSubmit, Direction: USBIPDirIn})
require.NoError(t, err)
require.Equal(t, int32(nonIsoPacketCount), command.NumberOfPackets)
require.Empty(t, command.IsoPackets)
}
func TestUSBIPUnlinkRoundTrip(t *testing.T) {
t.Parallel()
command := UnlinkCommand{
Header: DataHeader{
Command: CmdUnlink,
SeqNum: 12,
DevID: 0x00030009,
Direction: USBIPDirOut,
Endpoint: 0,
},
SeqNum: 11,
}
var buffer bytes.Buffer
require.NoError(t, WriteUnlinkCommand(&buffer, command))
header, err := ReadDataHeader(&buffer)
require.NoError(t, err)
actualCommand, err := ReadUnlinkCommandBody(&buffer, header)
require.NoError(t, err)
require.Equal(t, command, actualCommand)
response := UnlinkResponse{
Header: DataHeader{
Command: RetUnlink,
SeqNum: command.Header.SeqNum,
DevID: command.Header.DevID,
Direction: command.Header.Direction,
Endpoint: command.Header.Endpoint,
},
Status: 0,
}
buffer.Reset()
require.NoError(t, WriteUnlinkResponse(&buffer, response))
header, err = ReadDataHeader(&buffer)
require.NoError(t, err)
actualResponse, err := ReadUnlinkResponseBody(&buffer, header)
require.NoError(t, err)
response.Header = DataHeader{Command: RetUnlink, SeqNum: command.Header.SeqNum}
require.Equal(t, response, actualResponse)
}
func TestUSBIPUnlinkDelayedFakeTransfer(t *testing.T) {
t.Parallel()
client, server := net.Pipe()
defer client.Close()
defer server.Close()
deadline := time.Now().Add(5 * time.Second)
require.NoError(t, client.SetDeadline(deadline))
require.NoError(t, server.SetDeadline(deadline))
serverDone := make(chan error, 1)
go func() {
header, err := ReadDataHeader(server)
if err != nil {
serverDone <- err
return
}
command, err := ReadSubmitCommandBody(server, header)
if err != nil {
serverDone <- err
return
}
header, err = ReadDataHeader(server)
if err != nil {
serverDone <- err
return
}
unlink, err := ReadUnlinkCommandBody(server, header)
if err != nil {
serverDone <- err
return
}
if unlink.SeqNum != command.Header.SeqNum {
serverDone <- errors.New("unexpected unlink seq")
return
}
serverDone <- WriteUnlinkResponse(server, UnlinkResponse{
Header: DataHeader{
Command: RetUnlink,
SeqNum: unlink.Header.SeqNum,
DevID: unlink.Header.DevID,
Direction: unlink.Header.Direction,
Endpoint: unlink.Header.Endpoint,
},
Status: usbipStatusECONNRESET,
})
}()
require.NoError(t, WriteSubmitCommand(client, SubmitCommand{
Header: DataHeader{
Command: CmdSubmit,
SeqNum: 31,
DevID: 0x00030009,
Direction: USBIPDirIn,
Endpoint: 1,
},
TransferBufferLength: 64,
}))
require.NoError(t, WriteUnlinkCommand(client, UnlinkCommand{
Header: DataHeader{
Command: CmdUnlink,
SeqNum: 32,
DevID: 0x00030009,
Direction: USBIPDirIn,
Endpoint: 1,
},
SeqNum: 31,
}))
header, err := ReadDataHeader(client)
require.NoError(t, err)
require.Equal(t, RetUnlink, header.Command)
response, err := ReadUnlinkResponseBody(client, header)
require.NoError(t, err)
require.Equal(t, int32(usbipStatusECONNRESET), response.Status)
require.NoError(t, <-serverDone)
}
func TestUSBIPRejectsInvalidDataPlaneLengths(t *testing.T) {
t.Parallel()
var buffer bytes.Buffer
err := WriteSubmitCommand(&buffer, SubmitCommand{
Header: DataHeader{Command: CmdSubmit, Direction: USBIPDirOut},
TransferBufferLength: -1,
})
require.ErrorContains(t, err, "negative")
err = WriteSubmitResponse(&buffer, SubmitResponse{
Header: DataHeader{Command: RetSubmit, Direction: USBIPDirIn},
ActualLength: maxUSBIPTransferBufferLength + 1,
})
require.ErrorContains(t, err, "too large")
err = WriteSubmitCommand(&buffer, SubmitCommand{
Header: DataHeader{Command: CmdSubmit, Direction: USBIPDirOut},
TransferBufferLength: 4,
Buffer: []byte("abc"),
})
require.ErrorContains(t, err, "payload length mismatch")
err = WriteSubmitResponse(&buffer, SubmitResponse{
Header: DataHeader{Command: RetSubmit, Direction: USBIPDirIn},
ActualLength: 4,
Buffer: []byte("abc"),
})
require.ErrorContains(t, err, "payload length mismatch")
err = WriteSubmitCommand(&buffer, SubmitCommand{
Header: DataHeader{Command: CmdSubmit, Direction: USBIPDirIn},
TransferBufferLength: 4,
Buffer: []byte("abcd"),
})
require.ErrorContains(t, err, "unexpected payload buffer")
err = WriteSubmitResponse(&buffer, SubmitResponse{
Header: DataHeader{Command: RetSubmit, Direction: USBIPDirOut},
Buffer: []byte("abcd"),
})
require.ErrorContains(t, err, "unexpected payload buffer")
var raw [28]byte
binary.BigEndian.PutUint32(raw[4:8], 0xffffffff)
_, err = ReadSubmitCommandBody(bytes.NewReader(raw[:]), DataHeader{Command: CmdSubmit, Direction: USBIPDirOut})
require.ErrorContains(t, err, "negative")
raw = [28]byte{}
binary.BigEndian.PutUint32(raw[12:16], uint32(maxUSBIPIsoPackets+1))
_, err = ReadSubmitCommandBody(bytes.NewReader(raw[:]), DataHeader{Command: CmdSubmit, Direction: USBIPDirIn})
require.ErrorContains(t, err, "too large")
raw = [28]byte{}
binary.BigEndian.PutUint32(raw[12:16], 0xfffffffe)
_, err = ReadSubmitCommandBody(bytes.NewReader(raw[:]), DataHeader{Command: CmdSubmit, Direction: USBIPDirIn})
require.ErrorContains(t, err, "negative")
}