Documentation
¶
Index ¶
- Constants
- Variables
- func AddDirectPath(ctx context.Context, sc *SignalConn, ln *net.TCPListener, ...) <-chan net.Conn
- func AwaitPeerClose(conn net.Conn, d time.Duration)
- func Chat(conn net.Conn, localTag, remoteTag string, stdin io.Reader, stdout io.Writer) error
- func ConnID(c net.Conn) string
- func Connect(ctx context.Context, myListener *net.TCPListener, theirCandidates []string, ...) (net.Conn, error)
- func DiscoverExternalAddr(localPort int) (string, error)
- func GatherCandidates(_ context.Context) (*net.TCPListener, []string, error)
- func GatherHostCandidates(port int) []string
- func ListenHostCandidates() (*net.TCPListener, []string, error)
- func OpenBrowser(url string) error
- func OpenSignalStream(ctx context.Context, agent *rpcclient.Agent, targetAgentID string) (rpcclient.ClientStream, error)
- func ReceiveDir(conn net.Conn, outputDir string) error
- func ReceiveFile(conn net.Conn, outputDir string, progress ProgressFunc) (string, error)
- func SendAnswer(stream rpcclient.ServerStream, msg *SignalMsg) error
- func SendDir(conn net.Conn, dirPath string, progress ProgressFunc) error
- func SendFile(conn net.Conn, path string, progress ProgressFunc) error
- func SendHangupServer(stream rpcclient.ServerStream, sessionID, reason string)
- func SendOffer(stream rpcclient.ClientStream, msg *SignalMsg) error
- func SendRelay(stream rpcclient.ClientStream, sessionID string) error
- type CallServer
- type ConnectOption
- type DataPath
- type E2E
- func (e *E2E) Answer(offer, answer *SignalMsg) error
- func (e *E2E) AwaitConfirm(stream rpcclient.ServerStream) error
- func (e *E2E) Complete(peerKey, peerKeyID string) error
- func (e *E2E) Confirm() string
- func (e *E2E) DirectToken() []byte
- func (e *E2E) Fingerprint() string
- func (e *E2E) FinishOffer(stream rpcclient.ClientStream, answer *SignalMsg) error
- func (e *E2E) KeyID() string
- func (e *E2E) Paired() bool
- func (e *E2E) PublicKey() string
- func (e *E2E) Secure(c net.Conn) net.Conn
- func (e *E2E) SignalConn(first FramePath) *SignalConn
- type Frame
- type FrameConn
- type FramePath
- type FrameType
- type ICEConfig
- type Meta
- type Pair
- type ProgressFunc
- type RaceEntry
- type RelayConn
- func (rc *RelayConn) Close() error
- func (rc *RelayConn) LocalAddr() net.Addr
- func (rc *RelayConn) Read(b []byte) (int, error)
- func (rc *RelayConn) RemoteAddr() net.Addr
- func (rc *RelayConn) SetDeadline(_ time.Time) error
- func (rc *RelayConn) SetReadDeadline(_ time.Time) error
- func (rc *RelayConn) SetWriteDeadline(_ time.Time) error
- func (rc *RelayConn) Write(b []byte) (int, error)
- type RelayMux
- type SignalConn
- type SignalMsg
- type SignalType
- type TCPDirect
Constants ¶
const ( PathHost = "webrtc-host" PathSrflx = "webrtc-srflx" // through a NAT mapping (srflx/prflx) PathTURN = "webrtc-turn" // a TURN relay candidate PathRelay = "relay" // the nfltr relay (ICE found no path, or --force-relay) )
Data path names (the CLI's "data path:" line, the NAT lab's matrix).
const ( RaceWon = "won" // the path chosen RaceLost = "lost" // ready, another chosen RaceFailed = "failed" // failed before the choice RaceLate = "late" // not ready when the choice was made )
Race outcomes (RaceEntry.Outcome).
const PathTCP = "tcp-direct"
PathTCP is a file transfer on the direct TCP channel (same mutual HMAC and encryption as before WebRTC): the fast path on a LAN.
const RaceWebRTC = "webrtc"
RaceWebRTC is the data channel's RaceEntry.Path, whichever ICE path (DataPath.Path) it took.
Variables ¶
var ErrE2EMismatch = errors.New("p2p: end-to-end key check failed: the two machines use different " +
"A2A keys (--e2ee-key-file / NFLTR_A2A_E2EE_KEY_FILE: the root key, or a machine key of one of the two; set one on both, or none), " +
"or something between them (the relay) replaced the keys")
ErrE2EMismatch is a failed key confirmation: the two sides derived different keys.
var ErrMuxOverrun = errors.New("p2p: relay stream path overrun: the peer sent past its credit")
ErrMuxOverrun is a read on a relay mux path whose peer sent past the path's window (a protocol violation: it ignored its credit). The path is failed after what arrived within the window; the other path is unaffected.
var ErrNoRoute = errors.New("p2p: could not establish direct connection (all candidates failed)")
ErrNoRoute is returned when all P2P connection attempts fail and no relay fallback is configured.
var ErrPeerRelay = errors.New("p2p: peer chose the server relay")
ErrPeerRelay is Connect's error, with AwaitConn, when the dialing side sent SignalRelay instead of a connection: the session continues on the server relay (NewServerRelayConn).
var ErrSTUNOff = errors.New("stun: no STUN server configured")
ErrSTUNOff is DiscoverExternalAddr's error when STUNServer is "".
var RTCWait = 10 * time.Second
RTCWait bounds how long the sender waits for the data channel to open before it falls back to the relay.
var STUNServer = "stun.l.google.com:19302"
STUNServer is the public STUN server used to discover the external (NAT-mapped) address of a local listener. Uses Google's free STUN service by default; "" turns discovery off (nfltr p2p with a private relay).
Functions ¶
func AddDirectPath ¶
func AddDirectPath(ctx context.Context, sc *SignalConn, ln *net.TCPListener, theirCandidates []string, token []byte, sessionID, role string, ) <-chan net.Conn
AddDirectPath tries the direct TCP channel (Connect) while signaling already flows over sc's relay path; on success the channel becomes another path. The returned channel yields the connection (nil if none) once the attempt ends.
func AwaitPeerClose ¶
AwaitPeerClose waits up to d for the peer to close conn (Read returns an error, io.EOF on a clean close), discarding what it reads. A receiver calls it after its ack: a sender closes only once it read the ack, so returning then means the ack was delivered. Without it a receiver that exited right after writing the ack could take the ack with it on the relay path (R3-G10: the file arrived intact, the sender exited 1 on "waiting for ack").
func Chat ¶
Chat runs an interactive text chat session over the connection. localTag is the display name for locally-typed lines (e.g., "you"). remoteTag is the display name for received lines. stdin/stdout are the I/O streams; os.Stdin / os.Stdout in normal use.
func ConnID ¶
ConnID identifies an authenticated direct connection: a hash of both handshake challenges, the same on both ends whatever a NAT did to the addresses. "" for any other conn.
func Connect ¶
func Connect( ctx context.Context, myListener *net.TCPListener, theirCandidates []string, token []byte, sessionID string, _ string, opts ...ConnectOption, ) (net.Conn, error)
Connect establishes a peer-to-peer TCP connection using the exchanged candidates. Both sides run it concurrently with the listener from GatherCandidates, the peer's candidates, the shared secret (E2E.DirectToken) and the session ID (HMAC context); role ("initiator"/"responder") names the side. Several connections can authenticate (several host IPs), so the two sides must keep the same one: the dialing side (AnnounceConn) keeps the highest ConnID it saw within a short window of the first and tells the other side (AwaitConn), which keeps exactly that connection. A side given neither option, or whose peer announces nothing (a peer that predates SignalSelect), keeps the highest ConnID it saw within that window, which two loaded peers can pick differently. myListener is closed before returning.
func DiscoverExternalAddr ¶
DiscoverExternalAddr sends one STUN Binding Request from localPort to STUNServer and returns the server-reflexive address ("ip:port") observed by the STUN server. This is the "server-reflexive candidate" in ICE terminology — the external IP:port that peers outside the local NAT can try to reach.
func GatherCandidates ¶
GatherCandidates opens a TCP listener on an OS-assigned port, gathers all local (host) candidates and the STUN server-reflexive candidate, and returns the listener together with the deduplicated candidate list.
The caller is responsible for closing the listener after negotiation.
func GatherHostCandidates ¶
GatherHostCandidates returns "ip:port" strings for every non-loopback IPv4 address on the local machine. These "host candidates" are directly reachable on the LAN without NAT traversal.
func ListenHostCandidates ¶
func ListenHostCandidates() (*net.TCPListener, []string, error)
ListenHostCandidates is GatherCandidates without the STUN probe: the listener and its host candidates only. Calls and file transfers use it for the direct TCP channel, which only connects where host addresses reach (ICE covers NATs), so they never wait for STUN (up to 4 s when UDP to the STUN server is blocked).
func OpenBrowser ¶
OpenBrowser opens the given URL in the default browser.
func OpenSignalStream ¶
func OpenSignalStream(ctx context.Context, agent *rpcclient.Agent, targetAgentID string) (rpcclient.ClientStream, error)
OpenSignalStream opens a bidirectional p2p.v1 signal stream from the local agent to targetAgentID.
func ReceiveDir ¶
ReceiveDir receives a directory tar+gzip bundle and extracts it to outputDir.
func ReceiveFile ¶
ReceiveFile reads a file sent by SendFile and writes it to outputDir. Returns the local path of the written file.
func SendAnswer ¶
func SendAnswer(stream rpcclient.ServerStream, msg *SignalMsg) error
SendAnswer transmits an answer over the provided ServerStream.
func SendDir ¶
func SendDir(conn net.Conn, dirPath string, progress ProgressFunc) error
SendDir sends a directory as a gzip-compressed tar archive.
func SendFile ¶
func SendFile(conn net.Conn, path string, progress ProgressFunc) error
SendFile sends a single file over an authenticated P2P net.Conn.
func SendHangupServer ¶
func SendHangupServer(stream rpcclient.ServerStream, sessionID, reason string)
SendHangupServer sends a hangup message and closes the server stream.
Types ¶
type CallServer ¶
type CallServer struct {
// TURN servers the page adds to its ICE servers beside STUN; nil means
// none. Set before Run; validated with ValidateICEServers by the caller.
TURN []turnwire.ICEServer
// contains filtered or unexported fields
}
func NewCallServer ¶
func NewCallServer(sig FrameConn, role, stunServer string, htmlFunc func() string) (*CallServer, error)
NewCallServer creates a CallServer bound to 127.0.0.1 with a random port. sig carries the signaling frames to the remote peer's CallServer. role is "initiator" or "responder". stunServer is the STUN server URL (e.g. "stun.l.google.com:19302"). htmlFunc returns the HTML page content to serve.
func (*CallServer) Run ¶
func (s *CallServer) Run(ctx context.Context) error
Run starts the HTTP server and blocks until the call ends (hangup, context cancellation, or the signaling ending). The caller should open the browser before or after calling Run.
func (*CallServer) URL ¶
func (s *CallServer) URL() string
URL returns the localhost URL with auth token that should be opened in the browser.
type ConnectOption ¶ added in v0.19.11
type ConnectOption func(*connectConfig)
ConnectOption configures Connect.
func AnnounceConn ¶ added in v0.19.11
func AnnounceConn(stream rpcclient.ClientStream, sessionID string) ConnectOption
AnnounceConn makes Connect, on the dialing side, send the connection it keeps to the peer on the signaling stream (SignalSelect with Conn).
func AwaitConn ¶ added in v0.19.11
func AwaitConn(stream rpcclient.ServerStream) ConnectOption
AwaitConn makes Connect, on the other side, keep the connection the dialing side announced (AnnounceConn), read from the signaling stream. Another signal or the stream's end (a peer that predates SignalSelect) leaves Connect keeping its own pick; SignalRelay makes it return ErrPeerRelay.
type DataPath ¶
type DataPath struct {
Conn net.Conn
Path string // PathHost, PathSrflx, PathTURN or PathRelay
Pair string // selected ICE candidate pair ("srflx -> prflx"); empty on the relay
// Race is the sender's race, one entry per path it raced (the
// direct TCP channel when offered, the data channel, the relay),
// in that order; nil on the receiver, which follows the choice, and
// with forceRelay.
Race []RaceEntry
}
DataPath is the conn a file transfer runs on and how it got there.
func OpenDataPath ¶
func OpenDataPath(ctx context.Context, sc *SignalConn, mux *RelayMux, ice ICEConfig, tcp *TCPDirect, initiator, forceRelay bool) (*DataPath, error)
OpenDataPath races the direct TCP channel (tcp; nil for none) against a WebRTC data channel negotiated over sc, and returns a conn on whichever is ready first (TCP on a LAN, the data channel across NATs); when neither is within RTCWait, the relay stream's data conn. The initiator (the sender) chooses and tells the responder (FrameDataPath), which uses exactly that connection; the losing paths are closed. forceRelay skips both. sc keeps being read until it ends (late candidates), so the relay stream never stalls.
sc must come from E2E.SignalConn: the returned conn is that E2E's Secure conn on the chosen path, so every path carries only ciphertext.
type E2E ¶
type E2E struct {
// contains filtered or unexported fields
}
E2E is one side's end-to-end key exchange for one session.
func NewE2E ¶
NewE2E starts this side's key exchange with a fresh ephemeral key. responder names the responder (the initiator: the agent it calls; the responder: its own name); pair is this end's A2A key, or nil (unpaired).
func (*E2E) Answer ¶
Answer (responder) completes the exchange with the offer's key and fills the answer's key and key confirmation.
func (*E2E) AwaitConfirm ¶
func (e *E2E) AwaitConfirm(stream rpcclient.ServerStream) error
AwaitConfirm (responder) reads the initiator's key confirmation, the message after the answer.
func (*E2E) Complete ¶
Complete derives the session keys from the peer's public key and key id (its offer's or answer's KeyID).
func (*E2E) DirectToken ¶
DirectToken is the direct TCP channel's handshake secret (Connect, TCPDirect): known only to the two ends, never sent. allow:wrapper
func (*E2E) Fingerprint ¶
Fingerprint is the session fingerprint (80 bits) both CLIs print: the same on both machines unless something in the middle replaced the keys.
func (*E2E) FinishOffer ¶
func (e *E2E) FinishOffer(stream rpcclient.ClientStream, answer *SignalMsg) error
FinishOffer (initiator) completes the exchange with the answer and checks its key confirmation, then sends this side's confirmation on stream, or a hangup naming the failure (the responder prints it).
func (*E2E) KeyID ¶ added in v0.21.18
KeyID is this end's A2A key id for its offer or answer ("" the root key, or unpaired). allow:wrapper
func (*E2E) PublicKey ¶
PublicKey is this side's public key for the offer or answer (hex). allow:wrapper
func (*E2E) Secure ¶
Secure wraps a session's data conn (any path: direct TCP, WebRTC data channel, the relay) in AES-256-GCM records: 4-byte big-endian length, then the sealed bytes, nonce = the record's sequence number. A record that does not authenticate (altered, reordered, replayed, or from another key) fails every later Read. The relay can still cut the stream short; transfers check the size and SHA-256 inside it.
func (*E2E) SignalConn ¶
func (e *E2E) SignalConn(first FramePath) *SignalConn
SignalConn starts a SignalConn whose paths (first, and every AddPath) carry sealed frames: the relay sees only their sequence numbers and sizes. OpenDataPath secures the data path with the same E2E.
type Frame ¶
type Frame struct {
Type FrameType `json:"type"`
// File/dir fields
Name string `json:"name,omitempty"`
Size int64 `json:"size,omitempty"`
SHA256 string `json:"sha256,omitempty"`
// Chat
Text string `json:"text,omitempty"`
// Ack / err
Error string `json:"error,omitempty"`
// Seq numbers signaling frames on a SignalConn (1, 2, …); the receiver
// dedupes and orders by it. Unused on a byte-stream transfer.
Seq uint64 `json:"seq,omitempty"`
}
Frame is the on-wire header for every P2P message. It is JSON-encoded, length-prefixed with a 4-byte big-endian uint32.
type FrameConn ¶
CallServer is a local HTTP + WebSocket server that bridges a browser to the remote CLI for WebRTC signaling and text chat.
Architecture:
Browser ←WS→ CallServer ←FrameRTC/FrameChat→ SignalConn ←→ remote peer
Media flows directly via WebRTC (browser-to-browser) once signaling completes. The CallServer only relays signaling and text-chat frames. FrameConn carries a call's signaling frames between the two CLIs (a SignalConn: relay stream, plus the direct TCP channel once it connects).
type FramePath ¶
FramePath is one transport for signaling frames: the relay stream (RelayMux.SignalPath) or the direct TCP channel (TCPPath).
type FrameType ¶
type FrameType string
FrameType identifies the purpose of a framed message.
const ( FrameFile FrameType = "file" // single file transfer (header + body) FrameDir FrameType = "dir" // tar+gzip bundle of a directory FrameChat FrameType = "chat" // UTF-8 text message FrameAck FrameType = "ack" // transfer confirmed by receiver FrameErr FrameType = "err" // error from receiver FrameDone FrameType = "done" // all transfers finished FrameRTC FrameType = "rtc" // WebRTC signaling (SDP/ICE) relayed via Text field FrameRTCReady FrameType = "rtc-ready" // browser connected to local WS, ready for WebRTC FrameDataPath FrameType = "path" // file data path the sender chose (Text: pathWebRTC or pathRelay) )
type ICEConfig ¶
type ICEConfig struct {
STUN string
TURN []turnwire.ICEServer
// contains filtered or unexported fields
}
ICEConfig is what a file transfer's WebRTC data path gathers with, the same ICE servers as a call: the STUN server ("host:port"; "" for none) and the TURN servers (relay-issued credentials or --turn).
type Meta ¶
type Meta struct {
// Op is the operation: "send-file", "send-dir", "chat", or "call". The
// file's name, size and SHA-256 travel inside the encrypted data stream.
Op string `json:"op"`
}
Meta describes the payload to be transferred in a P2P session.
type Pair ¶ added in v0.21.18
type Pair interface {
KeyID() string
Key(peerKeyID string) (key []byte, pairKeyID string, err error)
}
Pair is an end's A2A key for p2p sessions (spec/proverif p2p_e2e.pv). KeyID is its own key id, sent in its offer or answer ("" the owner's root key). Key is the pair key and the pair key id with a peer that presents peerKeyID: the root key between two root holders, else the machine end's key (a root holder derives it); an error refuses the session.
type ProgressFunc ¶
type ProgressFunc func(transferred, total int64)
ProgressFunc is called periodically during a transfer with the number of bytes transferred so far and the total (0 = unknown).
type RaceEntry ¶ added in v0.20.0
type RaceEntry struct {
Path string // PathTCP, RaceWebRTC or PathRelay
Outcome string // RaceWon, RaceLost, RaceFailed or RaceLate
After time.Duration // since the race began: when the path was ready or failed, else when the choice was made
}
RaceEntry is one path's part in the sender's race (DataPath.Race).
type RelayConn ¶
type RelayConn struct {
// contains filtered or unexported fields
}
RelayConn wraps a bidirectional A2A stream as a net.Conn so the existing P2P transfer/chat/call code can use it unchanged. Data is relayed through the server instead of flowing peer-to-peer.
func NewClientRelayConn ¶
func NewClientRelayConn(stream rpcclient.ClientStream) *RelayConn
NewClientRelayConn wraps a ClientStream as a net.Conn for relay.
func NewServerRelayConn ¶
func NewServerRelayConn(stream rpcclient.ServerStream) *RelayConn
NewServerRelayConn wraps a ServerStream as a net.Conn for relay.
func (*RelayConn) Close ¶
Close implements net.Conn. On the client side it ends the stream's send half, as closing a TCP connection sends FIN, so the peer's Read returns io.EOF (AwaitPeerClose).
func (*RelayConn) RemoteAddr ¶
type RelayMux ¶
type RelayMux struct {
// contains filtered or unexported fields
}
RelayMux splits one p2p.v1 relay stream, after the offer/answer, into a signaling path (sequenced Frames for a SignalConn) and a data byte stream (the file transfer's relay fallback). Every stream message starts with its kind byte.
Each path is flow-controlled on its own (signaling protocol 4): a sender sends only what the peer's reader has room for (the path's window) and waits for the reader's credit grants for more, so a slow reader slows only its own path's sender, the demux never waits, and the other path flows. Both ends must speak protocol 4: a peer below it never grants credit.
func NewClientRelayMux ¶
func NewClientRelayMux(stream rpcclient.ClientStream) *RelayMux
NewClientRelayMux starts a mux on the initiator's stream.
func NewServerRelayMux ¶
func NewServerRelayMux(stream rpcclient.ServerStream) *RelayMux
NewServerRelayMux starts a mux on the responder's stream.
func (*RelayMux) DataConn ¶
DataConn is the relay fallback for file data, as a byte stream on the same relay stream (RelayConn semantics: an empty message or the stream's end is io.EOF; Close half-closes the initiator's stream).
func (*RelayMux) SignalPath ¶
SignalPath is the mux's signaling FramePath. Closing it closes the mux.
type SignalConn ¶
type SignalConn struct {
// contains filtered or unexported fields
}
SignalConn carries the signaling frames between the two CLIs (a call's SDP, ICE candidates, chat fallback and hangup; a file transfer's WebRTC negotiation) over one or more paths at once: the relay stream from the first frame, and the direct TCP channel once it connects (AddPath).
Every frame gets the next sequence number and is written on every open path; a path that joins first carries the frames sent before it (the last maxAhead of them), so a frame still in flight on a path that breaks is carried by the newer one (spec/SignalConn.tla NoSilentLoss). The receiver delivers each sequence number once, in order: a copy of a number already delivered is dropped (dedupe by sequence number, never by payload: two identical frames are two frames), and a frame that overtook an earlier one (the direct path is faster than the relay) waits for it. So a path joining or failing mid-call loses and duplicates nothing. A path that fails is dropped; reads end (io.EOF) when the last path has ended.
func NewSignalConn ¶
func NewSignalConn(first FramePath) *SignalConn
NewSignalConn starts a SignalConn of plain frames on its first path (a call's local test harness; sessions between two CLIs use E2E.SignalConn).
func (*SignalConn) AddPath ¶
func (sc *SignalConn) AddPath(p FramePath)
AddPath adds a path: the frames sent so far and later frames are written on it too, and frames read from it are delivered (deduped and in order with the other paths).
func (*SignalConn) ReadFrame ¶
func (sc *SignalConn) ReadFrame() (*Frame, error)
ReadFrame returns the next frame in sequence order; io.EOF once every path has ended or the SignalConn is closed.
func (*SignalConn) WriteFrame ¶
func (sc *SignalConn) WriteFrame(f *Frame) error
WriteFrame numbers f and writes it on every open path. It fails only when no path took it; that ends the SignalConn (its number is spent, never reused or replayed to a later path).
type SignalMsg ¶
type SignalMsg struct {
Type SignalType `json:"type"`
// Session is a UUID that uniquely identifies this P2P session.
Session string `json:"session"`
// Key is the sender's ephemeral X25519 public key (hex; offer and answer).
Key string `json:"key,omitempty"`
// KeyID is the sender's A2A key id (offer and answer; "" the owner's
// root key, or unpaired): the relay's word; a root initiator's Pair
// takes the answer's only for the machine it named, so a wrong one is
// refused or fails the key confirmation (E2E.Complete; spec/proverif
// p2p_e2e.pv Initiator).
KeyID string `json:"key_id,omitempty"`
// Confirm is a key confirmation (answer and SignalConfirm).
Confirm string `json:"confirm,omitempty"`
// Candidates is the list of "host:port" addresses that the peer will
// accept connections on. Both local IPs and the STUN-discovered external
// address are included.
Candidates []string `json:"candidates"`
// Meta describes the intended transfer (file, dir, or chat).
Meta *Meta `json:"meta,omitempty"`
// Error is set in a hangup message to describe the failure reason.
Error string `json:"error,omitempty"`
// Conn is the ConnID of the direct connection the dialing side keeps
// (SignalSelect).
Conn string `json:"conn,omitempty"`
// Proto is the sender's SignalProto (0 from a CLI that predates it).
Proto int `json:"proto,omitempty"`
}
SignalMsg is the JSON envelope exchanged between peers during signaling. Both offer and answer are transmitted as the payload of an rpcclient stream message with message_type "p2p.v1".
func RecvSignal ¶
func RecvSignal(stream rpcclient.ClientStream) (*SignalMsg, error)
RecvSignal reads a single SignalMsg from a ClientStream.
func RecvSignalServer ¶
func RecvSignalServer(stream rpcclient.ServerStream) (*SignalMsg, error)
RecvSignalServer reads a single SignalMsg from a ServerStream.
type SignalType ¶
type SignalType string
SignalType identifies the type of a signaling message exchanged during P2P session negotiation. The vocabulary mirrors WebRTC (offer / answer / hangup).
const ( // SignalOffer is sent by the initiating peer. It carries connection // candidates and the initiator's end-to-end public key. SignalOffer SignalType = "offer" // SignalAnswer is sent by the responding peer. It carries that peer's // connection candidates, public key and key confirmation. SignalAnswer SignalType = "answer" // SignalConfirm is the initiator's key confirmation, the message after // the answer (E2E.FinishOffer). SignalConfirm SignalType = "confirm" // SignalSelect is the dialing side's choice among the direct // connections that authenticated (Conn: its ConnID), sent after the // answer (AnnounceConn). A peer that predates it never sends it, and its // peer then keeps its own pick. SignalSelect SignalType = "select" // SignalHangup terminates signaling before a direct connection is formed. SignalHangup SignalType = "hangup" // SignalProto is the signaling protocol a CLI speaks, in every offer and // answer. From 2, after the offer/answer the relay stream is a RelayMux: // sequenced signaling frames from the first message (a call starts at // once; the direct TCP channel joins later), and file data on a WebRTC // data channel (OpenDataPath). From 3, every session is end-to-end // encrypted (E2E): the offer and answer carry X25519 public keys instead // of a session token, the initiator confirms the keys, and the signaling // frames and the data stream are sealed on every path. From 4, each // RelayMux path is flow-controlled on its own: a reader grants credit as // it reads, so a slow data reader slows only the data sender and // signaling always flows. A peer below 4 is refused (a peer below 3 is // never served in the clear; one at 3 never grants credit, so its data // sender would wait forever). An older peer omits it (0). SignalProto = 4 )
const ( // SignalRelay is sent by the initiator when direct P2P fails, instructing // both sides to relay data through the server's A2A channel. SignalRelay SignalType = "relay" )
type TCPDirect ¶
type TCPDirect struct {
Listener *net.TCPListener
Candidates []string
Token []byte
SessionID string
}
TCPDirect is the direct TCP channel a file transfer races against WebRTC: this side's listener (from ListenHostCandidates), the peer's candidates, and the session's token and ID. nil: no race (--force-relay, or the peer sent no candidates).