UtPex

object UtPex

BEP-11 "Peer Exchange (PEX)" (ut_pex). This is the pure message codec, ported from libtorrent's ut_pex.cpp (src/ut_pex.cpp, ut_pex_plugin::tick builds the message and ut_pex_peer_plugin::on_extended parses it).

PEX lets connected peers gossip the addresses of other peers, so a swarm can grow without everyone hammering the tracker. A PEX message is the bencoded payload of a BEP-10 extended message (id 20), carried under the extension id the remote peer advertised for ut_pex.

Wire format

A single bencode dict with up to six string keys:

keycontents
addedcompact IPv4 peers, 6 bytes each: 4-byte IP + 2-byte port
added.fone PexFlags byte per peer in added, same order
droppedcompact IPv4 peers that left since the last message
added6compact IPv6 peers, 18 bytes each: 16-byte IP + 2-byte port
added6.fone flags byte per peer in added6
dropped6compact IPv6 peers that left

All integers (IPs and ports) are network byte order (big-endian); a port like 6881 is the two bytes 0x1a 0xe1. There are no flags for dropped peers.

libtorrent only parses added/added.f when their lengths agree (added.f.length == added.length / 6); a mismatch means the whole added list is ignored. parse reproduces that. The internal pex_lt_v2 flag (bit 0x10) is masked off on receive, because it must not be trusted from the wire. See PexFlags.RECEIVE_MASK.

This class is the codec only; the once-a-minute scheduling, the dedup against previously-sent peers, and the add_peer side effects live in the peer-plugin layer.

Types

Link copied to clipboard
class Builder(val maxAdded: Int = MAX_ADDED_ENTRIES)

Stateful per-connection PEX differ, the port of ut_pex_plugin's m_old_peers bookkeeping. The session holds one Builder per peer connection (the peer we gossip to) and calls tick roughly once a minute with the current swarm membership; the builder emits only the delta versus what it sent last time:

Link copied to clipboard
data class BuildResult(val message: UtPex.Message, val addedCount: Int, val droppedCount: Int)

The result of a Builder.tick: the Message to send plus the set of (ip,port) keys that were sent as added, so the caller can record what the remote now knows. Builder already tracks this internally, but exposing it lets the session log or audit.

Link copied to clipboard
data class Message(val added: List<UtPex.PexPeer> = emptyList(), val dropped: List<UtPex.PexPeer> = emptyList())

A fully parsed PEX message: the four logical lists libtorrent reads out of the dict. IPv4 and IPv6 are merged here for convenience, because each PexPeer knows its own family via PexPeer.isV4. encode still writes them into the correct added and added6 keys.

Link copied to clipboard
object PexFlags

The per-peer PEX flag bits, the port of the pex_* constants in libtorrent/extensions/ut_pex.hpp (referenced from ut_pex.cpp). They travel in the added.f and added6.f byte strings.

Link copied to clipboard
data class PexPeer(val ip: ByteArray, val port: Int, val flags: Int = 0)

One peer advertised in (or dropped from) a PEX message.

Properties

Link copied to clipboard
const val IPV4_ENTRY_SIZE: Int = 6

Bytes per compact IPv4 peer entry: 4-byte address + 2-byte port.

Link copied to clipboard
const val IPV6_ENTRY_SIZE: Int = 18

Bytes per compact IPv6 peer entry: 16-byte address + 2-byte port.

Link copied to clipboard
const val MAX_ADDED_ENTRIES: Int = 50

The default cap on how many added peers a single PEX message carries, so a packet stays small. libtorrent uses max_peer_entries = 100 (ut_pex.cpp:61); this port caps at 50 per the session policy. dropped peers are not counted against this limit. Only newly-added ones count, matching if (num_added >= max_peer_entries) break; in ut_pex_plugin::tick.

Functions

Link copied to clipboard
fun encode(added: List<UtPex.PexPeer>, dropped: List<UtPex.PexPeer> = emptyList(), includeEmpty: Boolean = true, maxAdded: Int = MAX_ADDED_ENTRIES): ByteArray

Encode a PEX message from added and dropped peer lists. Each peer is routed to its IPv4 or IPv6 key by address length; flags are written for added peers only (added.f and added6.f), never for dropped ones. This matches ut_pex_plugin::tick.

Link copied to clipboard
fun parse(payload: ByteArray): UtPex.Message?

Parse a PEX message payload (the bencoded body of the extended message, without the wire framing).

Link copied to clipboard
fun peerFromIpv4(host: String, port: Int, flags: Int = 0): UtPex.PexPeer?

Build a PexPeer from a literal IPv4 dotted-quad host (e.g. "1.2.3.4") and a port. Returns null if host is not four decimal octets. IPv6 literals are not parsed here; construct those from raw bytes. That keeps this codec free of a full address parser (the peer layer's PeerAddress does that).