protocol

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package protocol defines constants for uTP (Micro Transport Protocol) per BEP 29

Package protocol implements uTP (Micro Transport Protocol) per BEP 29 This is a complete implementation of uTP with full congestion control, packet loss handling, selective ACKs, and retransmission logic.

Package protocol implements uTP connection state management per BEP 29

Package protocol implements uTP (Micro Transport Protocol) per BEP 29.

Index

Constants

View Source
const (
	ST_DATA  = 0 // Regular data packet
	ST_FIN   = 1 // Finalize connection
	ST_STATE = 2 // State packet (ACK only)
	ST_RESET = 3 // Reset connection
	ST_SYN   = 4 // Connect SYN
)

Packet types per BEP 29

View Source
const (
	EXT_NO_EXTENSION  = 0 // No extensions
	EXT_SELECTIVE_ACK = 1 // Selective ACK extension
)

Extension types per BEP 29

View Source
const (
	CS_IDLE      = iota // Connection idle
	CS_SYN_SENT         // SYN sent, waiting for SYN-ACK
	CS_SYN_RECV         // SYN received, sent SYN-ACK
	CS_CONNECTED        // Connected
	CS_FIN_SENT         // FIN sent, waiting for FIN-ACK
	CS_CLOSED           // Connection closed
)

Connection states per BEP 29

View Source
const (
	// Congestion control targets
	CCONTROL_TARGET                   = 100 * time.Millisecond // Target delay
	MAX_CWND_INCREASE_PACKETS_PER_RTT = 3000                   // Max window increase per RTT

	// Packet sizes
	MIN_WINDOW_SIZE = 150  // Minimum packet size
	MAX_PACKET_SIZE = 1400 // Maximum data per packet

	// Timeouts
	INIT_TIMEOUT = 1000 * time.Millisecond // Initial timeout
	MIN_TIMEOUT  = 500 * time.Millisecond  // Minimum timeout

	// Protocol version
	UTP_VERSION = 1
)

Constants per BEP 29

View Source
const (
	MetadataRequest uint8 = 0
	MetadataData    uint8 = 1
	MetadataReject  uint8 = 2
)

Metadata message types (BEP 9)

View Source
const (
	MsgChoke         uint8 = 0  // BEP 3: choke — peer will not serve requests
	MsgUnchoke       uint8 = 1  // BEP 3: unchoke — peer will serve requests
	MsgInterested    uint8 = 2  // BEP 3: interested — wants pieces the peer has
	MsgNotInterested uint8 = 3  // BEP 3: not interested — no longer wants pieces
	MsgHave          uint8 = 4  // BEP 3: have <piece index> — completed & verified a piece
	MsgBitfield      uint8 = 5  // BEP 3: bitfield — sent as first message after handshake
	MsgRequest       uint8 = 6  // BEP 3: request <index><begin><length> — ask for a block
	MsgPiece         uint8 = 7  // BEP 3: piece <index><begin><block> — deliver a block
	MsgCancel        uint8 = 8  // BEP 3: cancel <index><begin><length> — cancel a pending request
	MsgPort          uint8 = 9  // BEP 5: port — advertise DHT listener port
	MsgSuggest       uint8 = 13 // BEP 6: suggest piece
	MsgHaveAll       uint8 = 14 // BEP 6: have all pieces
	MsgHaveNone      uint8 = 15 // BEP 6: have no pieces
	MsgReject        uint8 = 16 // BEP 6: reject request
	MsgAllowedFast   uint8 = 17 // BEP 6: allowed fast piece
	MsgExtended      uint8 = 20 // BEP 10: extension protocol message
)

BEP 3 §Peer Messages — message IDs for the peer wire protocol. https://www.bittorrent.org/beps/bep_0003.html

View Source
const (
	ProtocolString = "BitTorrent protocol" // BEP 3: pstr = "BitTorrent protocol"
	HandshakeLen   = 68                    // BEP 3: 1 (pstrlen) + 19 (pstr) + 8 (reserved) + 20 (info_hash) + 20 (peer_id)
)

BEP 3 §Handshake — protocol constants.

View Source
const (
	ExtHandshake uint8 = 0 // BEP 10: extension handshake (ext_id=0, always)
)

