Skip to content

v1.2.27

Choose a tag to compare

@VuzzyM VuzzyM released this 02 May 13:42
Adds bep_0014 (local peer discovery)

* Add BEP-14 Local Peer Discovery (LPD)

Implements BEP-14 Local Peer Discovery via UDP multicast, allowing
clients to discover peers on the local network without a tracker.

- Adds LPDServer with IPv4 (239.192.152.143:6771) and optional IPv6
  (ff15::efc0:988f:6771) multicast support
- Announces active torrents periodically and on new torrent add
- Discovered peers are added to matching torrents and broadcast to all
  active torrents (LPD is the only source of local IPs)
- Fixes self-message detection: compare source IP against publisher's
  local address rather than broken string contains check
- Configurable via EnableLocalServiceDiscovery and
  LocalServiceDiscoveryConfig (interface selection, IPv6 toggle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add BEP-14 tests before refactor

Unit tests:
- TestLPDPeerDeduplication: same peer address added twice -> one entry
- TestLPDPeerExpiry: stale peers removed after 2*bep14LongTimeout

Integration tests:
- TestPeerAddedToAllTorrents: peer broadcast to all active torrents,
  not only the one matching the announced infohash
- TestNewTorrentGetsExistingPeers: torrent added after discovery
  immediately receives known peers via lpdPeers() in AddTorrentSpec
- TestReceiverMalformedMessages: wrong method / missing Infohash /
  missing Port header all silently dropped with no peers added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Refactor BEP-14: decouple LPD goroutines from *Client

- Define lpdClient interface so receiver/announcer goroutines never
  touch *Client internals directly, eliminating interleaved lock ordering
- Replace missinggo.Event stop/force signals with context.Context and a
  buffered chan struct{}, removing the missinggo dependency from bep14.go
- Change peers map from map[int64]string (timestamp key, address value)
  to map[string]time.Time (address key) for O(1) deduplication and
  straightforward expiry in refresh()
- Fix double-dial bug in lpdConnNew: the unconditional net.DialUDP call
  before the interface-specific branch leaked a connection on every start
- Move OnLPDAnnouncement logic into (*Client).OnLPDAnnouncement and
  TorrentInfohashesAndPort into (*Client).TorrentInfohashesAndPort,
  keeping bep14.go free of metainfo/torrent-map access
- lpdForce uses non-blocking channel send; no mutex required

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add torrent lpd subcommand for BEP-14 Local Peer Discovery

Two modes, no full client required:

  torrent lpd listen [--ip6]
    Joins the multicast group and prints every incoming BT-SEARCH
    announcement (timestamp, source IP, peer port, infohash(es))
    to stdout until Ctrl-C.

  torrent lpd announce --port PORT [--ip6] INFOHASH [...]
    Sends a single BT-SEARCH packet for the given infohash hex
    strings and exits immediately.

Both modes optionally extend to the IPv6 group via --ip6.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Clean up BEP-14 branch for upstream PR

Drop leftover debug log.Println in torrent.go, apply gofmt to config.go
and torrent.go, and revert the go.work.sum churn so the rebase onto
upstream/master can apply cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Extract buildAnnouncePacket helper and add tests

Pull the packet-building loop out of lpdConn.announcer into a pure
buildAnnouncePacket function so it can be unit-tested directly. The
announcer now just feeds the helper the current queue and resumes from
the returned index on the next tick.

Tests added:
- TestBuildAnnouncePacketSingle: small queue fits in one packet and
  completes a rotation.
- TestBuildAnnouncePacketFragments: a 40-infohash queue is split across
  multiple packets, every infohash appears exactly once, and rotation is
  reported only after the final packet.
- TestBuildAnnouncePacketEmptyQueue: empty queue yields nil packet and a
  trivial rotation.
- TestMultiInfohash now asserts the parsed infohash list and port.

Also handles a shutdown race in announcer where a Write on the closed
multicast conn would log a spurious error, and drops a stale //hiiiii
comment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Revert stray whitespace change in testutil/greeting.go

Unrelated to BEP-14; can be squashed into the prior cleanup commit
during rebase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* whitespace and discovery info to peer.go

* Address PR #998 feedback: rename, MTU comment, attribution

- Rename LPDServer to lpdServer (package-internal, unexport).
- Add comment explaining the 1400-byte announce packet cap (typical
  Ethernet MTU).
- Credit the original implementation at
  gitlab.com/axet/libtorrent/lpd.go (LGPL-3).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* BEP-14: make LocalServiceDiscovery a *Config (nil = disabled)

Replace the EnableLocalServiceDiscovery bool + LocalServiceDiscoveryConfig
struct value pair with a single LocalServiceDiscovery *LocalServiceDiscoveryConfig
field. A nil pointer disables LPD; any non-nil value enables it with those
options. Addresses the config.go:49 feedback from PR #998: one source of
truth, no need for callers or internal code to track an enabled flag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* BEP-14: take explicit ownership of the multicast packet conn

Replace setMulticastInterface's silent if/if dispatch with a switch-based
newMcPacketConn factory that returns an error on unsupported networks.
The returned *ipv4.PacketConn or *ipv6.PacketConn is stored on lpdConn
via a narrow mcPacketConn interface instead of being dropped on the floor
for GC to pick up. Addresses anacrolix's bep14.go:73 feedback from PR #998.

Also fixes a tab-before-comment nit on PeerSourceLPD to keep gofmt clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* BEP-14: make discovery tests independent of multicast delivery

TestDiscovery and related tests opened real multicast sockets and waited
for cross-client loopback, which hangs on GitHub's macOS runners (EPIPE
on writes, nothing delivered on reads) until the 10-minute test
timeout. Extract handleAnnouncePacket from receiver() so tests can drive
the receive path directly with crafted BT-SEARCH packets, and give
waitForPeers a 30s deadline via a timer-driven Broadcast so a missed
wakeup fails fast instead of hanging the whole suite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* BEP-14: add MPL-2.0 headers to files derived from axet/libtorrent lpd.go

Alexey Kuznetsov granted permission to use lpd.go under MPL-2.0,
matching this project's license. Add the standard MPL-2.0 short
header to bep14.go, bep14_test.go, and cmd/torrent/lpd.go, with an
attribution note recording the relicensing grant. Drops the earlier
LGPL-3 note, which is superseded by the grant.

---------

Co-authored-by: Phil Cummins <philip.cummins@bsc.es>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Matt Joiner <anacrolix@gmail.com>
Co-authored-by: Vuzzy