Skip to content

Commit

Permalink
Start implementing user BEP 10 extension support
Browse files Browse the repository at this point in the history
  • Loading branch information
anacrolix committed Jan 9, 2024
1 parent 7308c68 commit 2c74860
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 10 deletions.
17 changes: 15 additions & 2 deletions callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import (
type Callbacks struct {
// Called after a peer connection completes the BitTorrent handshake. The Client lock is not
// held.
CompletedHandshake func(*PeerConn, InfoHash)
ReadMessage func(*PeerConn, *pp.Message)
CompletedHandshake func(*PeerConn, InfoHash)
ReadMessage func(*PeerConn, *pp.Message)
// This can be folded into the general case below.
ReadExtendedHandshake func(*PeerConn, *pp.ExtendedHandshakeMessage)
PeerConnClosed func(*PeerConn)
// BEP 10 message. Not sure if I should call this Ltep universally. Each handler here is called
// in order.
PeerConnReadExtensionMessage []func(PeerConnReadExtensionMessageEvent)

// Provides secret keys to be tried against incoming encrypted connections.
ReceiveEncryptedHandshakeSkeys mse.SecretKeyIter
Expand All @@ -38,3 +42,12 @@ type PeerRequestEvent struct {
Peer *Peer
Request
}

type PeerConnReadExtensionMessageEvent struct {
PeerConn *PeerConn
// Whether the client has builtin support for this extension.
BuiltinHandler bool
// You can look up what protocol this corresponds to using the PeerConn.LocalLtepProtocolMap.
ExtensionNumber pp.ExtensionNumber
Payload []byte
}
2 changes: 1 addition & 1 deletion peer_protocol/extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type (
}

ExtensionName string
ExtensionNumber int
ExtensionNumber uint8
)

const (
Expand Down
36 changes: 29 additions & 7 deletions peerconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type PeerConn struct {
PeerID PeerID
PeerExtensionBytes pp.PeerExtensionBits
PeerListenPort int
// 1-based mapping from extension number to extension name.
LocalLtepProtocolMap []pp.ExtensionName

// The actual Conn, used for closing, and setting socket options. Do not use methods on this
// while holding any mutexes.
Expand All @@ -55,6 +57,7 @@ type PeerConn struct {

messageWriter peerConnMsgWriter

// The peer's extension map, as sent in their extended handshake.
PeerExtensionIDs map[pp.ExtensionName]pp.ExtensionNumber
PeerClientName atomic.Value
uploadTimer *time.Timer
Expand Down Expand Up @@ -854,8 +857,20 @@ func (c *PeerConn) onReadExtendedMsg(id pp.ExtensionNumber, payload []byte) (err
}()
t := c.t
cl := t.cl
switch id {
case pp.HandshakeExtendedID:
{
event := PeerConnReadExtensionMessageEvent{
PeerConn: c,
// Add one for the handshake ID. This isn't quite right yet, we don't actually have a
// way to differentiate builtin handlers.
BuiltinHandler: int(id) < len(c.LocalLtepProtocolMap)+1,
ExtensionNumber: id,
Payload: payload,
}
for _, cb := range c.callbacks.PeerConnReadExtensionMessage {
cb(event)
}
}
if id == pp.HandshakeExtendedID {
var d pp.ExtendedHandshakeMessage
if err := bencode.Unmarshal(payload, &d); err != nil {
c.logger.Printf("error parsing extended handshake message %q: %s", payload, err)
Expand All @@ -864,7 +879,6 @@ func (c *PeerConn) onReadExtendedMsg(id pp.ExtensionNumber, payload []byte) (err
if cb := c.callbacks.ReadExtendedHandshake; cb != nil {
cb(c, &d)
}
// c.logger.WithDefaultLevel(log.Debug).Printf("received extended handshake message:\n%s", spew.Sdump(d))
if d.Reqq != 0 {
c.PeerMaxRequests = d.Reqq
}
Expand Down Expand Up @@ -896,13 +910,19 @@ func (c *PeerConn) onReadExtendedMsg(id pp.ExtensionNumber, payload []byte) (err
c.pex.Init(c)
}
return nil
case metadataExtendedId:
}
// Zero was taken care of above.
if int(id) >= len(c.LocalLtepProtocolMap)+1 {
return fmt.Errorf("unexpected extended message ID: %v", id)
}
switch c.LocalLtepProtocolMap[id-1] {
case pp.ExtensionNameMetadata:
err := cl.gotMetadataExtensionMsg(payload, t, c)
if err != nil {
return fmt.Errorf("handling metadata extension message: %w", err)
}
return nil
case pexExtendedId:
case pp.ExtensionNamePex:
if !c.pex.IsEnabled() {
return nil // or hang-up maybe?
}
Expand All @@ -911,7 +931,7 @@ func (c *PeerConn) onReadExtendedMsg(id pp.ExtensionNumber, payload []byte) (err
err = fmt.Errorf("receiving pex message: %w", err)
}
return
case utHolepunchExtendedId:
case utHolepunch.ExtensionName:
var msg utHolepunch.Msg
err = msg.UnmarshalBinary(payload)
if err != nil {
Expand All @@ -921,7 +941,9 @@ func (c *PeerConn) onReadExtendedMsg(id pp.ExtensionNumber, payload []byte) (err
err = c.t.handleReceivedUtHolepunchMsg(msg, c)
return
default:
return fmt.Errorf("unexpected extended message ID: %v", id)
// This should have been a user configured protocol, and handled by a callback. How can they
// propagate errors?
return nil
}
}

Expand Down

0 comments on commit 2c74860

Please sign in to comment.