BEP 10 §Extension Messages — extension message sub-IDs.

View Source
const (
	ExtensionBit = 0x100000 // BEP 10: bit 20 from right → reserved[5] & 0x10
)

BEP 10 — extension bits in the 8-byte handshake reserved field.

View Source
const MetadataBlockSize = 16384

MetadataBlockSize is the standard block size for metadata exchange (16KB)

View Source
const OurMetadataExtID uint8 = 1

OurMetadataExtID is the extension ID we announce for ut_metadata Peers send responses TO US using this ID

Variables

This section is empty.

Functions

func AllowedFastSet

func AllowedFastSet(k int, numPieces uint32, infoHash [20]byte, ip net.IP) []uint32

AllowedFastSet computes the canonical allowed fast set per BEP 6.

Algorithm:

x = (ip & 0xFFFFFF00) ++ infohash   (24 bytes)
while |a| < k:
    x = SHA1(x)
    for i in 0..4 and |a| < k:
        index = BigEndian(x[i*4 : i*4+4]) % numPieces
        if index not in a: add to a

func ClearPiece

func ClearPiece(bitfield []byte, pieceIndex int)

ClearPiece clears a piece from the bitfield (BEP 3).

func CountPieces

func CountPieces(bitfield []byte) int

CountPieces counts the number of pieces set in the bitfield

func DefaultPeerID

func DefaultPeerID() (dht.Key, error)

DefaultPeerID generates a peer ID with default client identification

func DialContext

func DialContext(ctx context.Context, network, addr string) (net.Conn, error)

DialContext attempts to establish a uTP connection (BEP 29 handshake)

func FetchMetadata

func FetchMetadata(peer net.Addr, infoHash, peerID dht.Key, timeout time.Duration, logger *slog.Logger) (*torrent.MetaInfo, error)

FetchMetadata connects to a peer and fetches metadata for a torrent

func GeneratePeerID

func GeneratePeerID(clientID string, version string) (dht.Key, error)

GeneratePeerID creates a unique peer ID following Azureus-style convention Format: -XX0000-<12 random chars>

func HasPiece

func HasPiece(bitfield []byte, pieceIndex int) bool

HasPiece checks if a piece is present in the bitfield (BEP 3).

func NewBitfield

func NewBitfield(numPieces int) []byte

NewBitfield creates a bitfield for the given number of pieces (BEP 3).

func ParseExtendedMessage

func ParseExtendedMessage(msg *Message) (extID uint8, payload []byte, err error)

ParseExtendedMessage parses an extended message

func SetPiece

func SetPiece(bitfield []byte, pieceIndex int)

SetPiece sets a piece as present in the bitfield. BEP 3: high bit of first byte = piece 0 (bit ordering).

func WriteMessage

func WriteMessage(w io.Writer, msg *Message) error

WriteMessage writes a message to a connection

Types

type AllowedFastMessage

type AllowedFastMessage struct {
	PieceIndex uint32
}

AllowedFastMessage declares a piece the choked peer may still request (BEP 6).

func ParseAllowedFastMessage

func ParseAllowedFastMessage(msg *Message) (*AllowedFastMessage, error)

ParseAllowedFastMessage parses a BEP 6 Allowed Fast message.

type BitfieldMessage

type BitfieldMessage struct {
	Bitfield []byte // Length must be (num_pieces + 7) / 8 bytes
}

