Documentation
¶
Index ¶
- Constants
- Variables
- func DecodeCBOR(r io.Reader, v interface{}) error
- func EncodeCBOR(v interface{}) ([]byte, error)
- func FormatPSK(psk string) (string, error)
- func ParsePSK(s string) (string, error)
- func ReadTypedMessage(r io.Reader, typeKey TypeKey, v interface{}) error
- func WriteTypedMessage(w io.Writer, typeKey TypeKey, v interface{}) error
- type ALPNListener
- type ALPNListenerConfig
- type Agent
- func (a *Agent) AgentInfoMessage() MsgAgentInfo
- func (a *Agent) Authenticated() bool
- func (a *Agent) AuthenticationInfo() *AgentAuthenticationInfo
- func (a *Agent) CertificateFingerPrint() (string, error)
- func (a *Agent) DiscoveredInstanceName() (InstanceName, bool)
- func (a *Agent) DisplayName() (string, DisplayNameStatus)
- func (a *Agent) HasAuthenticationInfo() bool
- func (a *Agent) HasCapability(cap AgentCapability) bool
- func (a *Agent) HasInfo() bool
- func (a *Agent) Info() *AgentInfo
- func (a *Agent) Metadata() (AgentMetadata, bool)
- func (a *Agent) MetadataVersion() uint64
- func (a *Agent) NewRemoteAgent(nc NetworkConnection) (*Agent, error)
- func (a *Agent) NextRequestID(peer PeerID) uint64
- func (a *Agent) RequestIDs() *RequestIDAllocator
- func (a *Agent) ResetState()
- func (a *Agent) StateToken() string
- type AgentAuthenticationInfo
- type AgentCapability
- type AgentConfig
- type AgentInfo
- type AgentMetadata
- type AgentRole
- type AgentTransport
- type ApplicationConnection
- type ApplicationStream
- type AuthenticationRole
- type Connection
- func (c *Connection) AcceptDataChannel(ctx context.Context) (*DataChannel, error)
- func (c *Connection) AcceptStream(ctx context.Context) (ApplicationStream, error)
- func (c *Connection) AcceptWebTransport(ctx context.Context) (*IncomingWebTransport, error)
- func (c *Connection) Authenticated() bool
- func (c *Connection) Close() error
- func (c *Connection) Done() <-chan struct{}
- func (c *Connection) Err() error
- func (c *Connection) LocalAgent() *Agent
- func (c *Connection) OpenDataChannel(ctx context.Context, init DataChannelInit) (*DataChannel, error)
- func (c *Connection) OpenStream(ctx context.Context) (ApplicationStream, error)
- func (c *Connection) OpenUniStream(ctx context.Context) (ApplicationStream, error)
- func (c *Connection) OpenWebTransport(ctx context.Context) (*WebTransportSession, error)
- func (c *Connection) RemoteAgent() *Agent
- func (c *Connection) SupportsDatagrams() bool
- type DTLSTransport
- type DataChannel
- func (c *DataChannel) Close() error
- func (c *DataChannel) Read(p []byte) (int, error)
- func (c *DataChannel) ReadDataChannel(p []byte) (int, DataEncoding, error)
- func (c *DataChannel) ReceiveMessage() ([]byte, error)
- func (c *DataChannel) ReceiveMessageWithEncoding() ([]byte, DataEncoding, error)
- func (c *DataChannel) SendMessage(payload []byte) error
- func (c *DataChannel) SendMessageWithEncoding(payload []byte, enc DataEncoding) error
- func (c *DataChannel) Write(p []byte) (n int, err error)
- func (c *DataChannel) WriteDataChannel(p []byte, enc DataEncoding) (n int, err error)
- type DataChannelInit
- type DataChannelParameters
- type DataEncoding
- type DiscoveredAgent
- func (a *DiscoveredAgent) AuthToken() (string, error)
- func (ra DiscoveredAgent) Dial(ctx context.Context, transportType AgentTransport, la *Agent) (*UnauthenticatedConnection, error)
- func (ra DiscoveredAgent) DialSession(ctx context.Context, transportType AgentTransport, la *Agent, origin string) (*UnauthenticatedConnection, error)
- func (a *DiscoveredAgent) DisplayName() (string, DisplayNameStatus)
- func (a *DiscoveredAgent) InstanceName() InstanceName
- func (a *DiscoveredAgent) MetadataChangedSince(prev AgentMetadata) bool
- func (a *DiscoveredAgent) MetadataVersion() (uint64, bool)
- func (a *DiscoveredAgent) Nickname() string
- type Discoverer
- type DisplayNameStatus
- type IncomingWebTransport
- type InstanceName
- type InteropQuirks
- type KeepAliveConfig
- type Listener
- func (l *Listener) Accept(ctx context.Context) (*UnauthenticatedConnection, error)
- func (l *Listener) Addr() net.Addr
- func (l *Listener) AnnounceMetadataChange() error
- func (l *Listener) Close() error
- func (l *Listener) ListenApplication(alpn string, config *ALPNListenerConfig) *ALPNListener
- func (l *Listener) Start() error
- func (l *Listener) StartWithoutAdvertising(addr string) error
- func (l *Listener) TXT() TXTRecordSet
- type MsgAgentInfo
- type MsgAgentInfoEvent
- type MsgAgentInfoRequest
- type MsgAgentInfoResponse
- type MsgAgentStatusRequest
- type MsgAgentStatusResponse
- type NetworkConnection
- type NetworkListener
- type NetworkTransport
- type PSKConfig
- type PeerID
- type QuicTransport
- type RequestID
- type RequestIDAllocator
- type SessionCue
- type TXTRecordSet
- type TypeKey
- type UnauthenticatedConnection
- func (c *UnauthenticatedConnection) AcceptAuthenticate(ctx context.Context) (role AuthenticationRole, err error)
- func (c *UnauthenticatedConnection) AuthenticatePSK(ctx context.Context, psk []byte) (*Connection, error)
- func (c *UnauthenticatedConnection) AwaitSession(ctx context.Context) (SessionCue, error)
- func (c *UnauthenticatedConnection) Close() error
- func (c *UnauthenticatedConnection) CompleteSession(ctx context.Context) (*Connection, error)
- func (c *UnauthenticatedConnection) GeneratePSK() ([]byte, error)
- func (c *UnauthenticatedConnection) GetAuthenticationRole() AuthenticationRole
- func (c *UnauthenticatedConnection) LocalAgent() *Agent
- func (c *UnauthenticatedConnection) RefuseSession() error
- func (c *UnauthenticatedConnection) RemoteAgent() *Agent
- func (c *UnauthenticatedConnection) RequestSession(ctx context.Context) error
- func (c *UnauthenticatedConnection) RequestingOrigin() (origin string, ok bool)
- func (c *UnauthenticatedConnection) RequireAuthentication() error
- func (c *UnauthenticatedConnection) SkipAuthentication() (*Connection, error)
- func (c *UnauthenticatedConnection) StartAuthentication() error
- func (c *UnauthenticatedConnection) TrustPeer() error
- type UniStreamOpener
- type WebTransportCloseInfo
- type WebTransportSession
- func (s *WebTransportSession) AcceptStream(ctx context.Context) (*WebTransportStream, error)
- func (s *WebTransportSession) AcceptUniStream(ctx context.Context) (*WebTransportStream, error)
- func (s *WebTransportSession) Close(info WebTransportCloseInfo) error
- func (s *WebTransportSession) Closed(ctx context.Context) (WebTransportCloseInfo, error)
- func (s *WebTransportSession) Done() <-chan struct{}
- func (s *WebTransportSession) Drain() error
- func (s *WebTransportSession) Draining() <-chan struct{}
- func (s *WebTransportSession) ExchangeID() uint64
- func (s *WebTransportSession) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error)
- func (s *WebTransportSession) OpenStreamSync(ctx context.Context) (*WebTransportStream, error)
- func (s *WebTransportSession) OpenUniStreamSync(ctx context.Context) (*WebTransportStream, error)
- func (s *WebTransportSession) ReceiveDatagram(ctx context.Context) ([]byte, error)
- func (s *WebTransportSession) SendDatagram(p []byte) error
- func (s *WebTransportSession) SupportsDatagrams() bool
- type WebTransportStream
- func (st *WebTransportStream) CancelRead(code uint32)
- func (st *WebTransportStream) CancelWrite(code uint32)
- func (st *WebTransportStream) Close() error
- func (st *WebTransportStream) Read(p []byte) (int, error)
- func (st *WebTransportStream) StreamID() int64
- func (st *WebTransportStream) Unidirectional() bool
- func (st *WebTransportStream) Write(p []byte) (int, error)
- type WebTransportStreamError
Examples ¶
Constants ¶
const ( // DefaultIdleTimeout is the recommended QUIC idle_timeout transport // parameter. DefaultIdleTimeout = 25 * time.Second // DefaultKeepAliveInterval is the default time without sending any message // after which an agent-status-request is sent. It has to be less than the // idle timeout; half of it leaves room for loss and round trips. DefaultKeepAliveInterval = DefaultIdleTimeout / 2 )
const ( AgentCapabilityDataChannels msgAgentCapability = 1100 AgentCapabilityQuickTransport msgAgentCapability = 1200 )
const ( DataChannelEncodingIdEncodingIdBlob msgDataChannelEncodingId = 0 DataChannelEncodingIdEncodingIdString msgDataChannelEncodingId = 1 DataChannelEncodingIdEncodingIdArrayBuffer msgDataChannelEncodingId = 2 )
const ( DataEncodingBinary = iota DataEncodingString DataEncodingArrayBuffer )
const ( // MaxExchangeID is the largest exchange-id of a WebTransport session: // `exchange-id = uint .lt 4611686018427387904` (below 2^62). MaxExchangeID = 1<<62 - 1 // MaxWebTransportCloseCode is the largest close-code of // data-transport-close: `uint .size 4`. MaxWebTransportCloseCode = 1<<32 - 1 // MaxWebTransportReasonLen is the longest reason of data-transport-close // in bytes: `text .size (0..1024)`. MaxWebTransportReasonLen = 1024 )
const ( LP2PSessionResultSuccess = msgLp2pSessionResult(ResultSuccess) LP2PSessionResultPermanentError = msgLp2pSessionResult(ResultPermanentError) LP2PSessionResultAuthenticationRequired msgLp2pSessionResult = 1110 )
const ( AgentCapabilityReceiveAudio msgAgentCapability = 1 AgentCapabilityReceiveVideo msgAgentCapability = 2 AgentCapabilityReceivePresentation msgAgentCapability = 3 AgentCapabilityControlPresentation msgAgentCapability = 4 AgentCapabilityReceiveRemotePlayback msgAgentCapability = 5 AgentCapabilityControlRemotePlayback msgAgentCapability = 6 AgentCapabilityReceiveStreaming msgAgentCapability = 7 AgentCapabilitySendStreaming msgAgentCapability = 8 )
const ( PskInputMethodNumeric msgPskInputMethod = 0 PskInputMethodQrCode msgPskInputMethod = 1 )
const ( AuthSpake2PskStatusPskNeedsPresentation msgAuthSpake2PskStatus = 0 AuthSpake2PskStatusPskShown msgAuthSpake2PskStatus = 1 AuthSpake2PskStatusPskInput msgAuthSpake2PskStatus = 2 )
const ( AuthStatusResultAuthenticated msgAuthStatusResult = 0 AuthStatusResultUnknownError msgAuthStatusResult = 1 AuthStatusResultTimeout msgAuthStatusResult = 2 AuthStatusResultSecretUnknown msgAuthStatusResult = 3 AuthStatusResultValidationTookTooLong msgAuthStatusResult = 4 AuthStatusResultProofInvalid msgAuthStatusResult = 5 )
const ( UrlAvailabilityAvailable msgUrlAvailability = 0 UrlAvailabilityInvalid msgUrlAvailability = 10 )
const ( PresentationTerminationSourceController msgPresentationTerminationSource = 1 PresentationTerminationSourceReceiver msgPresentationTerminationSource = 2 PresentationTerminationSourceUnknown msgPresentationTerminationSource = 255 )
const ( PresentationTerminationReasonApplicationRequest msgPresentationTerminationReason = 1 PresentationTerminationReasonUserRequest msgPresentationTerminationReason = 2 PresentationTerminationReasonReceiverReplacedPresentation msgPresentationTerminationReason = 20 PresentationTerminationReasonReceiverIdleTooLong msgPresentationTerminationReason = 30 PresentationTerminationReasonReceiverPoweringDown msgPresentationTerminationReason = 100 PresentationTerminationReasonReceiverError msgPresentationTerminationReason = 101 PresentationTerminationReasonUnknown msgPresentationTerminationReason = 255 )
const ( PresentationConnectionCloseEventReasonCloseMethodCalled msgPresentationConnectionCloseEventReason = 1 PresentationConnectionCloseEventReasonConnectionObjectDiscarded msgPresentationConnectionCloseEventReason = 10 PresentationConnectionCloseEventReasonUnrecoverableErrorWhileSendingOrReceivingMessage msgPresentationConnectionCloseEventReason = 100 )
const ( ResultSuccess msgResult = 1 ResultInvalidUrl msgResult = 10 ResultInvalidPresentationId msgResult = 11 ResultTimeout msgResult = 100 ResultTransientError msgResult = 101 ResultPermanentError msgResult = 102 ResultTerminating msgResult = 103 ResultUnknownError msgResult = 199 )
const ( RemotePlaybackTerminationRequestReasonUserTerminatedViaController msgRemotePlaybackTerminationRequestReason = 11 RemotePlaybackTerminationRequestReasonUnknown msgRemotePlaybackTerminationRequestReason = 255 )
const ( RemotePlaybackTerminationEventReasonReceiverCalledTerminate msgRemotePlaybackTerminationEventReason = 1 RemotePlaybackTerminationEventReasonUserTerminatedViaReceiver msgRemotePlaybackTerminationEventReason = 2 RemotePlaybackTerminationEventReasonReceiverIdleTooLong msgRemotePlaybackTerminationEventReason = 30 RemotePlaybackTerminationEventReasonReceiverPoweringDown msgRemotePlaybackTerminationEventReason = 100 RemotePlaybackTerminationEventReasonReceiverCrashed msgRemotePlaybackTerminationEventReason = 101 RemotePlaybackTerminationEventReasonUnknown msgRemotePlaybackTerminationEventReason = 255 )
const ( RemotePlaybackControlsPreloadNone msgRemotePlaybackControlsPreload = 0 RemotePlaybackControlsPreloadMetadata msgRemotePlaybackControlsPreload = 1 RemotePlaybackControlsPreloadAuto msgRemotePlaybackControlsPreload = 2 )
const ( RemotePlaybackStateLoadingEmpty msgRemotePlaybackStateLoading = 0 RemotePlaybackStateLoadingIdle msgRemotePlaybackStateLoading = 1 RemotePlaybackStateLoadingLoading msgRemotePlaybackStateLoading = 2 RemotePlaybackStateLoadingNoSource msgRemotePlaybackStateLoading = 3 )
const ( RemotePlaybackStateLoadedNothing msgRemotePlaybackStateLoaded = 0 RemotePlaybackStateLoadedMetadata msgRemotePlaybackStateLoaded = 1 RemotePlaybackStateLoadedCurrent msgRemotePlaybackStateLoaded = 2 RemotePlaybackStateLoadedFuture msgRemotePlaybackStateLoaded = 3 RemotePlaybackStateLoadedEnough msgRemotePlaybackStateLoaded = 4 )
const ( AddedTextTrackKindSubtitles msgAddedTextTrackKind = 1 AddedTextTrackKindCaptions msgAddedTextTrackKind = 2 AddedTextTrackKindDescriptions msgAddedTextTrackKind = 3 AddedTextTrackKindChapters msgAddedTextTrackKind = 4 AddedTextTrackKindMetadata msgAddedTextTrackKind = 5 )
const ( TextTrackModeDisabled msgTextTrackMode = 1 TextTrackModeShowing msgTextTrackMode = 2 TextTrackModeHidden msgTextTrackMode = 3 )
const ( MediaErrorMsgCodeUserAborted msgMediaErrorMsgCode = 1 MediaErrorMsgCodeNetworkError msgMediaErrorMsgCode = 2 MediaErrorMsgCodeDecodeError msgMediaErrorMsgCode = 3 MediaErrorMsgCodeSourceNotSupported msgMediaErrorMsgCode = 4 MediaErrorMsgCodeUnknownError msgMediaErrorMsgCode = 5 )
const ( VideoRotationVideoRotation0 msgVideoRotation = 0 VideoRotationVideoRotation90 msgVideoRotation = 1 VideoRotationVideoRotation180 msgVideoRotation = 2 VideoRotationVideoRotation270 msgVideoRotation = 3 )
const ( StreamingBufferStatusEnoughData msgStreamingBufferStatus = 0 StreamingBufferStatusInsufficientData msgStreamingBufferStatus = 1 StreamingBufferStatusTooMuchData msgStreamingBufferStatus = 2 )
const ( CapabilityReceiveAudio = AgentCapabilityReceiveAudio CapabilityReceiveVideo = AgentCapabilityReceiveVideo CapabilityReceivePresentation = AgentCapabilityReceivePresentation CapabilityControlPresentation = AgentCapabilityControlPresentation CapabilityReceiveRemotePlayback = AgentCapabilityReceiveRemotePlayback CapabilityControlRemotePlayback = AgentCapabilityControlRemotePlayback CapabilityReceiveStreaming = AgentCapabilityReceiveStreaming CapabilitySendStreaming = AgentCapabilitySendStreaming )
Exported capability constants
const ( MdnsServiceType = "_openscreen._udp" MdnsDomain = "local" ALPN_OSP = "osp" )
const ( // WebTransportSessionGone is the RESET_STREAM and STOP_SENDING code for // a stream whose session is closed or unknown (2^32). Codes below it are // WebTransport stream error codes; unknown codes from 2^32 are treated // as session-gone. WebTransportSessionGone uint64 = 1 << 32 // MaxWebTransportStreamErrorCode is the largest WebTransport stream // error code. MaxWebTransportStreamErrorCode = 1<<32 - 1 )
const MTU = 8192
const MaxDataChannelID = 65534
MaxDataChannelID is the largest channel-id (LP2P "Data channel extension": "between 0 and 65,534").
const ( // QuicErrorCodeNoLongerNeeded is used when neither side needs the // connection anymore. QuicErrorCodeNoLongerNeeded quic.ApplicationErrorCode = 5139 )
QUIC application error codes used when closing a connection.
Variables ¶
var ( // ErrDataChannelIDOutOfRange is returned when an explicit channel ID is // above MaxDataChannelID, or no ID is free. ErrDataChannelIDOutOfRange = errors.New("data channel ID out of range") // ErrDataChannelIDInUse is returned when an explicit channel ID is used // by another open data channel of the connection. ErrDataChannelIDInUse = errors.New("data channel ID in use") // ErrDataChannelIDParity is returned when an explicit channel ID has // the parity the peer allocates from. ErrDataChannelIDParity = errors.New("data channel ID has the peer's parity") // ErrDataChannelRejected is returned when the peer answers the // data-channel-open-request with a result other than success. ErrDataChannelRejected = errors.New("data channel rejected by the peer") )
var ( // ErrSessionRefused is the cause of a connection closed because the // server refused the session with lp2p-session-response permanent-error, // or because RefuseSession was called. ErrSessionRefused = errors.New("session refused") // ErrSessionUnsupported is returned by RequestSession when the peer // lists neither data-channels nor quic-transport in its agent-info, or // the transport can't carry the session stream. ErrSessionUnsupported = errors.New("peer does not support LP2P sessions") // ErrSessionProtocol is the cause of a connection closed for a violation // of the session extension, e.g. a second lp2p-session-request. ErrSessionProtocol = errors.New("session extension protocol violation") // ErrSessionNotVerified is returned by RequestSession, CompleteSession // and RequestingOrigin when the peer's certificate wasn't verified first. ErrSessionNotVerified = errors.New("peer certificate not verified") )
var ( // ErrWebTransportUnsupported is returned by OpenWebTransport when the // peer doesn't list quic-transport in its agent-info: the extension's // messages must not be sent to it. ErrWebTransportUnsupported = errors.New("peer does not support the WebTransport extension") // ErrWebTransportRefused is returned by OpenWebTransport when the peer // answers with a result other than success. ErrWebTransportRefused = errors.New("WebTransport session refused by the peer") // ErrWebTransportClosed is returned when using a closed session, and by // Closed for a session that was closed abruptly without a close code. ErrWebTransportClosed = errors.New("WebTransport session closed") // ErrWebTransportSessionGone is returned by stream operations when the // peer reset or stopped the stream with session-gone. It carries no // stream error code. ErrWebTransportSessionGone = errors.New("WebTransport stream reset: session gone") // ErrWebTransportNoDatagrams is returned by SendDatagram when QUIC // datagrams weren't negotiated on the connection. ErrWebTransportNoDatagrams = errors.New("QUIC datagrams not supported on this connection") // ErrWebTransportDatagramTooLarge is returned by SendDatagram for a // datagram that doesn't fit in a QUIC DATAGRAM frame. ErrWebTransportDatagramTooLarge = errors.New("datagram too large") )
var DataChannelOpenTimeout = 10 * time.Second
DataChannelOpenTimeout bounds how long OpenDataChannel waits for the data-channel-open-response when the context has no earlier deadline.
var ErrAuthenticationFailed = errors.New("authentication failed")
ErrAuthenticationFailed is the cause of a connection closed because authentication failed, locally or as reported by the remote agent.
var ErrConnectionClosed = errors.New("connection closed")
var ErrDiscovererClosed = errors.New("discoverer closed")
var ErrFingerprintMismatch = errors.New("agent certificate doesn't match advertised fingerprint")
ErrFingerprintMismatch is returned when the certificate presented by an advertising agent doesn't match its advertised fp.
var ErrInvalidMessage = errors.New("invalid message")
ErrInvalidMessage is the cause of encode and decode errors for messages whose values are outside the limits their CDDL defines.
var ErrInvalidPSK = errors.New("invalid PSK")
ErrInvalidPSK is returned for a PSK that is not a base-10 numeric PSK.
var ErrListenerClosed = errors.New("listener closed")
var ErrNoAgentInfo = errors.New("peer did not complete metadata discovery")
ErrNoAgentInfo is returned by Dial and the listener when the peer doesn't complete metadata discovery in time: it didn't answer agent-info-request or didn't send auth-capabilities. Peers that don't implement network.bs "Metadata Discovery" and "Authentication" fail this way, see InteropQuirks.TolerateMissingAgentInfo.
var ErrQuirkDisabled = errors.New("interop quirk not enabled")
ErrQuirkDisabled is returned when calling an API that requires an interop quirk that is not enabled in the AgentConfig.
var ErrSpake2SuiteUnsupported = errors.New("SPAKE2 edwards25519 suite (network.bs) is not implemented; enable InteropQuirks.Spake2SuiteP256 to use the P-256 suite")
ErrSpake2SuiteUnsupported is returned when authenticating without InteropQuirks.Spake2SuiteP256: the spec's edwards25519 suite is not implemented.
var ErrStreamDirection = errors.New("unidirectional stream: operation not supported in this direction")
ErrStreamDirection is returned when reading from a stream that can only be written or writing to a stream that can only be read: one half of a unidirectional stream.
var ErrTransportClosed = errors.New("transport closed")
var ErrTransportHandedOff = errors.New("transport handed off")
var WebTransportOpenTimeout = 10 * time.Second
WebTransportOpenTimeout bounds how long OpenWebTransport waits for the data-transport-start-response when the context has no earlier deadline.
Functions ¶
func DecodeCBOR ¶
DecodeCBOR decodes a CBOR-encoded value from the reader. Exactly one CBOR data item is consumed, so it is safe to call repeatedly on a stream carrying multiple messages.
func EncodeCBOR ¶
EncodeCBOR encodes v as CBOR with deterministically ordered map keys.
func FormatPSK ¶
FormatPSK encodes a numeric PSK for display to the user (network.bs Appendix B "Base-10 Numeric"): groups of three digits separated by dashes below 9 digits, groups of four otherwise, zero-padded on the left to fill the first group. The input may be in any form ParsePSK accepts.
The spec's padding rule ("Zero-pad N on the left with 3 - len(N) mod 3 digits") would add a whole group of zeros when len(N) is already a multiple of the group size, and it doesn't cover exactly 9 digits; like Chromium's openscreen, padding is only added when needed and 9 digits use groups of four. Leading zeros are dropped when decoding, so this doesn't affect interoperability.
func ParsePSK ¶
ParsePSK decodes a base-10 numeric PSK as typed or displayed (network.bs Appendix B "Base-10 Numeric"): "Remove dashes and leading zeros from N. Parse N as a base-10 decimal number to obtain P." Whitespace is ignored as well. It returns the canonical decimal text of P, which is the SPAKE2 password pw: e.g. "0614-8854-8833" and "61488548833" both yield "61488548833". This is the encoding Chromium's openscreen uses too.
func ReadTypedMessage ¶
ReadTypedMessage reads a message that must have the given type key and decodes its body into v.
Types ¶
type ALPNListener ¶
type ALPNListener struct {
// contains filtered or unexported fields
}
ALPNListener allows listening for application protocols on the same port as OSP.
func (*ALPNListener) Accept ¶
func (l *ALPNListener) Accept(ctx context.Context) (quic.Connection, error)
func (*ALPNListener) Close ¶
func (l *ALPNListener) Close() error
Close stops accepting connections for the ALPN. It is idempotent.
type ALPNListenerConfig ¶
type ALPNListenerConfig struct {
VerifyConnection func(cs tls.ConnectionState) error
}
type Agent ¶
type Agent struct {
PeerID PeerID
Certificate *tls.Certificate
CertificateSNBase uint32
CertificateSNCounter uint32
// contains filtered or unexported fields
}
func NewAgent ¶
func NewAgent(c AgentConfig) (*Agent, error)
func (*Agent) AgentInfoMessage ¶
func (a *Agent) AgentInfoMessage() MsgAgentInfo
AgentInfoMessage returns the agent-info a local agent sends in an agent-info-response: its display and model name, the capabilities ospc implements plus AgentConfig.Capabilities, its state token and locales.
func (*Agent) Authenticated ¶
Authenticated reports whether a connection to this remote agent completed authentication.
func (*Agent) AuthenticationInfo ¶
func (a *Agent) AuthenticationInfo() *AgentAuthenticationInfo
func (*Agent) CertificateFingerPrint ¶
func (*Agent) DiscoveredInstanceName ¶
func (a *Agent) DiscoveredInstanceName() (InstanceName, bool)
DiscoveredInstanceName returns the DNS-SD Instance Name a remote agent was discovered with, if it was discovered.
func (*Agent) DisplayName ¶
func (a *Agent) DisplayName() (string, DisplayNameStatus)
DisplayName returns the name to present for a remote agent and its status:
- Once authenticated, the display name from agent-info is verified if the Instance Name the agent was discovered with is a prefix of it, or if the agent wasn't discovered (incoming connection).
- Otherwise the discovered Instance Name is returned, unverified or truncated (should not be shown).
- An agent that wasn't discovered and isn't authenticated yet returns the agent-info display name as unverified, or "" if it has none.
A mismatch between Instance Name and agent-info display name leaves the Instance Name unverified; the agent-info is still available through Info.
Example ¶
a := &Agent{}
a.setDiscovery(parseInstanceName(`Living\ Ro\000`), 0, false)
name, status := a.DisplayName()
fmt.Println(name, status, status.Displayable())
a.setInfo(AgentInfo{DisplayName: "Living Room TV"})
a.setAuthenticated()
name, status = a.DisplayName()
fmt.Println(name, status, status.Displayable())
Output: Living Ro truncated false Living Room TV verified true
func (*Agent) HasAuthenticationInfo ¶
func (*Agent) HasCapability ¶
func (a *Agent) HasCapability(cap AgentCapability) bool
HasCapability reports whether a remote agent listed cap in its agent-info. It is false until the agent's info is known.
func (*Agent) Metadata ¶
func (a *Agent) Metadata() (AgentMetadata, bool)
Metadata returns a snapshot of a remote agent's metadata, to compare later discoveries against with DiscoveredAgent.MetadataChangedSince. It returns false if no agent-info was received yet.
func (*Agent) MetadataVersion ¶
MetadataVersion returns the mv value advertised by this (local) agent.
func (*Agent) NewRemoteAgent ¶
func (a *Agent) NewRemoteAgent(nc NetworkConnection) (*Agent, error)
func (*Agent) NextRequestID ¶
NextRequestID returns the next request (or watch) ID to use for a message sent by this (local) agent to the given peer.
func (*Agent) RequestIDs ¶
func (a *Agent) RequestIDs() *RequestIDAllocator
RequestIDs returns the allocator used for request and watch IDs sent by this (local) agent. It is shared by all connections of the agent, so IDs continue to increase when reconnecting to the same peer.
func (*Agent) ResetState ¶
func (a *Agent) ResetState()
ResetState signals that this (local) agent lost its state: it picks a new state token and resets the request ID counters of all peers to 1.
func (*Agent) StateToken ¶
StateToken returns the state token of this (local) agent: 8 characters in the range [0-9A-Za-z].
type AgentAuthenticationInfo ¶
type AgentAuthenticationInfo struct {
PSKConfig PSKConfig
}
type AgentCapability ¶
type AgentCapability = msgAgentCapability
AgentCapability represents an agent capability
type AgentConfig ¶
type AgentConfig struct {
// ID
PeerID PeerID
Certificate *tls.Certificate
CertificateSNBase uint32
// Info
DisplayName string
ModelName string
Locales []string
// Capabilities are advertised in agent-info in addition to the ones ospc
// itself implements (data channels, QUIC transport). application.bs
// "Presentation API": a controlling user agent must support
// control-presentation (CapabilityControlPresentation), a receiving user
// agent receive-presentation (CapabilityReceivePresentation).
Capabilities []AgentCapability
// AuthInfo
PSKConfig PSKConfig
SupportedTransports []AgentTransport
// KeepAlive configures the connection keep-alive.
KeepAlive KeepAliveConfig
// Logger receives the logs of the agent and its connections. Per-message
// logs are at debug level. Nil discards all logs.
Logger *slog.Logger
// InteropQuirks enables deviations from the spec for interop with
// non-conforming peers. All are off by default, see InteropQuirks.
InteropQuirks InteropQuirks
// SessionExtension enables the LP2P session extension
// (local-peer-to-peer index.bs "Session extension") on connections
// accepted by this agent's listeners: every client must send an
// lp2p-session-request, reported by RequestingOrigin, and the connection
// is refused (RefuseSession) or accepted (CompleteSession) by the caller;
// auth-capabilities is only sent on RequestAuthentication and SPAKE2 is
// only ever started by the client. Off by default: plain OSP peers don't
// send lp2p-session-request. Dialing with it is per connection, see
// DiscoveredAgent.DialSession.
SessionExtension bool
}
func NewAgentConfig ¶
func NewAgentConfig(nickname string) AgentConfig
func (*AgentConfig) WithCertificateSNBase ¶
func (c *AgentConfig) WithCertificateSNBase(snBase uint32)
func (*AgentConfig) WithLogger ¶
func (c *AgentConfig) WithLogger(logger *slog.Logger)
WithLogger sets the logger of the agent, see AgentConfig.Logger.
func (*AgentConfig) WithModelName ¶
func (c *AgentConfig) WithModelName(modelName string)
func (*AgentConfig) WithPSKConfig ¶
func (c *AgentConfig) WithPSKConfig(easeOfInput, entropy int)
type AgentInfo ¶
type AgentInfo struct {
DisplayName string
ModelName string
// Capabilities as listed in agent-info. For a remote agent they are
// what it advertised; for a local agent see AgentInfoMessage.
Capabilities []AgentCapability
StateToken string // 8 characters in the range [0-9A-Za-z]
Locales []string
}
type AgentMetadata ¶
type AgentMetadata struct {
PeerID PeerID
// MetadataVersion is the mv value seen at discovery, before Info was
// retrieved. Only meaningful if HasMetadataVersion is set.
MetadataVersion uint64
HasMetadataVersion bool
// Info is the agent-info received from the agent.
Info AgentInfo
}
AgentMetadata is a snapshot of what is known about a remote agent after connecting to it. Callers can keep it (e.g. keyed by PeerID) and compare a later discovery against it to avoid needless connections; see DiscoveredAgent.MetadataChangedSince.
func (AgentMetadata) StateReset ¶
func (m AgentMetadata) StateReset(info AgentInfo) bool
StateReset reports whether the agent advertised a different state-token in info than in the snapshot, meaning it lost its state. Per application.bs, the agent "should discard any requests, responses, watches and events for that agent".
type AgentTransport ¶
type AgentTransport int
const ( AgentTransportQUIC AgentTransport = iota + 1 AgentTransportWebRTC )
type ApplicationConnection ¶
type ApplicationConnection interface {
AcceptStream(context.Context) (ApplicationStream, error)
OpenStreamSync(context.Context) (ApplicationStream, error)
Close() error
}
Abstract connection for the application protocol.
type ApplicationStream ¶
type ApplicationStream interface {
io.ReadWriteCloser
}
Abstract stream for the application protocol.
type AuthenticationRole ¶
type AuthenticationRole int
const ( AuthenticationRolePresenter AuthenticationRole = 0 AuthenticationRoleConsumer AuthenticationRole = 1 )
func (AuthenticationRole) String ¶
func (t AuthenticationRole) String() string
type Connection ¶
type Connection struct {
// contains filtered or unexported fields
}
Connection is an authenticated connection to a remote agent.
func (*Connection) AcceptDataChannel ¶
func (c *Connection) AcceptDataChannel(ctx context.Context) (*DataChannel, error)
AcceptDataChannel accepts a data channel opened by the peer.
func (*Connection) AcceptStream ¶
func (c *Connection) AcceptStream(ctx context.Context) (ApplicationStream, error)
AcceptStream accepts an incoming application stream for use by custom protocols. Streams opened by the peer as unidirectional streams can only be read: Write fails.
The connection owns accepting streams: it reads the type key of the first message on every incoming stream. Streams for protocols handled by the connection itself (data channels, pooled WebTransport) are consumed internally, all others are returned here unchanged, i.e. starting with their type key.
func (*Connection) AcceptWebTransport ¶
func (c *Connection) AcceptWebTransport(ctx context.Context) (*IncomingWebTransport, error)
AcceptWebTransport returns the next WebTransport session the peer opens, to be accepted or refused.
func (*Connection) Authenticated ¶
func (c *Connection) Authenticated() bool
Authenticated reports whether the remote agent was authenticated. It is only false for connections handed over by UnauthenticatedConnection.SkipAuthentication.
func (*Connection) Close ¶
func (c *Connection) Close() error
Close the connection and all associated streams. It waits for all goroutines of the connection to exit. Close is idempotent.
func (*Connection) Done ¶
func (c *Connection) Done() <-chan struct{}
Done is closed when the connection is closed, by either side.
func (*Connection) Err ¶
func (c *Connection) Err() error
Err returns why the connection was closed, or nil if it is still open.
func (*Connection) LocalAgent ¶
func (c *Connection) LocalAgent() *Agent
func (*Connection) OpenDataChannel ¶
func (c *Connection) OpenDataChannel(ctx context.Context, init DataChannelInit) (*DataChannel, error)
OpenDataChannel opens a data channel. It returns once the peer accepted it with a data-channel-open-response.
func (*Connection) OpenStream ¶
func (c *Connection) OpenStream(ctx context.Context) (ApplicationStream, error)
OpenStream opens a new application stream for use by custom protocols. The stream is a raw bidirectional byte stream. The first bytes written should be the type key of an OSP message, as the receiving agent routes incoming streams by it, see AcceptStream.
func (*Connection) OpenUniStream ¶
func (c *Connection) OpenUniStream(ctx context.Context) (ApplicationStream, error)
OpenUniStream opens a new unidirectional application stream, which can only be written to. Per application.bs, OSP messages are sent on unidirectional streams: write the type key and CBOR body of each message (see WriteTypedMessage), messages that must be processed in order on the same stream, and close it when done. Transports without unidirectional streams fall back to a bidirectional stream.
func (*Connection) OpenWebTransport ¶
func (c *Connection) OpenWebTransport(ctx context.Context) (*WebTransportSession, error)
OpenWebTransport opens a WebTransport session on the connection. It returns once the peer accepted it, or fails with ErrWebTransportRefused. It fails with ErrWebTransportUnsupported if the peer doesn't list quic-transport in its agent-info.
func (*Connection) RemoteAgent ¶
func (c *Connection) RemoteAgent() *Agent
func (*Connection) SupportsDatagrams ¶
func (c *Connection) SupportsDatagrams() bool
SupportsDatagrams reports whether QUIC datagrams were negotiated on the connection, so WebTransport sessions on it are "supports-unreliable".
type DTLSTransport ¶
type DTLSTransport struct{}
func NewDTLSTransport ¶
func NewDTLSTransport() *DTLSTransport
func (*DTLSTransport) DialAddr ¶
func (t *DTLSTransport) DialAddr(ctx context.Context, addr string, tlsConf *tls.Config) (NetworkConnection, error)
func (*DTLSTransport) ListenAddr ¶
func (t *DTLSTransport) ListenAddr(addr string, tlsConf *tls.Config) (NetworkListener, error)
type DataChannel ¶
type DataChannel struct {
DataChannelParameters
// contains filtered or unexported fields
}
DataChannel is an open data channel.
func (*DataChannel) Close ¶
func (c *DataChannel) Close() error
Close closes the DataChannel and the underlying Quic stream. The channel's ID is free for reuse from then on.
func (*DataChannel) Read ¶
func (c *DataChannel) Read(p []byte) (int, error)
Read reads a packet of len(p) bytes as binary data
func (*DataChannel) ReadDataChannel ¶
func (c *DataChannel) ReadDataChannel(p []byte) (int, DataEncoding, error)
ReadDataChannel reads a packet of len(p) bytes
func (*DataChannel) ReceiveMessage ¶
func (c *DataChannel) ReceiveMessage() ([]byte, error)
ReceiveMessage
func (*DataChannel) ReceiveMessageWithEncoding ¶
func (c *DataChannel) ReceiveMessageWithEncoding() ([]byte, DataEncoding, error)
ReceiveMessageWithEncoding
func (*DataChannel) SendMessage ¶
func (c *DataChannel) SendMessage(payload []byte) error
SendMessage
func (*DataChannel) SendMessageWithEncoding ¶
func (c *DataChannel) SendMessageWithEncoding(payload []byte, enc DataEncoding) error
SendMessageWithEncoding
func (*DataChannel) Write ¶
func (c *DataChannel) Write(p []byte) (n int, err error)
Write writes len(p) bytes from p as binary data
func (*DataChannel) WriteDataChannel ¶
func (c *DataChannel) WriteDataChannel(p []byte, enc DataEncoding) (n int, err error)
WriteDataChannel writes len(p) bytes from p
type DataChannelInit ¶
type DataChannelInit struct {
Label string
Protocol string
// ID is the channel-id to use. If nil, one is allocated: the QUIC
// client allocates even IDs and the QUIC server odd IDs, taking the
// lowest one of its parity not used by an open data channel of the
// connection. An explicit ID must be free and of the local parity, so
// the two sides never pick the same ID. IDs only label channels;
// channels are told apart by their QUIC stream, so IDs from the peer
// are accepted whatever their parity, duplicates included.
ID *uint64
}
DataChannelInit configures a data channel to open.
type DataChannelParameters ¶
DataChannelParameters describes an open data channel.
type DataEncoding ¶
type DataEncoding uint64
DataEncoding represents pre-agreed encoding IDs used in data-frame messages of a data channel.
type DiscoveredAgent ¶
type DiscoveredAgent struct {
PeerID PeerID
// TXT holds the unescaped TXT records.
TXT TXTRecordSet
// contains filtered or unexported fields
}
DiscoveredAgent represents a discovered remote agent that has not been contacted yet.
func DirectAgent ¶
func DirectAgent(instanceName string, addr *net.UDPAddr, txt TXTRecordSet) (*DiscoveredAgent, error)
DirectAgent returns a DiscoveredAgent for an agent known without mDNS discovery: listening on addr, with the given DNS-SD Instance Name (unescaped) and unescaped TXT records. It can be dialed like a discovered agent.
func (*DiscoveredAgent) AuthToken ¶
func (a *DiscoveredAgent) AuthToken() (string, error)
AuthToken returns the advertised at value: the auth-initiation-token to use in the first authentication message to this agent.
func (DiscoveredAgent) Dial ¶
func (ra DiscoveredAgent) Dial(ctx context.Context, transportType AgentTransport, la *Agent) (*UnauthenticatedConnection, error)
Dial opens a connection to the remote agent.
The advertising agent is verified as network.bs "TLS 1.3" requires: the agent fingerprint of the certificate it presents must match its advertised fp TXT record. Nothing else about the certificate is checked, its identity is established by authentication.
The non-standard sn TXT key, if present, is used to compute the agent hostname sent as TLS server_name. Without it, which is all the spec defines (see w3c/openscreenprotocol#293), the hostname can't be computed and the server_name is the encoded Instance Name and domain only.
Dial returns once the peer completed metadata discovery (agent-info and auth-capabilities), see ErrNoAgentInfo.
func (DiscoveredAgent) DialSession ¶
func (ra DiscoveredAgent) DialSession(ctx context.Context, transportType AgentTransport, la *Agent, origin string) (*UnauthenticatedConnection, error)
DialSession opens a connection to the remote agent with the LP2P session extension, for a session requested by origin. After the agent-info exchange the caller verifies the server (StartAuthentication and AuthenticatePSK, or TrustPeer), sends the lp2p-session-request with RequestSession, follows the server's cues with AwaitSession, and finishes with CompleteSession.
func (*DiscoveredAgent) DisplayName ¶
func (a *DiscoveredAgent) DisplayName() (string, DisplayNameStatus)
DisplayName returns the name to present for the agent before connecting, and its status. The status is DisplayNameTruncated (don't show) or DisplayNameUnverified. After authenticating, use Agent.DisplayName on the connection's remote agent to get a verified display name.
func (*DiscoveredAgent) InstanceName ¶
func (a *DiscoveredAgent) InstanceName() InstanceName
InstanceName returns the parsed DNS-SD Instance Name of the agent.
func (*DiscoveredAgent) MetadataChangedSince ¶
func (a *DiscoveredAgent) MetadataChangedSince(prev AgentMetadata) bool
MetadataChangedSince reports whether the agent's metadata may have changed since prev was retrieved, i.e. whether connecting to the agent to discover updated metadata is warranted. It is conservative: it returns true unless both prev and the advertisement carry the same mv for the same PeerID.
The spec requires mv to only increase; any different value is treated as changed since a lower value likely indicates the agent lost its state.
func (*DiscoveredAgent) MetadataVersion ¶
func (a *DiscoveredAgent) MetadataVersion() (uint64, bool)
MetadataVersion returns the advertised mv value, if present.
func (*DiscoveredAgent) Nickname ¶
func (a *DiscoveredAgent) Nickname() string
Nickname of the remote agent: its unescaped DNS-SD Instance Name, without truncation marker. It is unverified and may be truncated; prefer DisplayName for presenting the agent to users.
type Discoverer ¶
type Discoverer struct {
// contains filtered or unexported fields
}
Discoverer is used to discover agents.
func (*Discoverer) Accept ¶
func (d *Discoverer) Accept(ctx context.Context) (*DiscoveredAgent, error)
Accept returns an a discovered agent. It should be called in a loop.
func (*Discoverer) Close ¶
func (d *Discoverer) Close() error
Close closes the discoverer. Any blocked Accept operations will be unblocked and return errors.
func (*Discoverer) WithNickname ¶
func (d *Discoverer) WithNickname(nickname string)
WithNickname defines a nickname of agents to discover. Agents match if their DNS-SD Instance Name equals the nickname or, if the Instance Name is truncated, is a prefix of it. The match is unverified, see Agent.DisplayName for the verified display name after authentication.
type DisplayNameStatus ¶
type DisplayNameStatus int
DisplayNameStatus describes how far a remote agent's name can be trusted. It models the three categories of application.bs #instance-names.
const ( // DisplayNameTruncated is a truncated and unverified DNS-SD Instance Name. // It should not be shown to the user. DisplayNameTruncated DisplayNameStatus = iota // DisplayNameUnverified is a complete but unverified DNS-SD Instance // Name. It can be shown as unverified prior to authentication. DisplayNameUnverified // DisplayNameVerified is the display name from agent-info, received over // an authenticated connection, of which the Instance Name is a prefix. DisplayNameVerified )
func (DisplayNameStatus) Displayable ¶
func (s DisplayNameStatus) Displayable() bool
Displayable reports whether a name with this status may be shown to the user. Truncated, unverified names should not be shown.
func (DisplayNameStatus) String ¶
func (s DisplayNameStatus) String() string
func (DisplayNameStatus) Verified ¶
func (s DisplayNameStatus) Verified() bool
Verified reports whether the name is a verified display name.
type IncomingWebTransport ¶
type IncomingWebTransport struct {
// contains filtered or unexported fields
}
IncomingWebTransport is a WebTransport session the peer opened, waiting to be accepted or refused.
func (*IncomingWebTransport) Accept ¶
func (i *IncomingWebTransport) Accept() (*WebTransportSession, error)
Accept accepts the session: it answers success and returns the open session. The peer may already have opened streams on it.
func (*IncomingWebTransport) ExchangeID ¶
func (i *IncomingWebTransport) ExchangeID() uint64
ExchangeID is the session's exchange-id.
func (*IncomingWebTransport) Refuse ¶
func (i *IncomingWebTransport) Refuse() error
Refuse refuses the session: it answers permanent-error and ends the control stream.
type InstanceName ¶
type InstanceName struct {
// Name is the unescaped Instance Name without the truncation marker.
Name string
// Truncated is set when the advertiser terminated the Instance Name with
// a null character, signalling it is not the complete display name.
Truncated bool
}
InstanceName is a parsed (unescaped) DNS-SD Instance Name.
func (InstanceName) MatchesNickname ¶
func (n InstanceName) MatchesNickname(nickname string) bool
MatchesNickname reports whether the Instance Name could belong to an agent with the given display name: an exact match for complete names, or a prefix match for truncated names. This is a discovery-time filter only, the result is unverified.
func (InstanceName) Status ¶
func (n InstanceName) Status() DisplayNameStatus
Status returns the unverified status of the Instance Name.
func (InstanceName) Verify ¶
func (n InstanceName) Verify(displayName string) DisplayNameStatus
Verify checks the Instance Name against the display name from agent-info. The caller must only pass a display name received over an authenticated connection. If the Instance Name is not a prefix of the display name, the name stays unverified.
type InteropQuirks ¶
type InteropQuirks struct {
// Spake2SuiteP256 authenticates with the RFC 9382
// SPAKE2-P256-SHA256-HKDF-HMAC ciphersuite: group P-256 instead of
// edwards25519. Everything else follows network.bs (SHA-256 transcript
// hash, HKDF, HMAC, SHA-512 password hash, client/server fingerprints as
// the identities A/B). Both agents must enable it.
//
// Violates network.bs "Authentication with SPAKE2": "Elliptic curve is
// edwards25519". The edwards25519 suite is not implemented (neither here
// nor in Chromium's openscreen, where BoringSSL offers no general
// edwards25519 group operations), so without this quirk AuthenticatePSK
// fails with ErrSpake2SuiteUnsupported. Chromium's osp_demo enables the
// same suite with --spake2_suite=p256.
Spake2SuiteP256 bool
// AcceptCertificateFingerprint accepts an "fp" TXT value computed as the
// base64 SHA-256 of the whole DER-encoded certificate, in addition to the
// spec's SPKI fingerprint, when dialing.
//
// Violates network.bs "Computing the Agent Fingerprint" (fp is the
// RFC 7469 SPKI fingerprint) and "TLS 1.3" (the fingerprint calculated for
// the received certificate must match fp). Older osp_demo builds advertise
// the whole-certificate hash.
AcceptCertificateFingerprint bool
// AdvertiseCertificateFingerprint advertises "fp" as the base64 SHA-256
// of the whole DER-encoded certificate instead of the SPKI fingerprint,
// so peers that pin the whole-certificate hash (older osp_demo
// controllers) accept the listener's certificate. Spec-compliant peers
// will reject the listener.
//
// Violates network.bs "Computing the Agent Fingerprint". The PeerID and
// the SPAKE2 identities are still the SPKI fingerprint.
AdvertiseCertificateFingerprint bool
// TolerateMissingAgentInfo lets Dial and Accept proceed when the peer
// doesn't send agent-info-response and auth-capabilities within a short
// grace period. The remote agent's Info is then nil and its
// authentication capabilities are assumed equal to the local ones.
//
// Violates network.bs "Metadata Discovery" and "Authentication": agents
// must answer agent-info-request, and authentication starts from the
// exchanged auth-capabilities. Older osp_demo builds implement neither.
TolerateMissingAgentInfo bool
// ConsumerInitiatesWithPublicValue makes a PSK consumer that initiates
// SPAKE2 send auth-spake2-handshake psk-needs-presentation with pA, as
// the current network.bs text says, which requires the consumer to know
// the PSK before the presenter showed it. Without it, the consumer's
// psk-needs-presentation carries no public-value: it asks the presenter
// to show the PSK and initiate with psk-shown. Chromium's openscreen
// never sends psk-needs-presentation but, when it presents, fails on one
// without pA unless it initiated first; enable this when osp_demo is the
// presenter (the osp-demo interop harness does with
// OSP_DEMO_EASE_OF_INPUT). Received psk-needs-presentation with pA is
// always answered.
//
// Violates the OSP fix local-peer-to-peer assumes: a consumer can't
// compute pA before it has the PSK, so network.bs "the PSK consumer
// sends ... psk-needs-presentation and the public-value field set to pA"
// is read as carrying no public-value.
ConsumerInitiatesWithPublicValue bool
// AllowSkipAuthentication enables
// UnauthenticatedConnection.SkipAuthentication, which hands the
// connection to the application protocol WITHOUT authenticating the
// peer. Application messages the peer sent before the hand-off are
// delivered rather than discarded. Every skipped authentication is logged
// at warning level.
//
// INSECURE. Violates network.bs "Authentication": agents must
// authenticate with SPAKE2 before exchanging application messages. Only
// for debugging against peers without authentication.
AllowSkipAuthentication bool
}
InteropQuirks relaxes the protocol to interoperate with implementations that deviate from the Open Screen Network Protocol (network.bs), such as older builds of Chromium's osp_demo. Every quirk breaks a rule of the spec and is off by default. Enable them only for interop testing and debugging, never in production: several of them remove security guarantees.
Only behavior that violates the spec is gated here. Spec-compatible leniency (e.g. dialing an agent that doesn't advertise the non-standard "sn" TXT key, or discovering an agent that omits "mv") is always on.
func (InteropQuirks) Any ¶
func (q InteropQuirks) Any() bool
Any reports whether any quirk is enabled.
type KeepAliveConfig ¶
type KeepAliveConfig struct {
// Disabled stops this agent from sending agent-status-request messages.
// Incoming agent-status-request messages are still answered.
Disabled bool
// Interval overrides DefaultKeepAliveInterval when non-zero.
Interval time.Duration
}
KeepAliveConfig configures the OSP keep-alive mechanism: an agent behaves as though a timer, shorter than the idle timeout, is reset every time a message is sent. When the timer expires an agent-status-request is sent. QUIC PING frames are not used.
type Listener ¶
type Listener struct {
// contains filtered or unexported fields
}
Listener acts as an advertising OSP agent and listens for incoming connections.
func Listen ¶
func Listen(transportType AgentTransport, a *Agent) (*Listener, error)
Listen starts an advertising agent and listens for incoming connections.
func NewListener ¶
func NewListener(a *Agent, transportType AgentTransport) *Listener
NewListener creates a new Listener
func (*Listener) Accept ¶
func (l *Listener) Accept(ctx context.Context) (*UnauthenticatedConnection, error)
Accept returns the next incoming connection. It should be called in a loop.
func (*Listener) Addr ¶
Addr returns the local address the listener listens on, or nil if it is not started.
func (*Listener) AnnounceMetadataChange ¶
AnnounceMetadataChange signals listening agents that the metadata of the local agent changed by advertising a greater mv value, so they know to reconnect to discover the updated metadata.
func (*Listener) Close ¶
Close closes the listener and the connections it didn't hand out yet. Connections returned by Accept are not closed. Any blocked Accept operations are unblocked and return ErrListenerClosed. Close is idempotent.
func (*Listener) ListenApplication ¶
func (l *Listener) ListenApplication(alpn string, config *ALPNListenerConfig) *ALPNListener
ListenApplication allows you to listen for quic connections on the same port but with a different ALPN. Only one per ALPN is allowed. Needs to be registered before starting the Listener.
func (*Listener) StartWithoutAdvertising ¶
StartWithoutAdvertising listens on addr (e.g. "127.0.0.1:0") without advertising the agent with mDNS. Peers connect with a DiscoveredAgent from DirectAgent, using Addr and TXT.
func (*Listener) TXT ¶
func (l *Listener) TXT() TXTRecordSet
TXT returns a copy of the TXT records the listener advertises, or would advertise when started without advertising.
type MsgAgentInfo ¶
type MsgAgentInfo struct {
DisplayName string `cbor:"0,keyasint"`
ModelName string `cbor:"1,keyasint,omitempty"`
Capabilities []AgentCapability `cbor:"2,keyasint"`
StateToken string `cbor:"3,keyasint"`
Locales []string `cbor:"4,keyasint"`
}
MsgAgentInfo is the exported agent-info structure
type MsgAgentInfoEvent ¶
type MsgAgentInfoEvent struct {
AgentInfo MsgAgentInfo `cbor:"0,keyasint"`
}
MsgAgentInfoEvent is the exported agent-info-event message
type MsgAgentInfoRequest ¶
type MsgAgentInfoRequest struct {
RequestID RequestID `cbor:"0,keyasint"`
}
MsgAgentInfoRequest is the exported agent-info-request message
type MsgAgentInfoResponse ¶
type MsgAgentInfoResponse struct {
RequestID RequestID `cbor:"0,keyasint"`
AgentInfo MsgAgentInfo `cbor:"1,keyasint"`
}
MsgAgentInfoResponse is the exported agent-info-response message
type MsgAgentStatusRequest ¶
type MsgAgentStatusRequest struct {
RequestID RequestID `cbor:"0,keyasint"`
Status *string `cbor:"1,keyasint,omitempty"`
}
MsgAgentStatusRequest is the exported agent-status-request message
type MsgAgentStatusResponse ¶
type MsgAgentStatusResponse struct {
RequestID RequestID `cbor:"0,keyasint"`
Status *string `cbor:"1,keyasint,omitempty"`
}
MsgAgentStatusResponse is the exported agent-status-response message
type NetworkConnection ¶
type NetworkConnection interface {
// Write sends a single serialized message.
io.WriteCloser
// ReadMessage returns the next received message. Transports with multiple
// streams read them independently: ordering is only preserved for
// messages within the same stream.
ReadMessage() (interface{}, error)
// Determines if this connection supports reliable delivery. If not, the
// network protocol agent needs to perform timeout & retransmission.
IsReliable() bool
// Upgrades the connection to an application connection. Any proceeding
// calls to the network connection will fail.
IntoApplicationConnection() (ApplicationConnection, error)
ConnectionState() tls.ConnectionState
}
Abstract connection for the network protocol, responsible for getting extended agent capabilities and performing the authentication ceremony.
type NetworkListener ¶
type NetworkTransport ¶
type NetworkTransport interface {
DialAddr(ctx context.Context, addr string, tlsConf *tls.Config) (NetworkConnection, error)
ListenAddr(addr string, tlsConf *tls.Config) (NetworkListener, error)
}
func NewNetworkTransport ¶
func NewNetworkTransport(typ AgentTransport) (NetworkTransport, error)
type PeerID ¶
type PeerID string
PeerID is a unique identifier for a peer, it is equal tot the OSP agent fingerprint (FP) of the peer.
type QuicTransport ¶
type QuicTransport struct {
// IdleTimeout is the QUIC idle_timeout transport parameter. Zero means
// DefaultIdleTimeout.
IdleTimeout time.Duration
// KeepAlive configures sending agent-status-request messages.
KeepAlive KeepAliveConfig
// Logger receives transport logs. Nil discards them.
Logger *slog.Logger
}
func NewQuicTransport ¶
func NewQuicTransport() *QuicTransport
func (*QuicTransport) DialAddr ¶
func (t *QuicTransport) DialAddr(ctx context.Context, addr string, tlsConf *tls.Config) (NetworkConnection, error)
func (*QuicTransport) ListenAddr ¶
func (t *QuicTransport) ListenAddr(addr string, tlsConf *tls.Config) (NetworkListener, error)
type RequestIDAllocator ¶
type RequestIDAllocator struct {
// contains filtered or unexported fields
}
RequestIDAllocator allocates request and watch IDs per remote agent.
Per the OpenScreen Application Protocol, request-id and watch-id values come from a counter that starts at 1 and increments by 1 for each ID. IDs are not tied to a particular connection: closing and reopening a connection to the same peer must not reset the counter. Counters are therefore keyed by the remote agent's PeerID (its certificate fingerprint), which identifies the peer across reconnects.
The counters are only reset when the local agent changes its state token (see Agent.ResetState).
A RequestIDAllocator is safe for concurrent use.
func NewRequestIDAllocator ¶
func NewRequestIDAllocator() *RequestIDAllocator
NewRequestIDAllocator returns an empty allocator.
func (*RequestIDAllocator) Next ¶
func (a *RequestIDAllocator) Next(peer PeerID) uint64
Next returns the next request ID for the given peer. The first ID for a peer is 1.
func (*RequestIDAllocator) Reset ¶
func (a *RequestIDAllocator) Reset()
Reset resets the counters of all peers to 1.
type SessionCue ¶
type SessionCue int
SessionCue is what AwaitSession reports.
const ( // SessionCueAuthenticate: a trust ceremony is due. On the client, the // server answered authentication-required; on the server, the client // started SPAKE2. Run AcceptAuthenticate and AuthenticatePSK (the // client first StartAuthentication), then AwaitSession again. SessionCueAuthenticate SessionCue = iota + 1 // SessionCueAccepted (client): the server accepted the session; call // CompleteSession. SessionCueAccepted // SessionCueRequested (server): the lp2p-session-request arrived. If // the client isn't verified yet, call RequireAuthentication and wait for // SessionCueAuthenticate; otherwise RequestingOrigin is available and // the session is accepted with CompleteSession or refused with // RefuseSession. SessionCueRequested )
func (SessionCue) String ¶
func (c SessionCue) String() string
type TXTRecordSet ¶
func (TXTRecordSet) Add ¶
func (r TXTRecordSet) Add(key, value string)
func (TXTRecordSet) FromSlice ¶
func (r TXTRecordSet) FromSlice(in []string) error
func (TXTRecordSet) Get ¶
func (r TXTRecordSet) Get(key string) []string
func (TXTRecordSet) Set ¶
func (r TXTRecordSet) Set(key, value string)
func (TXTRecordSet) ToSlice ¶
func (r TXTRecordSet) ToSlice() []string
type TypeKey ¶
type TypeKey uint64
type UnauthenticatedConnection ¶
type UnauthenticatedConnection struct {
// contains filtered or unexported fields
}
UnauthenticatedConnection represents an OSPC connection that didn't pass authentication yet.
func (*UnauthenticatedConnection) AcceptAuthenticate ¶
func (c *UnauthenticatedConnection) AcceptAuthenticate(ctx context.Context) (role AuthenticationRole, err error)
AcceptAuthenticate waits until the authentication role is known, after the auth-capabilities exchange. The presenter then shows a PSK to the user (GeneratePSK, FormatPSK), the consumer asks the user for it; both pass it to AuthenticatePSK.
func (*UnauthenticatedConnection) AuthenticatePSK ¶
func (c *UnauthenticatedConnection) AuthenticatePSK(ctx context.Context, psk []byte) (*Connection, error)
AuthenticatePSK authenticates the peer with SPAKE2 (network.bs "Authentication with SPAKE2") using psk, a base-10 numeric PSK in any form ParsePSK accepts. It blocks until authentication completes or ctx is done.
If the peer already initiated, the local agent answers it. Otherwise it initiates: the PSK presenter with psk-status psk-shown, the PSK consumer with psk-needs-presentation. A consumer that doesn't know the PSK yet asks the user for it after AcceptAuthenticate; the presenter's handshake is answered once AuthenticatePSK is called.
The spec's edwards25519 suite is not implemented: without InteropQuirks.Spake2SuiteP256 it fails with ErrSpake2SuiteUnsupported and closes the connection. A malformed psk returns ErrInvalidPSK and leaves the connection open.
On the client of a connection made with DialSession, the returned Connection must not be used before CompleteSession returns it: until the server accepted the session, streams it opens are held.
func (*UnauthenticatedConnection) AwaitSession ¶
func (c *UnauthenticatedConnection) AwaitSession(ctx context.Context) (SessionCue, error)
AwaitSession waits for the next cue of the session exchange.
On the client, after RequestSession: SessionCueAuthenticate when the server answers authentication-required, SessionCueAccepted when it accepts. On the server, after Accept: SessionCueAuthenticate when the client starts SPAKE2, SessionCueRequested when its lp2p-session-request arrives. Each cue is reported once; it fails with ErrSessionRefused if the server refuses, or with the close reason.
func (*UnauthenticatedConnection) Close ¶
func (c *UnauthenticatedConnection) Close() error
Close the unauthenticated connection. If the connection has progressed to authenticated, it is not closed but an error is returned. This allows for defer closing regardless.
func (*UnauthenticatedConnection) CompleteSession ¶
func (c *UnauthenticatedConnection) CompleteSession(ctx context.Context) (*Connection, error)
CompleteSession finishes the session exchange and returns the connection.
On the server it accepts the session: it sends the success lp2p-session-response, once the client's certificate is verified (by AuthenticatePSK, or because the caller trusts its fingerprint, see TrustPeer) and the user granted the session.
On the client it waits for the success response, if not received yet.
Until it returns, neither agent sends messages of the data channel or WebTransport extensions.
func (*UnauthenticatedConnection) GeneratePSK ¶
func (c *UnauthenticatedConnection) GeneratePSK() ([]byte, error)
GeneratePSK creates a random PSK for the PSK presenter, with the entropy both agents require. It is canonical decimal text (see ParsePSK): show it to the user with FormatPSK and pass it to AuthenticatePSK.
func (*UnauthenticatedConnection) GetAuthenticationRole ¶
func (c *UnauthenticatedConnection) GetAuthenticationRole() AuthenticationRole
GetAuthenticationRole determines if the agent should act as presenter or consumer of the PSK.
func (*UnauthenticatedConnection) LocalAgent ¶
func (c *UnauthenticatedConnection) LocalAgent() *Agent
LocalAgent provides info about local agent
func (*UnauthenticatedConnection) RefuseSession ¶
func (c *UnauthenticatedConnection) RefuseSession() error
RefuseSession refuses the session on the server: it answers the lp2p-session-request with permanent-error and closes the connection once the client ended its side of the stream, or after a short wait. Call it when no receiver allows the requesting origin, or when the user declines.
func (*UnauthenticatedConnection) RemoteAgent ¶
func (c *UnauthenticatedConnection) RemoteAgent() *Agent
RemoteAgent provides info about remote agent
func (*UnauthenticatedConnection) RequestSession ¶
func (c *UnauthenticatedConnection) RequestSession(ctx context.Context) error
RequestSession sends the lp2p-session-request with the origin given to DialSession, on the client. The server's certificate must be verified first, by AuthenticatePSK or TrustPeer (ErrSessionNotVerified otherwise). It fails with ErrSessionUnsupported if the agent lists neither data-channels nor quic-transport. Then follow the server's cues with AwaitSession.
func (*UnauthenticatedConnection) RequestingOrigin ¶
func (c *UnauthenticatedConnection) RequestingOrigin() (origin string, ok bool)
RequestingOrigin returns the requesting origin the client sent in its lp2p-session-request, on the server, once the client's certificate is verified: before, it isn't looked at. It is the ASCII serialization of an origin as asserted by the client's user agent: parse it, and treat a value that doesn't parse as an opaque origin, which matches nothing.
func (*UnauthenticatedConnection) RequireAuthentication ¶
func (c *UnauthenticatedConnection) RequireAuthentication() error
RequireAuthentication answers the lp2p-session-request with the provisional authentication-required, on a server that hasn't verified the client's certificate. The client then starts SPAKE2: wait for SessionCueAuthenticate with AwaitSession and run AcceptAuthenticate and AuthenticatePSK.
func (*UnauthenticatedConnection) SkipAuthentication ¶
func (c *UnauthenticatedConnection) SkipAuthentication() (*Connection, error)
SkipAuthentication hands the connection to the application protocol without authenticating the peer. INSECURE, for interop debugging only: it returns ErrQuirkDisabled unless InteropQuirks.AllowSkipAuthentication is set on the local agent, and violates network.bs, which requires authentication before exchanging application messages. The returned Connection reports Authenticated() == false.
func (*UnauthenticatedConnection) StartAuthentication ¶
func (c *UnauthenticatedConnection) StartAuthentication() error
StartAuthentication asks the PSK presenter to show the PSK when this agent is the consumer, see baseConnection.StartAuthentication. Call it before asking the user for the PSK.
func (*UnauthenticatedConnection) TrustPeer ¶
func (c *UnauthenticatedConnection) TrustPeer() error
TrustPeer marks the peer's certificate as verified without SPAKE2, because its fingerprint (RemoteAgent().PeerID) is a trust anchor of the caller.
type UniStreamOpener ¶
type UniStreamOpener interface {
OpenUniStreamSync(context.Context) (ApplicationStream, error)
}
UniStreamOpener is implemented by application connections that support unidirectional streams, as used for OSP messages (application.bs). The returned stream can only be written to.
type WebTransportCloseInfo ¶
WebTransportCloseInfo is the close code and reason of a cleanly closed session.
type WebTransportSession ¶
type WebTransportSession struct {
// contains filtered or unexported fields
}
WebTransportSession is an open WebTransport session pooled on an OSP connection.
func (*WebTransportSession) AcceptStream ¶
func (s *WebTransportSession) AcceptStream(ctx context.Context) (*WebTransportStream, error)
AcceptStream returns the next bidirectional stream the peer opened.
func (*WebTransportSession) AcceptUniStream ¶
func (s *WebTransportSession) AcceptUniStream(ctx context.Context) (*WebTransportStream, error)
AcceptUniStream returns the next unidirectional stream the peer opened, which can only be read.
func (*WebTransportSession) Close ¶
func (s *WebTransportSession) Close(info WebTransportCloseInfo) error
Close closes the session with info: it sends data-transport-close, ends the control stream and resets all streams of the session with session-gone. The reason must be at most 1024 bytes. It returns once the peer ended its side of the control stream, so that closing the connection right after doesn't discard the close, or after a short wait.
func (*WebTransportSession) Closed ¶
func (s *WebTransportSession) Closed(ctx context.Context) (WebTransportCloseInfo, error)
Closed waits until the session is closed. It returns the close info of a clean close (data-transport-close, or FIN on the control stream), or the error of an abrupt one (control stream reset, connection closed).
func (*WebTransportSession) Done ¶
func (s *WebTransportSession) Done() <-chan struct{}
Done is closed once the session is closed, cleanly or not.
func (*WebTransportSession) Drain ¶
func (s *WebTransportSession) Drain() error
Drain asks the peer to finish using the session and close it.
func (*WebTransportSession) Draining ¶
func (s *WebTransportSession) Draining() <-chan struct{}
Draining is closed once the peer asked to finish using the session.
func (*WebTransportSession) ExchangeID ¶
func (s *WebTransportSession) ExchangeID() uint64
ExchangeID is the session's exchange-id.
func (*WebTransportSession) ExportKeyingMaterial ¶
func (s *WebTransportSession) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error)
ExportKeyingMaterial derives keying material of the given length that both sides of the session derive identically and no other session yields: the TLS exporter of the QUIC connection with the label "EXPORTER-WebTransport" and the WebTransport Exporter Context of draft-ietf-webtrans-http3 section 4.8, with the exchange-id as session ID, label and context inside it. Label and context are at most 255 bytes.
func (*WebTransportSession) OpenStreamSync ¶
func (s *WebTransportSession) OpenStreamSync(ctx context.Context) (*WebTransportStream, error)
OpenStreamSync opens a bidirectional stream on the session.
func (*WebTransportSession) OpenUniStreamSync ¶
func (s *WebTransportSession) OpenUniStreamSync(ctx context.Context) (*WebTransportStream, error)
OpenUniStreamSync opens a unidirectional stream on the session, which can only be written.
func (*WebTransportSession) ReceiveDatagram ¶
func (s *WebTransportSession) ReceiveDatagram(ctx context.Context) ([]byte, error)
ReceiveDatagram returns the next datagram received for the session.
func (*WebTransportSession) SendDatagram ¶
func (s *WebTransportSession) SendDatagram(p []byte) error
SendDatagram sends p as a datagram of the session: a QUIC DATAGRAM frame with the exchange-id as a varint before p.
func (*WebTransportSession) SupportsDatagrams ¶
func (s *WebTransportSession) SupportsDatagrams() bool
SupportsDatagrams reports whether the session delivers datagrams: it is "supports-unreliable" if so and "reliable-only" otherwise.
type WebTransportStream ¶
type WebTransportStream struct {
// contains filtered or unexported fields
}
WebTransportStream is a stream of a WebTransportSession: a QUIC stream of the same directionality. Reads and writes fail with a *WebTransportStreamError when the stream was reset or stopped with a WebTransport error code, and with ErrWebTransportSessionGone when it was reset or stopped because the session is gone.
func (*WebTransportStream) CancelRead ¶
func (st *WebTransportStream) CancelRead(code uint32)
CancelRead stops receiving with a WebTransport error code (STOP_SENDING).
func (*WebTransportStream) CancelWrite ¶
func (st *WebTransportStream) CancelWrite(code uint32)
CancelWrite aborts sending with a WebTransport error code (RESET_STREAM).
func (*WebTransportStream) Close ¶
func (st *WebTransportStream) Close() error
Close ends the sending side of the stream with FIN.
func (*WebTransportStream) StreamID ¶
func (st *WebTransportStream) StreamID() int64
StreamID is the QUIC stream ID.
func (*WebTransportStream) Unidirectional ¶
func (st *WebTransportStream) Unidirectional() bool
Unidirectional reports whether the stream has only one direction: it can only be written if opened locally, only read if opened by the peer.
type WebTransportStreamError ¶
type WebTransportStreamError struct {
Code uint32
Remote bool // The peer reset or stopped the stream.
}
WebTransportStreamError is returned by stream operations when the stream was reset or stopped with a WebTransport stream error code.
func (*WebTransportStreamError) Error ¶
func (e *WebTransportStreamError) Error() string
Source Files
¶
- agent.go
- apln_listener.go
- connection.go
- connection_application.go
- connection_network.go
- datachannel.go
- dialer.go
- discover.go
- discover_names.go
- hostname.go
- keepalive.go
- listener.go
- message.go
- message_io.go
- messages_lp2p.go
- messages_lp2p_ext.go
- messages_osp.go
- metadata.go
- ospc.go
- psk.go
- quirks.go
- request_id.go
- session.go
- transport.go
- transport_quic.go
- transport_webrtc.go
- varuint.go
- webtransport.go