Documentation
¶
Overview ¶
Package serviceinfo handles FDO Service Info and Service Info Modules.
Index ¶
- Constants
- Variables
- func ArraySizeCBOR(arr []*KV) int64
- func NewChunkInPipe(buffers int) (*UnchunkReader, *ChunkWriter)
- func NewChunkOutPipe(buffers int) (*ChunkReader, *UnchunkWriter)
- type ChunkReader
- type ChunkWriter
- type DeviceModule
- type Devmod
- type DevmodModulesChunk
- type KV
- type MTUKey
- type OwnerModule
- type Producer
- type UnchunkReader
- type UnchunkWriter
- type UnknownModule
Constants ¶
const DefaultMTU = 1300
DefaultMTU for service info when Max(Owner|Device)ServiceInfoSz is null.
Variables ¶
var ErrSizeTooSmall = errors.New("not enough size for chunk")
ErrSizeTooSmall indicates that a chunk could not be read due to insufficient max size.
Functions ¶
func ArraySizeCBOR ¶
ArraySizeCBOR returns the size of the service info slice once marshaled to CBOR.
func NewChunkInPipe ¶
func NewChunkInPipe(buffers int) (*UnchunkReader, *ChunkWriter)
NewChunkInPipe creates a ChunkWriter and UnchunkReader pair. All chunks sent to the writer will be unchunked and emitted from the reader.
func NewChunkOutPipe ¶
func NewChunkOutPipe(buffers int) (*ChunkReader, *UnchunkWriter)
NewChunkOutPipe creates a ChunkReader and UnchunkWriter pair. All service info sent to the writer will be chunked using the given MTU and emitted from the reader.
Types ¶
type ChunkReader ¶
type ChunkReader struct {
// contains filtered or unexported fields
}
ChunkReader reads ServiceInfo chunked at some MTU.
func (*ChunkReader) Close ¶
func (r *ChunkReader) Close() error
Close the reader if no more reads will be performed so that the Writer errors rather than deadlocks.
type ChunkWriter ¶
type ChunkWriter struct {
// contains filtered or unexported fields
}
ChunkWriter expects ServiceInfo key-values to be written in the correct order and appropriate MTU. When all chunks have been written, it must be closed exactly once with either Close or CloseWithError.
func (*ChunkWriter) Close ¶
func (w *ChunkWriter) Close() (err error)
Close must be called exactly once when all ServiceInfo have been written.
func (*ChunkWriter) CloseWithError ¶
func (w *ChunkWriter) CloseWithError(err error) error
CloseWithError causes reads from the associated UnchunkReader to error with the given error.
func (*ChunkWriter) WriteChunk ¶
func (w *ChunkWriter) WriteChunk(kv *KV) error
WriteChunk is called with chunked ServiceInfos.
type DeviceModule ¶
type DeviceModule interface { // Transition sets the state of the module to active or inactive. Receive // and Respond will not be called unless Transition has been called at // least once and with the last input of true. Transition, as such, is only // a callback to allow setting up/tearing down state. Transition(active bool) error // Receive handles received service info. When any message is received // multiple times, the values will automatically be concatenated. For // cumulative data, this means that large values like files can be read // from a single io.Reader. For repetitive discrete objects, a CBOR decoder // should be applied to the io.Reader and a stream of objects can be read. // // The respond callback allows the module to send any number of service // info KVs. A writer is provided, as automatic chunking of messages larger // than the MTU will be performed. However, there is no automatic batching // of writes into a single KV, so each Write will result in at least one // service info KV being sent (possibly in a larger group of KVs per FDO // message). // // The yield callback will cause the next service info to be sent in a new // message, regardless of how much space is left in the current device // service info array. // // For manual chunking using yield, it may be desirable to know the MTU. // The full negotiated MTU (not the current space left from the MTU) can be // acquired from `ctx.Value(serviceinfo.MTUKey{}).(uint16)`. Receive(ctx context.Context, messageName string, messageBody io.Reader, respond func(message string) io.Writer, yield func()) error // Yield indicates that all service info key value pairs have been received // from the owner module, possibly across multiple messages with // IsMoreServiceInfo set. // // The respond and yield callbacks behave the same as for Receive. Yield(ctx context.Context, respond func(message string) io.Writer, yield func()) error }
A DeviceModule handles a single service info key's moduleName (key format: "moduleName:messageName"). "active" messages are automatically handled and do not result in a call to Receive method.
Chunking is applied automatically, so large responses may be written without custom chunking code. Responses larger than the MTU will cause the marshaled payload to be split across multiple bstrs. The data within the bstrs must be concatenated at the receiver in order to reconstruct the payload and guarantee a valid CBOR object. When chunking occurs, the device sends IsMoreServiceInfo = true.
Unchunking is automatic and applies the reverse process to the above. Note that this only occurs when IsMoreServiceInfo = true and the same message is sent multiple times in a row.
MessageBody must be fully read before writing a response unless the module implements UnsafeModule. Writing before fully consuming the reader will case the writer to fail.
Responses may be queued and not sent immediately, as the receive queue in TO2 may still be filling, potentially across multiple rounds of 68-69 messages. Sends only occur once the peer has stopped indicating IsMoreServiceInfo.
Any error returned will cause an ErrorMessage to be sent and TO2 will fail. If a warning should be logged, this must be done within the handler.
type Devmod ¶
type Devmod struct { Os string `devmod:"os,required"` Arch string `devmod:"arch,required"` Version string `devmod:"version,required"` Device string `devmod:"device,required"` Serial []byte `devmod:"sn"` PathSep string `devmod:"pathsep"` FileSep string `devmod:"sep,required"` Newline string `devmod:"nl"` Temp string `devmod:"tmp"` Dir string `devmod:"dir"` ProgEnv string `devmod:"progenv"` Bin string `devmod:"bin,required"` MudURL string `devmod:"mudurl"` }
Devmod implements the one required FDO ServiceInfo Module
┌─────────────────┬───────────┬───────────────┬─────────────────────────────────────────────────────────────┐ │devmod:active │ Required │ bool │ Indicates the module is active. Devmod is required on │ │ │ │ │ all devices │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:os │ Required │ tstr │ OS name (e.g., Linux) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:arch │ Required │ tstr │ Architecture name / instruction set (e.g., X86_64) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:version │ Required │ tstr │ Version of OS (e.g., “Ubuntu* 16.0.4LTS”) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:device │ Required │ tstr │ Model specifier for this FIDO Device Onboard Device, │ │ │ │ │ manufacturer specific │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:sn │ Optional │ tstr/bstr │ Serial number for this FIDO Device Onboard Device, │ │ │ │ │ manufacturer specific │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:pathsep │ Optional │ tstr │ Filename path separator, between the directory and │ │ │ │ │ sub-directory (e.g., ‘/’ or ‘\’) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:sep │ Required │ tstr │ Filename separator, that works to make lists of file │ │ │ │ │ names (e.g., ‘:’ or ‘;’) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:nl │ Optional │ tstr │ Newline sequence (e.g., a tstr of length 1 containing │ │ │ │ │ U+000A; a tstr of length 2 containing U+000D followed │ │ │ │ │ by U+000A) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:tmp │ Optional │ tstr │ Location of temporary directory, including terminating │ │ │ │ │ file separator (e.g., “/tmp”) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:dir │ Optional │ tstr │ Location of suggested installation directory, including │ │ │ │ │ terminating file separator (e.g., “.” or “/home/fdo” or │ │ │ │ │ “c:\Program Files\fdo”) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:progenv │ Optional │ tstr │ Programming environment. See Table 3-22 (e.g., │ │ │ │ │ “bin:java:py3:py2”) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:bin │ Required │ tstr │ Either the same value as “arch”, or a list of machine │ │ │ │ │ formats that can be interpreted by this device, in │ │ │ │ │ preference order, separated by the “sep” value (e.g., │ │ │ │ │ “x86:X86_64”) │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:mudurl │ Optional │ tstr │ URL for the Manufacturer Usage Description file that │ │ │ │ │ relates to this device │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:nummodules│ Required │ uint │ Number of modules supported by this FIDO Device Onboard │ │ │ │ │ Device │ ├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤ │devmod:modules │ Required │ [uint, uint, │ Enumerates the modules supported by this FIDO Device │ │ │ │ tstr1, tstr2, │ Onboard Device. The first element is an integer from │ │ │ │ ...] │ zero to devmod:nummodules. The second element is the │ │ │ │ │ number of module names to return The subsequent elements │ │ │ │ │ are module names. During the initial Device ServiceInfo, │ │ │ │ │ the device sends the complete list of modules to the Owner. │ │ │ │ │ If the list is long, it might require more than one │ │ │ │ │ ServiceInfo message. │ └─────────────────┴───────────┴───────────────┴─────────────────────────────────────────────────────────────┘
func (*Devmod) Write ¶
func (d *Devmod) Write(ctx context.Context, deviceModules map[string]DeviceModule, mtu uint16, w *UnchunkWriter)
Write the devmod messages.
type DevmodModulesChunk ¶
DevmodModulesChunk is the CBOR array value used in devmod:modules messages. Instead of representing it as an []any, it provides a more typed interface, knowing that the array will always contain [int, int, [string...]].
func (DevmodModulesChunk) MarshalCBOR ¶
func (c DevmodModulesChunk) MarshalCBOR() ([]byte, error)
MarshalCBOR implements cbor.Marshaler.
func (*DevmodModulesChunk) UnmarshalCBOR ¶
func (c *DevmodModulesChunk) UnmarshalCBOR(data []byte) error
UnmarshalCBOR implements cbor.Unmarshaler.
type KV ¶
KV is a ServiceInfoKV structure.
type MTUKey ¶
type MTUKey struct{}
MTUKey is the context key for the uint16 MTU value. See the description of DeviceModule.Receive.
type OwnerModule ¶
type OwnerModule interface { // HandleInfo is called once for each service info KV received from the // device. HandleInfo(ctx context.Context, messageName string, messageBody io.Reader) error // ProduceInfo is called once for each TO2.DeviceServiceInfo, after // HandleInfo is called for each service info KV, unless the device // indicated IsMoreServiceInfo. // // If `blockPeer` is true, the owner service will indicate // IsMoreServiceInfo to keep the device from sending service info in the // next exchange. If `moduleDone` is true, then IsMoreServiceInfo will not // be set true, regardless of the value of `more`, and this module will no // longer be used in the TO2 protocol. ProduceInfo(ctx context.Context, producer *Producer) (blockPeer, moduleDone bool, _ error) }
OwnerModule implements a service info module.
type Producer ¶
type Producer struct {
// contains filtered or unexported fields
}
Producer allows an owner service info module to produce service info either with auto-chunking (not yet implemented) or manually.
func NewProducer ¶
NewProducer creates a new producer instance for the given MTU.
func (*Producer) Available ¶
Available returns the remaining space available for a message body in bytes. If the next service info will not fit in the remaining bytes, then the module should return and on the next ProduceInfo the full MTU will be available.
func (*Producer) ServiceInfo ¶
ServiceInfo returns all ServiceInfo, guaranteed to fit within the MTU.
type UnchunkReader ¶
type UnchunkReader struct {
// contains filtered or unexported fields
}
UnchunkReader gets a new io.Reader for each logical ServiceInfo.
func (*UnchunkReader) NextServiceInfo ¶
func (r *UnchunkReader) NextServiceInfo() (key string, val io.ReadCloser, ok bool)
NextServiceInfo gets a new io.Reader for a logical ServiceInfo. The reader contains the unchunked value of the ServiceInfo so that the consumer does not need to be aware of the MTU.
type UnchunkWriter ¶
type UnchunkWriter struct {
// contains filtered or unexported fields
}
UnchunkWriter is used for writing unchunked logical ServiceInfos. Each ServiceInfo is written by first calling NextServiceInfo to give it a module and message name, then the full body is written via zero or more calls to Write.
func (*UnchunkWriter) Close ¶
func (w *UnchunkWriter) Close() error
Close is called when all ServiceInfos have been written and no further calls to Next or Write will be made.
func (*UnchunkWriter) CloseWithError ¶
func (w *UnchunkWriter) CloseWithError(err error) error
CloseWithError causes reads from the associated ChunkReader to error with the given error.
func (*UnchunkWriter) ForceNewMessage ¶
func (w *UnchunkWriter) ForceNewMessage() error
ForceNewMessage causes the next (*ChunkReader).ReadChunk to return ErrSizeTooSmall. This in turn forces the next KV to be put into a new ServiceInfo message.
This method facilitates implementing custom chunking conventions, provided that the MTU used for automatic chunking at the client level is known to the implementer.
func (*UnchunkWriter) NextServiceInfo ¶
func (w *UnchunkWriter) NextServiceInfo(moduleName, messageName string) error
NextServiceInfo must be called once before each logical ServiceInfo.
type UnknownModule ¶
type UnknownModule struct{}
UnknownModule handles receiving and responding to service info for an inactive or missing module.
Section 3.8.3.1 says to ignore all messages for unknown modules, except message=active, which should respond CBOR false. The exceception to the exception is that devmod should always return active=true.
func (UnknownModule) Receive ¶
func (m UnknownModule) Receive(_ context.Context, _ string, messageBody io.Reader, _ func(string) io.Writer, _ func()) error
Receive implements DeviceModule.
func (UnknownModule) Transition ¶
func (m UnknownModule) Transition(bool) error
Transition implements DeviceModule.