BitfieldMessage represents which pieces the peer has. BEP 3: sent as the very first message after handshake (if peer has any pieces). Each bit represents a piece index (1 = have, 0 = don't have). Bit ordering: high bit of first byte = piece 0, next bit = piece 1, etc.

func ParseBitfieldMessage

func ParseBitfieldMessage(msg *Message) (*BitfieldMessage, error)

ParseBitfieldMessage parses a bitfield message

type CancelMessage

type CancelMessage struct {
	Index  uint32 // BEP 3: piece index (matches original request)
	Begin  uint32 // BEP 3: byte offset (matches original request)
	Length uint32 // BEP 3: block size (matches original request)
}

CancelMessage cancels a pending request. BEP 3: cancel <index><begin><length> — identical payload format to request. Used during endgame mode when a block arrives from another peer.

func ParseCancelMessage

func ParseCancelMessage(msg *Message) (*CancelMessage, error)

ParseCancelMessage parses a cancel message

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn represents a uTP connection. It operates in two modes:

  • dialer mode: owns its own UDP socket (recvChan == nil)
  • acceptor mode: shares the Listener's socket (recvChan != nil)

func (*Conn) Close

func (c *Conn) Close() error

Close implements net.Conn (BEP 29: send ST_FIN).

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() net.Addr

LocalAddr implements net.Conn

func (*Conn) Read

func (c *Conn) Read(b []byte) (int, error)

Read implements net.Conn.

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() net.Addr

RemoteAddr implements net.Conn

func (*Conn) SetDeadline

func (c *Conn) SetDeadline(t time.Time) error

SetDeadline implements net.Conn

func (*Conn) SetReadDeadline

func (c *Conn) SetReadDeadline(t time.Time) error

SetReadDeadline implements net.Conn

func (*Conn) SetWriteDeadline

func (c *Conn) SetWriteDeadline(t time.Time) error

SetWriteDeadline implements net.Conn

func (*Conn) Write

func (c *Conn) Write(b []byte) (n int, err error)

Write implements net.Conn NOTE: This is a simplified implementation. Full BEP 29 compliance requires: - Proper sequence number management - Retransmission on packet loss - Window-based flow control - Packet pacing

type Extension

type Extension struct {
	Type  byte
	Bytes []byte
}

Extension represents a uTP extension header

type ExtensionHandshake

type ExtensionHandshake struct {
	M            map[string]int `bencode:"m"`                       // BEP 10: dict of extension name → local message ID
	P            int            `bencode:"p,omitempty"`             // BEP 10: local TCP listen port
	V            string         `bencode:"v,omitempty"`             // BEP 10: client name and version
	YourIP       string         `bencode:"yourip,omitempty"`        // BEP 10: compact IP of the remote peer
	IPv6         string         `bencode:"ipv6,omitempty"`          // BEP 10: compact IPv6 address
	IPv4         string         `bencode:"ipv4,omitempty"`          // BEP 10: compact IPv4 address
	Reqq         int            `bencode:"reqq,omitempty"`          // BEP 10: max outstanding request hint
	MetadataSize int            `bencode:"metadata_size,omitempty"` // BEP 9: total size of info dict in bytes
	UploadOnly   int            `bencode:"upload_only,omitempty"`   // BEP 21: 1 = partial seed (not downloading)
}

ExtensionHandshake represents the BEP 10 extension handshake. Sent as extended message with ext_id=0 immediately after the BEP 3 handshake.

func NewExtensionHandshake

func NewExtensionHandshake(port int) *ExtensionHandshake

NewExtensionHandshake creates a minimal extension handshake with metadata support. BEP 10: the "m" dict declares the extension IDs THIS peer will use when SENDING. BEP 9: "ut_metadata" enables metadata exchange for magnet link resolution.

func ParseExtensionHandshake

func ParseExtensionHandshake(data []byte) (*ExtensionHandshake, error)

ParseExtensionHandshake parses an extension handshake from bencode

func (*ExtensionHandshake) GetMetadataExtID

func (e *ExtensionHandshake) GetMetadataExtID() (int, bool)

GetMetadataExtID returns the peer's declared extension ID for ut_metadata (BEP 9). This is the ID the PEER will use when sending ut_metadata messages to us.

func (*ExtensionHandshake) HasMetadataSupport

func (e *ExtensionHandshake) HasMetadataSupport() bool

HasMetadataSupport checks if peer supports metadata exchange (BEP 9).

func (*ExtensionHandshake) Serialize

func (e *ExtensionHandshake) Serialize() ([]byte, error)

Serialize encodes the extension handshake to bencode

type Handshake

type Handshake struct {
	Pstr     string
	Reserved [8]byte
	InfoHash dht.Key
	PeerID   dht.Key
}

Handshake represents the BitTorrent handshake

func NewHandshake

func NewHandshake(infoHash, peerID dht.Key) *Handshake

NewHandshake creates a handshake with extension protocol support. BEP 3 §Handshake: pstrlen(1) + pstr(19) + reserved(8) + info_hash(20) + peer_id(20) = 68 bytes.

func ReadHandshake

func ReadHandshake(r io.Reader) (*Handshake, error)

ReadHandshake reads a handshake from a connection

func (*Handshake) Serialize

func (h *Handshake) Serialize() []byte

Serialize converts the handshake to wire format

func (*Handshake) SupportsExtensions

func (h *Handshake) SupportsExtensions() bool

SupportsExtensions checks if the peer supports extension protocol (BEP 10).

func (*Handshake) SupportsFast

func (h *Handshake) SupportsFast() bool

SupportsFast checks if the peer supports BEP 6 Fast Extension.

type HaveMessage

type HaveMessage struct {
	PieceIndex uint32 // BEP 3: zero-based piece index
}

HaveMessage announces that the peer completed and verified a piece. BEP 3: have <piece index> — payload is a single 4-byte big-endian uint32.

func ParseHaveMessage

func ParseHaveMessage(msg *Message) (*HaveMessage, error)

ParseHaveMessage parses a have message

type Header struct {
	Type          uint8       // 4 bits: packet type
	Version       uint8       // 4 bits: protocol version (must be 1)
	Extension     uint8       // 8 bits: extension type
	ConnectionID  uint16      // 16 bits: connection identifier
	Timestamp     uint32      // 32 bits: timestamp in microseconds
	TimestampDiff uint32      // 32 bits: timestamp difference
	WndSize       uint32      // 32 bits: advertised window size
	SeqNr         uint16      // 16 bits: sequence number
	AckNr         uint16      // 16 bits: acknowledgment number
	Extensions    []Extension // Linked list of extensions
}

Header represents uTP packet header per BEP 29 (20 bytes + extensions)

func NewHeader

func NewHeader(packetType uint8, connID uint16) *Header

NewHeader creates a new uTP header

func (*Header) AddSelectiveAck

func (h *Header) AddSelectiveAck(ackBitmask []byte)

AddSelectiveAck adds selective ACK extension to header

func (*Header) Marshal

func (h *Header) Marshal() ([]byte, error)

Marshal serializes header to bytes

func (*Header) ParseSelectiveAck

func (h *Header) ParseSelectiveAck() (*SelectiveAck, error)

ParseSelectiveAck parses selective ACK from extensions

func (*Header) String

func (h *Header) String() string

String returns string representation of header for debugging

func (*Header) Unmarshal

func (h *Header) Unmarshal(buf []byte) error

Unmarshal deserializes header from bytes

type Listener

type Listener struct {
	// contains filtered or unexported fields
}

Listener accepts incoming uTP connections on a shared UDP port.

func Listen

func Listen(ctx context.Context, addr string) (*Listener, error)

Listen creates a uTP listener on the given address (e.g. ":6881").

func (*Listener) Accept

func (l *Listener) Accept() (net.Conn, error)

Accept blocks until a new uTP connection is established or ctx is done.

func (*Listener) Addr

func (l *Listener) Addr() net.Addr

Addr returns the listener's network address.

func (*Listener) Close

func (l *Listener) Close() error

Close stops the listener and all active connections.

type Message

type Message struct {
	ID      uint8
	Payload []byte
}

Message represents a BitTorrent protocol message

func NewAllowedFastMessage

func NewAllowedFastMessage(pieceIndex uint32) *Message

NewAllowedFastMessage creates an Allowed Fast message (BEP 6: msg ID 17, 4-byte index).

func NewBitfieldMessage

func NewBitfieldMessage(bitfield []byte) *Message

NewBitfieldMessage creates a bitfield message

func NewCancelMessage

func NewCancelMessage(index, begin, length uint32) *Message

NewCancelMessage creates a cancel message

func NewChokeMessage

func NewChokeMessage() *Message

NewChokeMessage creates a choke message

func NewExtendedMessage

func NewExtendedMessage(extID uint8, payload []byte) *Message

NewExtendedMessage creates an extended protocol message

func NewHaveAllMessage

func NewHaveAllMessage() *Message

NewHaveAllMessage creates a HaveAll message (BEP 6: msg ID 14, no payload).

func NewHaveMessage

func NewHaveMessage(pieceIndex uint32) *Message

NewHaveMessage creates a have message

func NewHaveNoneMessage

func NewHaveNoneMessage() *Message

NewHaveNoneMessage creates a HaveNone message (BEP 6: msg ID 15, no payload).

func NewInterestedMessage

func NewInterestedMessage() *Message

NewInterestedMessage creates an interested message

func NewNotInterestedMessage

func NewNotInterestedMessage() *Message

NewNotInterestedMessage creates a not interested message

func NewPieceMessage

func NewPieceMessage(index, begin uint32, block []byte) *Message

NewPieceMessage creates a piece message

func NewRejectMessage

func NewRejectMessage(index, begin, length uint32) *Message

NewRejectMessage creates a Reject Request message (BEP 6: msg ID 16, 12-byte payload).

func NewRequestMessage

func NewRequestMessage(index, begin, length uint32) *Message

NewRequestMessage creates a request message

func NewSuggestMessage

func NewSuggestMessage(pieceIndex uint32) *Message

NewSuggestMessage creates a Suggest Piece message (BEP 6: msg ID 13, 4-byte index).

func NewUnchokeMessage

func NewUnchokeMessage() *Message

NewUnchokeMessage creates an unchoke message

func ReadMessage

func ReadMessage(r io.Reader, timeout time.Duration) (*Message, error)

ReadMessage reads a message from a connection

type MetadataExchanger

type MetadataExchanger struct {
	// contains filtered or unexported fields
}

MetadataExchanger handles metadata exchange with a peer

func NewMetadataExchanger

func NewMetadataExchanger(conn net.Conn, infoHash, peerID dht.Key, logger *slog.Logger) *MetadataExchanger

NewMetadataExchanger creates a new metadata exchanger

func (*MetadataExchanger) PerformHandshake

func (m *MetadataExchanger) PerformHandshake(port int) error

PerformHandshake performs the BitTorrent and extension handshakes

func (*MetadataExchanger) RequestMetadata

func (m *MetadataExchanger) RequestMetadata(timeout time.Duration) (*torrent.MetaInfo, error)

RequestMetadata requests all metadata blocks from the peer

type MetadataMessage

type MetadataMessage struct {
	MsgType   uint8 `bencode:"msg_type"`
	Piece     int   `bencode:"piece"`
	TotalSize int   `bencode:"total_size,omitempty"`
}

MetadataMessage represents a ut_metadata message

type PieceMessage

type PieceMessage struct {
	Index uint32 // BEP 3: zero-based piece index
	Begin uint32 // BEP 3: byte offset within the piece
	Block []byte // BEP 3: raw piece data for this block
}

PieceMessage delivers a block of a piece. BEP 3: piece <index><begin><block> — index and begin are 4-byte big-endian uint32.

func ParsePieceMessage

func ParsePieceMessage(msg *Message) (*PieceMessage, error)

ParsePieceMessage parses a piece message

type RejectMessage

type RejectMessage struct {
	Index  uint32
	Begin  uint32
	Length uint32
}

RejectMessage rejects a previously received request (BEP 6).

func ParseRejectMessage

func ParseRejectMessage(msg *Message) (*RejectMessage, error)

ParseRejectMessage parses a BEP 6 Reject Request message.

type RequestMessage

type RequestMessage struct {
	Index  uint32 // BEP 3: zero-based piece index
	Begin  uint32 // BEP 3: byte offset within the piece
	Length uint32 // BEP 3: block size (typically 16384 bytes)
}

RequestMessage requests a block of a piece. BEP 3: request <index><begin><length> — all fields are 4-byte big-endian uint32. Length is typically 2^14 (16384 = 16 KiB) except for the last block in a piece.

func ParseRequestMessage

func ParseRequestMessage(msg *Message) (*RequestMessage, error)

ParseRequestMessage parses a request message

type SelectiveAck

type SelectiveAck struct {
	Bitmask []byte // Bitmask of received packets
}

SelectiveAck represents selective ACK extension

type State

type State struct {
	// contains filtered or unexported fields
}

State tracks connection state and all per-connection data

func NewState

func NewState() *State

NewState creates a new uTP connection state

func (*State) AddOutstandingPacket

func (s *State) AddOutstandingPacket(seqNr uint16, data []byte)

AddOutstandingPacket tracks a sent packet

func (*State) AdjustWindow

func (s *State) AdjustWindow(ourDelay uint32)

AdjustWindow performs delay-based congestion control per BEP 29

func (*State) CanSend

func (s *State) CanSend(packetSize uint32) bool

CanSend checks if we can send a packet given window limits

func (*State) CheckTimeout

func (s *State) CheckTimeout() []uint16

CheckTimeout checks if any outstanding packets have timed out (BEP 29) Returns list of packets to retransmit

func (*State) DetectPacketLossViaSeqGap

func (s *State) DetectPacketLossViaSeqGap(ackNr uint16, selectiveAcks []byte) []uint16

DetectPacketLossViaSeqGap detects packet loss via sequence number gaps (BEP 29) If 3+ packets are ACKed past the oldest unacked packet, it's assumed lost

func (*State) GetConnectionIDs

func (s *State) GetConnectionIDs() (recvID, sendID uint16)

GetConnectionIDs returns the connection IDs for this connection

func (*State) GetOutstandingPacket

func (s *State) GetOutstandingPacket(seqNr uint16) *outstandingPacket

GetOutstandingPacket retrieves a packet for retransmission

func (*State) GetRTTInfo

func (s *State) GetRTTInfo() (rtt, rttVar int64, timeout time.Duration)

GetRTTInfo returns RTT information for debugging

func (*State) GetState

func (s *State) GetState() int

GetState returns current connection state

func (*State) GetWindowInfo

func (s *State) GetWindowInfo() (maxWindow, curWindow, wndSize uint32)

GetWindowInfo returns current window information for debugging

func (*State) IncrementSeqNr

func (s *State) IncrementSeqNr() uint16

IncrementSeqNr increments and returns next sequence number

func (*State) MarkPacketResent

func (s *State) MarkPacketResent(seqNr uint16)

MarkPacketResent updates the sent time for a retransmitted packet

func (*State) OnPacketLoss

func (s *State) OnPacketLoss()

OnPacketLoss handles packet loss per BEP 29: multiply max_window by 0.5

func (*State) ProcessAck

func (s *State) ProcessAck(ackNr uint16) []uint16

ProcessAck processes received ACK and detects duplicate ACKs (BEP 29: packet loss section) Returns list of packets to retransmit if packet loss detected

func (*State) ProcessSelectiveAck

func (s *State) ProcessSelectiveAck(selectiveAcks []byte)

ProcessSelectiveAck processes selective ACK extension

func (*State) RemoveOutstandingPacket

func (s *State) RemoveOutstandingPacket(seqNr uint16)

RemoveOutstandingPacket removes an ACKed packet

func (*State) ResetTimeout

func (s *State) ResetTimeout()

ResetTimeout resets timeout counter (BEP 29)

func (*State) SetConnectionIDs

func (s *State) SetConnectionIDs(recvID, sendID uint16)

SetConnectionIDs sets the connection IDs during handshake

func (*State) SetState

func (s *State) SetState(state int)

SetState updates connection state

func (*State) UpdateBaseDelay

func (s *State) UpdateBaseDelay(delay uint32)

UpdateBaseDelay updates sliding minimum delay (BEP 29: base_delay)

func (*State) UpdateDelayTracking

func (s *State) UpdateDelayTracking(timestamp uint32)

UpdateDelayTracking updates delay measurements for congestion control

func (*State) UpdateRTT

func (s *State) UpdateRTT(packetRTT int64)

UpdateRTT updates RTT and timeout per BEP 29 section "timeouts"

type SuggestMessage

type SuggestMessage struct {
	PieceIndex uint32
}

SuggestMessage suggests a piece for the peer to download (BEP 6).

func ParseSuggestMessage

func ParseSuggestMessage(msg *Message) (*SuggestMessage, error)

ParseSuggestMessage parses a BEP 6 Suggest Piece message.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL